diff options
Diffstat (limited to 'lldb/source/Plugins/Process/Utility')
36 files changed, 13623 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.cpp b/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.cpp new file mode 100644 index 00000000000..bf6b6c2eac4 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.cpp @@ -0,0 +1,327 @@ +//===-- LibUnwindRegisterContext.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LibUnwindRegisterContext.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Target/Thread.h" +// Project includes + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// LibUnwindRegisterContext constructor +//---------------------------------------------------------------------- +LibUnwindRegisterContext::LibUnwindRegisterContext +( + Thread &thread, + StackFrame *frame, + const lldb_private::unw_cursor_t& unwind_cursor +) : + RegisterContext (thread, frame), + m_unwind_cursor (unwind_cursor), + m_unwind_cursor_is_valid (true) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +LibUnwindRegisterContext::~LibUnwindRegisterContext() +{ +} + +void +LibUnwindRegisterContext::Invalidate () +{ + m_unwind_cursor_is_valid = false; +} + +size_t +LibUnwindRegisterContext::GetRegisterCount () +{ + return m_thread.GetRegisterContext()->GetRegisterCount(); +} + +const lldb::RegisterInfo * +LibUnwindRegisterContext::GetRegisterInfoAtIndex (uint32_t reg) +{ + return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex(reg); +} + +size_t +LibUnwindRegisterContext::GetRegisterSetCount () +{ + return m_thread.GetRegisterContext()->GetRegisterSetCount(); +} + + + +const lldb::RegisterSet * +LibUnwindRegisterContext::GetRegisterSet (uint32_t reg_set) +{ + return m_thread.GetRegisterContext()->GetRegisterSet (reg_set); +} + + + +bool +LibUnwindRegisterContext::ReadRegisterValue (uint32_t reg, Scalar &value) +{ + if (m_unwind_cursor_is_valid == false) + return false; + + // Read the register + unw_word_t reg_value; + if (unw_get_reg (&m_unwind_cursor, reg, ®_value) != UNW_ESUCCESS) + return false; + + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg); + switch (reg_info->encoding) + { + case eEncodingUint: + switch (reg_info->byte_size) + { + case 1: + case 2: + case 4: + value = (uint32_t)reg_value; + return true; + + case 8: + value = (uint64_t)reg_value; + return true; + } + break; + + case eEncodingSint: + switch (reg_info->byte_size) + { + case 1: + case 2: + case 4: + value = (int32_t)reg_value; + return true; + + case 8: + value = (int64_t)reg_value; + return true; + } + break; + + case eEncodingIEEE754: + if (reg_info->byte_size > sizeof(unw_word_t)) + return false; + + switch (reg_info->byte_size) + { + case sizeof (float): + if (sizeof (float) == sizeof(uint32_t)) + { + value = (uint32_t)reg_value; + return true; + } + else if (sizeof (float) == sizeof(uint64_t)) + { + value = (uint64_t)reg_value; + return true; + } + break; + + case sizeof (double): + if (sizeof (double) == sizeof(uint32_t)) + { + value = (uint32_t)reg_value; + return true; + } + else if (sizeof (double) == sizeof(uint64_t)) + { + value = (uint64_t)reg_value; + return true; + } + break; + + case sizeof (long double): + if (sizeof (long double) == sizeof(uint32_t)) + { + value = (uint32_t)reg_value; + return true; + } + else if (sizeof (long double) == sizeof(uint64_t)) + { + value = (uint64_t)reg_value; + return true; + } + break; + } + break; + } + return false; +} + + +bool +LibUnwindRegisterContext::ReadRegisterBytes (uint32_t reg, DataExtractor &data) +{ + Scalar reg_value; + + if (ReadRegisterValue (reg, reg_value)) + { + if (reg_value.GetData(data)) + { + // "reg_value" is local and now "data" points to the data within + // "reg_value", so we must make a copy that will live within "data" + DataBufferSP data_sp (new DataBufferHeap (data.GetDataStart(), data.GetByteSize())); + data.SetData (data_sp, 0, data.GetByteSize()); + return true; + } + } + return false; +} + + +bool +LibUnwindRegisterContext::WriteRegisterValue (uint32_t reg, const Scalar &value) +{ + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info == NULL) + return false; + unw_word_t reg_value; + switch (value.GetType()) + { + case Scalar::e_sint: reg_value = value.SInt(); break; + case Scalar::e_uint: reg_value = value.UInt(); break; + case Scalar::e_slong: reg_value = value.SLong(); break; + case Scalar::e_ulong: reg_value = value.ULong(); break; + case Scalar::e_slonglong: reg_value = value.SLongLong(); break; + case Scalar::e_ulonglong: reg_value = value.ULongLong(); break; + case Scalar::e_float: + if (sizeof (float) == sizeof (unsigned int)) + reg_value = value.UInt(); + else if (sizeof (float) == sizeof (unsigned long)) + reg_value = value.ULong(); + else if (sizeof (float) == sizeof (unsigned long long)) + reg_value = value.ULongLong(); + else + return false; + break; + + case Scalar::e_double: + if (sizeof (double) == sizeof (unsigned int)) + reg_value = value.UInt(); + else if (sizeof (double) == sizeof (unsigned long)) + reg_value = value.ULong(); + else if (sizeof (double) == sizeof (unsigned long long)) + reg_value = value.ULongLong(); + else + return false; + break; + + case Scalar::e_long_double: + if (sizeof (long double) == sizeof (unsigned int)) + reg_value = value.UInt(); + else if (sizeof (long double) == sizeof (unsigned long)) + reg_value = value.ULong(); + else if (sizeof (long double) == sizeof (unsigned long long)) + reg_value = value.ULongLong(); + else + return false; + break; + } + + return unw_set_reg (&m_unwind_cursor, reg, reg_value) == UNW_ESUCCESS; +} + + +bool +LibUnwindRegisterContext::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset) +{ + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg); + + if (reg_info == NULL) + return false; + if (reg_info->byte_size > sizeof (unw_word_t)) + return false; + + Scalar value; + uint32_t offset = data_offset; + + switch (reg_info->encoding) + { + case eEncodingUint: + if (reg_info->byte_size <= 4) + value = data.GetMaxU32 (&offset, reg_info->byte_size); + else if (reg_info->byte_size <= 8) + value = data.GetMaxU64 (&offset, reg_info->byte_size); + else + return false; + break; + + case eEncodingSint: + if (reg_info->byte_size <= 4) + value = (int32_t)data.GetMaxU32 (&offset, reg_info->byte_size); + else if (reg_info->byte_size <= 8) + value = data.GetMaxS64 (&offset, reg_info->byte_size); + else + return false; + break; + + case eEncodingIEEE754: + switch (reg_info->byte_size) + { + case sizeof (float): + value = data.GetFloat (&offset); + break; + + case sizeof (double): + value = data.GetDouble (&offset); + break; + + case sizeof (long double): + value = data.GetLongDouble (&offset); + break; + default: + return false; + } + } + return WriteRegisterValue (reg, value); +} + + +bool +LibUnwindRegisterContext::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + // libunwind frames can't handle this it doesn't always have all register + // values. This call should only be called on frame zero anyway so there + // shouldn't be any problem + return false; +} + +bool +LibUnwindRegisterContext::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + // Since this class doesn't respond to "ReadAllRegisterValues()", it must + // not have been the one that saved all the register values. So we just let + // the thread's register context (the register context for frame zero) do + // the writing. + return m_thread.GetRegisterContext()->WriteAllRegisterValues(data_sp); +} + + +uint32_t +LibUnwindRegisterContext::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) +{ + return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (kind, num); +} + diff --git a/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.h b/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.h new file mode 100644 index 00000000000..4e89b27961f --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.h @@ -0,0 +1,83 @@ +//===-- LibUnwindRegisterContext.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_LibUnwindRegisterContext_h_ +#define lldb_LibUnwindRegisterContext_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" + +#include "libunwind.h" + +class LibUnwindRegisterContext : public lldb_private::RegisterContext +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + LibUnwindRegisterContext (lldb_private::Thread &thread, + lldb_private::StackFrame *frame, + const lldb_private::unw_cursor_t &unwind_cursor); + + virtual + ~LibUnwindRegisterContext (); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + virtual void + Invalidate (); + + virtual size_t + GetRegisterCount (); + + virtual const lldb::RegisterInfo * + GetRegisterInfoAtIndex (uint32_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const lldb::RegisterSet * + GetRegisterSet (uint32_t reg_set); + + virtual bool + ReadRegisterValue (uint32_t reg, lldb_private::Scalar &value); + + virtual bool + ReadRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data); + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteRegisterValue (uint32_t reg, const lldb_private::Scalar &value); + + virtual bool + WriteRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data, uint32_t data_offset); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + +private: + lldb_private::unw_cursor_t m_unwind_cursor; + bool m_unwind_cursor_is_valid; + //------------------------------------------------------------------ + // For LibUnwindRegisterContext only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (LibUnwindRegisterContext); +}; + +#endif // lldb_LibUnwindRegisterContext_h_ diff --git a/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.cpp b/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.cpp new file mode 100644 index 00000000000..58a7ac043c5 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.cpp @@ -0,0 +1,306 @@ +//===-- MacOSXLibunwindCallbacks.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_MacOSXLibunwindCallbacks_cpp_ +#define liblldb_MacOSXLibunwindCallbacks_cpp_ +#if defined(__cplusplus) + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +#include "lldb-enumerations.h" +#include "libunwind.h" +#include "llvm-c/EnhancedDisassembly.h" + +using namespace lldb; + +namespace lldb_private { + +/* Don't implement (libunwind does not use) + find_proc_info + put_unwind_info + get_dyn_info_list_addr + access_mem + resume +*/ +/* + Should implement (not needed yet) + access_fpreg + access_vecreg + proc_is_sigtramp + proc_is_inferior_function_call + access_reg_inf_func_call +*/ + +static int +access_reg (lldb_private::unw_addr_space_t as, lldb_private::unw_regnum_t regnum, lldb_private::unw_word_t *valp, int write, void *arg) +{ + if (arg == 0) + return -1; + Thread *th = (Thread *) arg; + /* FIXME Only support reading for now. */ + if (write == 1) + return -1; + if (th->GetRegisterContext()->GetRegisterInfoAtIndex(regnum) == NULL) + return -1; + DataExtractor de; + if (!th->GetRegisterContext()->ReadRegisterBytes (regnum, de)) + return -1; + memcpy (valp, de.GetDataStart(), de.GetByteSize()); + return UNW_ESUCCESS; +} + +static int +get_proc_name (lldb_private::unw_addr_space_t as, lldb_private::unw_word_t ip, char *bufp, size_t buf_len, lldb_private::unw_word_t *offp, void *arg) +{ + if (arg == 0) + return -1; + Thread *th = (Thread *) arg; + Address addr; + if (!th->GetProcess().ResolveLoadAddress(ip, addr)) + return -1; + + SymbolContext sc; + if (!th->GetProcess().GetTarget().GetImages().ResolveSymbolContextForAddress (addr, eSymbolContextFunction, sc)) + return -1; + if (!sc.symbol) + return -1; + strlcpy (bufp, sc.symbol->GetMangled().GetMangledName().AsCString(""), buf_len); + if (offp) + *offp = addr.GetLoadAddress(&th->GetProcess()) - sc.symbol->GetValue().GetLoadAddress(&th->GetProcess()); + return UNW_ESUCCESS; +} + +static int +find_image_info (lldb_private::unw_addr_space_t as, lldb_private::unw_word_t load_addr, lldb_private::unw_word_t *mh, + lldb_private::unw_word_t *text_start, lldb_private::unw_word_t *text_end, + lldb_private::unw_word_t *eh_frame, lldb_private::unw_word_t *eh_frame_len, + lldb_private::unw_word_t *compact_unwind_start, lldb_private::unw_word_t *compact_unwind_len, void *arg) +{ + if (arg == 0) + return -1; + Thread *th = (Thread *) arg; + Address addr; + if (!th->GetProcess().ResolveLoadAddress(load_addr, addr)) + return -1; + + SymbolContext sc; + if (!th->GetProcess().GetTarget().GetImages().ResolveSymbolContextForAddress (addr, eSymbolContextModule, sc)) + return -1; + + SectionList *sl = sc.module_sp->GetObjectFile()->GetSectionList(); + static ConstString g_segment_name_TEXT("__TEXT"); + SectionSP text_segment_sp(sl->FindSectionByName(g_segment_name_TEXT)); + if (!text_segment_sp) + return -1; + + *mh = text_segment_sp->GetLoadBaseAddress (&th->GetProcess()); + *text_start = text_segment_sp->GetLoadBaseAddress (&th->GetProcess()); + *text_end = *text_start + text_segment_sp->GetByteSize(); + + static ConstString g_section_name_eh_frame ("__eh_frame"); + SectionSP eh_frame_section_sp = text_segment_sp->GetChildren().FindSectionByName(g_section_name_eh_frame); + if (eh_frame_section_sp.get()) { + *eh_frame = eh_frame_section_sp->GetLoadBaseAddress (&th->GetProcess()); + *eh_frame_len = eh_frame_section_sp->GetByteSize(); + } else { + *eh_frame = 0; + *eh_frame_len = 0; + } + + static ConstString g_section_name_unwind_info ("__unwind_info"); + SectionSP unwind_info_section_sp = text_segment_sp->GetChildren().FindSectionByName(g_section_name_unwind_info); + if (unwind_info_section_sp.get()) { + *compact_unwind_start = unwind_info_section_sp->GetLoadBaseAddress (&th->GetProcess()); + *compact_unwind_len = unwind_info_section_sp->GetByteSize(); + } else { + *compact_unwind_start = 0; + *compact_unwind_len = 0; + } + return UNW_ESUCCESS; +} + +static int +get_proc_bounds (lldb_private::unw_addr_space_t as, lldb_private::unw_word_t ip, lldb_private::unw_word_t *low, lldb_private::unw_word_t *high, void *arg) +{ + if (arg == 0) + return -1; + Thread *th = (Thread *) arg; + Address addr; + if (!th->GetProcess().ResolveLoadAddress(ip, addr)) + return -1; + SymbolContext sc; + if (!th->GetProcess().GetTarget().GetImages().ResolveSymbolContextForAddress (addr, eSymbolContextFunction | eSymbolContextSymbol, sc)) + return -1; + if (sc.function) + { + lldb::addr_t start, len; + start = sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress(&th->GetProcess()); + len = sc.function->GetAddressRange().GetByteSize(); + if (start == LLDB_INVALID_ADDRESS || len == LLDB_INVALID_ADDRESS) + return -1; + *low = start; + *high = start + len; + return UNW_ESUCCESS; + } + if (sc.symbol) + { + lldb::addr_t start, len; + start = sc.symbol->GetAddressRangeRef().GetBaseAddress().GetLoadAddress(&th->GetProcess()); + len = sc.symbol->GetAddressRangeRef().GetByteSize(); + if (start == LLDB_INVALID_ADDRESS) + return -1; + *low = start; + if (len != LLDB_INVALID_ADDRESS) + *high = start + len; + else + *high = 0; + return UNW_ESUCCESS; + } + return -1; +} + +static int +access_raw (lldb_private::unw_addr_space_t as, lldb_private::unw_word_t addr, lldb_private::unw_word_t extent, uint8_t *valp, int write, void *arg) +{ + if (arg == 0) + return -1; + Thread *th = (Thread *) arg; + /* FIXME Only support reading for now. */ + if (write == 1) + return -1; + + Error error; + if (th->GetProcess().ReadMemory (addr, valp, extent, error) != extent) + return -1; + return UNW_ESUCCESS; +} + + +static int +reg_info (lldb_private::unw_addr_space_t as, lldb_private::unw_regnum_t regnum, lldb_private::unw_regtype_t *type, char *buf, size_t buflen, void *arg) +{ + if (arg == 0) + return -1; + Thread *th = (Thread *) arg; + RegisterContext *regc = th->GetRegisterContext(); + if (regnum > regc->GetRegisterCount()) + { + *type = UNW_NOT_A_REG; + return UNW_ESUCCESS; + } + + const char *name = regc->GetRegisterName (regnum); + if (name == NULL) + { + *type = UNW_NOT_A_REG; + return UNW_ESUCCESS; + } + strlcpy (buf, name, buflen); + + const lldb::RegisterInfo *reginfo = regc->GetRegisterInfoAtIndex (regnum); + if (reginfo == NULL || reginfo->encoding == eEncodingInvalid) + { + *type = UNW_NOT_A_REG; + return UNW_ESUCCESS; + } + if (reginfo->encoding == eEncodingUint || reginfo->encoding == eEncodingSint) + *type = UNW_INTEGER_REG; + if (reginfo->encoding == eEncodingIEEE754) + *type = UNW_FLOATING_POINT_REG; + if (reginfo->encoding == eEncodingVector) + *type = UNW_VECTOR_REG; + + return UNW_ESUCCESS; +} + + +static int +read_byte_for_edis (uint8_t *buf, uint64_t addr, void *arg) +{ + if (arg == 0) + return -1; + Thread *th = (Thread *) arg; + DataBufferHeap onebyte(1, 0); + Error error; + if (th->GetProcess().ReadMemory (addr, onebyte.GetBytes(), onebyte.GetByteSize(), error) != 1) + return -1; + *buf = onebyte.GetBytes()[0]; + return UNW_ESUCCESS; +} + +static int +instruction_length (lldb_private::unw_addr_space_t as, lldb_private::unw_word_t addr, int *length, void *arg) +{ + EDDisassemblerRef disasm; + EDInstRef cur_insn; + + if (arg == 0) + return -1; + Thread *th = (Thread *) arg; + const ArchSpec target_arch (th->GetProcess().GetTarget().GetArchitecture ()); + + if (target_arch.GetCPUType() == CPU_TYPE_I386) + { + if (EDGetDisassembler (&disasm, "i386-apple-darwin", kEDAssemblySyntaxX86ATT) != 0) + return -1; + } + else if (target_arch.GetCPUType() == CPU_TYPE_X86_64) + { + if (EDGetDisassembler (&disasm, "x86_64-apple-darwin", kEDAssemblySyntaxX86ATT) != 0) + return -1; + } + else + { + return -1; + } + + if (EDCreateInsts (&cur_insn, 1, disasm, read_byte_for_edis, addr, arg) != 1) + return -1; + *length = EDInstByteSize (cur_insn); + EDReleaseInst (cur_insn); + return UNW_ESUCCESS; +} + +lldb_private::unw_accessors_t +get_macosx_libunwind_callbacks () { + lldb_private::unw_accessors_t ap; + bzero (&ap, sizeof (lldb_private::unw_accessors_t)); + ap.find_proc_info = NULL; + ap.put_unwind_info = NULL; + ap.get_dyn_info_list_addr = NULL; + ap.find_image_info = find_image_info; + ap.access_mem = NULL; + ap.access_reg = access_reg; + ap.access_fpreg = NULL; + ap.access_vecreg = NULL; + ap.resume = NULL; + ap.get_proc_name = get_proc_name; + ap.get_proc_bounds = get_proc_bounds; + ap.access_raw = access_raw; + ap.reg_info = reg_info; + ap.proc_is_sigtramp = NULL; + ap.proc_is_inferior_function_call = NULL; + ap.access_reg_inf_func_call = NULL; + ap.instruction_length = instruction_length; + return ap; +} + + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_MacOSXLibunwindCallbacks_cpp_ diff --git a/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.h b/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.h new file mode 100644 index 00000000000..78bd27b2ad3 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.h @@ -0,0 +1,22 @@ +//===-- MacOSXLibunwindCallbacks.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_MacOSXLibunwindCallbacks_h_ +#define liblldb_MacOSXLibunwindCallbacks_h_ +#if defined(__cplusplus) + +namespace lldb_private { + +unw_accessors_t get_macosx_libunwind_callbacks (); + +} // namespace lldb_utility + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_MacOSXLibunwindCallbacks_h_ + diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp b/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp new file mode 100644 index 00000000000..df2f7c07f65 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp @@ -0,0 +1,255 @@ +//===-- RegisterContextMacOSXFrameBackchain.cpp -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextMacOSXFrameBackchain.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/Thread.h" +// Project includes +#include "StringExtractorGDBRemote.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// RegisterContextMacOSXFrameBackchain constructor +//---------------------------------------------------------------------- +RegisterContextMacOSXFrameBackchain::RegisterContextMacOSXFrameBackchain +( + Thread &thread, + StackFrame *frame, + const UnwindMacOSXFrameBackchain::Cursor &cursor +) : + RegisterContext (thread, frame), + m_cursor (cursor), + m_cursor_is_valid (true) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +RegisterContextMacOSXFrameBackchain::~RegisterContextMacOSXFrameBackchain() +{ +} + +void +RegisterContextMacOSXFrameBackchain::Invalidate () +{ + m_cursor_is_valid = false; +} + +size_t +RegisterContextMacOSXFrameBackchain::GetRegisterCount () +{ + return m_thread.GetRegisterContext()->GetRegisterCount(); +} + +const lldb::RegisterInfo * +RegisterContextMacOSXFrameBackchain::GetRegisterInfoAtIndex (uint32_t reg) +{ + return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex(reg); +} + +size_t +RegisterContextMacOSXFrameBackchain::GetRegisterSetCount () +{ + return m_thread.GetRegisterContext()->GetRegisterSetCount(); +} + + + +const lldb::RegisterSet * +RegisterContextMacOSXFrameBackchain::GetRegisterSet (uint32_t reg_set) +{ + return m_thread.GetRegisterContext()->GetRegisterSet (reg_set); +} + + + +bool +RegisterContextMacOSXFrameBackchain::ReadRegisterValue (uint32_t reg, Scalar &value) +{ + if (!m_cursor_is_valid) + return false; + + uint64_t reg_value = LLDB_INVALID_ADDRESS; + + const RegisterInfo *reg_info = m_thread.GetRegisterContext()->GetRegisterInfoAtIndex (reg); + if (reg_info == NULL) + return false; + + switch (reg_info->kinds[eRegisterKindGeneric]) + { + case LLDB_REGNUM_GENERIC_PC: + if (m_cursor.pc == LLDB_INVALID_ADDRESS) + return false; + reg_value = m_cursor.pc; + break; + + case LLDB_REGNUM_GENERIC_FP: + if (m_cursor.fp == LLDB_INVALID_ADDRESS) + return false; + reg_value = m_cursor.pc; + break; + + default: + return false; + } + + switch (reg_info->encoding) + { + case eEncodingUint: + switch (reg_info->byte_size) + { + case 1: + case 2: + case 4: + value = (uint32_t)reg_value; + return true; + + case 8: + value = (uint64_t)reg_value; + return true; + } + break; + + case eEncodingSint: + switch (reg_info->byte_size) + { + case 1: + case 2: + case 4: + value = (int32_t)reg_value; + return true; + + case 8: + value = (int64_t)reg_value; + return true; + } + break; + + case eEncodingIEEE754: + switch (reg_info->byte_size) + { + case sizeof (float): + if (sizeof (float) == sizeof(uint32_t)) + { + value = (uint32_t)reg_value; + return true; + } + else if (sizeof (float) == sizeof(uint64_t)) + { + value = (uint64_t)reg_value; + return true; + } + break; + + case sizeof (double): + if (sizeof (double) == sizeof(uint32_t)) + { + value = (uint32_t)reg_value; + return true; + } + else if (sizeof (double) == sizeof(uint64_t)) + { + value = (uint64_t)reg_value; + return true; + } + break; + + case sizeof (long double): + if (sizeof (long double) == sizeof(uint32_t)) + { + value = (uint32_t)reg_value; + return true; + } + else if (sizeof (long double) == sizeof(uint64_t)) + { + value = (uint64_t)reg_value; + return true; + } + break; + } + break; + } + return false; +} + + +bool +RegisterContextMacOSXFrameBackchain::ReadRegisterBytes (uint32_t reg, DataExtractor &data) +{ + Scalar reg_value; + + if (ReadRegisterValue (reg, reg_value)) + { + if (reg_value.GetData(data)) + { + // "reg_value" is local and now "data" points to the data within + // "reg_value", so we must make a copy that will live within "data" + DataBufferSP data_sp (new DataBufferHeap (data.GetDataStart(), data.GetByteSize())); + data.SetData (data_sp, 0, data.GetByteSize()); + return true; + } + } + return false; +} + + +bool +RegisterContextMacOSXFrameBackchain::WriteRegisterValue (uint32_t reg, const Scalar &value) +{ + // Not supported yet. We could easily add support for this by remembering + // the address of each entry (it would need to be part of the cursor) + return false; +} + + +bool +RegisterContextMacOSXFrameBackchain::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset) +{ + // Not supported yet. We could easily add support for this by remembering + // the address of each entry (it would need to be part of the cursor) + return false; +} + + +bool +RegisterContextMacOSXFrameBackchain::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + // libunwind frames can't handle this it doesn't always have all register + // values. This call should only be called on frame zero anyway so there + // shouldn't be any problem + return false; +} + +bool +RegisterContextMacOSXFrameBackchain::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + // Since this class doesn't respond to "ReadAllRegisterValues()", it must + // not have been the one that saved all the register values. So we just let + // the thread's register context (the register context for frame zero) do + // the writing. + return m_thread.GetRegisterContext()->WriteAllRegisterValues(data_sp); +} + + +uint32_t +RegisterContextMacOSXFrameBackchain::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) +{ + return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (kind, num); +} + diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h b/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h new file mode 100644 index 00000000000..f4118c2795d --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h @@ -0,0 +1,83 @@ +//===-- RegisterContextMacOSXFrameBackchain.h -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_RegisterContextMacOSXFrameBackchain_h_ +#define lldb_RegisterContextMacOSXFrameBackchain_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" + +#include "UnwindMacOSXFrameBackchain.h" + +class RegisterContextMacOSXFrameBackchain : public lldb_private::RegisterContext +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextMacOSXFrameBackchain (lldb_private::Thread &thread, + lldb_private::StackFrame *frame, + const UnwindMacOSXFrameBackchain::Cursor &cursor); + + virtual + ~RegisterContextMacOSXFrameBackchain (); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + virtual void + Invalidate (); + + virtual size_t + GetRegisterCount (); + + virtual const lldb::RegisterInfo * + GetRegisterInfoAtIndex (uint32_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const lldb::RegisterSet * + GetRegisterSet (uint32_t reg_set); + + virtual bool + ReadRegisterValue (uint32_t reg, lldb_private::Scalar &value); + + virtual bool + ReadRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data); + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteRegisterValue (uint32_t reg, const lldb_private::Scalar &value); + + virtual bool + WriteRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data, uint32_t data_offset); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + +private: + UnwindMacOSXFrameBackchain::Cursor m_cursor; + bool m_cursor_is_valid; + //------------------------------------------------------------------ + // For RegisterContextMacOSXFrameBackchain only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (RegisterContextMacOSXFrameBackchain); +}; + +#endif // lldb_RegisterContextMacOSXFrameBackchain_h_ diff --git a/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.cpp b/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.cpp new file mode 100644 index 00000000000..1b6fa58dcd4 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.cpp @@ -0,0 +1,73 @@ +//===-- UnwindLibUnwind.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Thread.h" +#include "UnwindLibUnwind.h" +#include "LibUnwindRegisterContext.h" + +using namespace lldb; +using namespace lldb_private; + +UnwindLibUnwind::UnwindLibUnwind (Thread &thread, unw_addr_space_t addr_space) : + Unwind (thread), + m_addr_space (addr_space), + m_cursors() +{ + m_pc_regnum = thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + m_sp_regnum = thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); +} + +uint32_t +UnwindLibUnwind::GetFrameCount() +{ + if (m_cursors.empty()) + { + unw_cursor_t cursor; + unw_init_remote (&cursor, m_addr_space, &m_thread); + + m_cursors.push_back (cursor); + + while (1) + { + int stepresult = unw_step (&cursor); + if (stepresult > 0) + m_cursors.push_back (cursor); + else + break; + } + } + return m_cursors.size(); +} + +bool +UnwindLibUnwind::GetFrameInfoAtIndex (uint32_t idx, addr_t& cfa, addr_t& pc) +{ + const uint32_t frame_count = GetFrameCount(); + if (idx < frame_count) + { + int pc_err = unw_get_reg (&m_cursors[idx], m_pc_regnum, &pc); + int sp_err = unw_get_reg (&m_cursors[idx], m_sp_regnum, &cfa); + return pc_err == UNW_ESUCCESS && sp_err == UNW_ESUCCESS; + } + return false; +} + +RegisterContext * +UnwindLibUnwind::CreateRegisterContextForFrame (StackFrame *frame) +{ + uint32_t idx = frame->GetID(); + const uint32_t frame_count = GetFrameCount(); + if (idx < frame_count) + return new LibUnwindRegisterContext (m_thread, frame, m_cursors[idx]); + return NULL; +} diff --git a/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.h b/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.h new file mode 100644 index 00000000000..d91f164a2f9 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.h @@ -0,0 +1,66 @@ +//===-- UnwindLibUnwind.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_UnwindLibUnwind_h_ +#define lldb_UnwindLibUnwind_h_ + +// C Includes +// C++ Includes +#include <vector> + +// Other libraries and framework includes +#include "libunwind.h" + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/Unwind.h" + +class UnwindLibUnwind : public lldb_private::Unwind +{ +public: + UnwindLibUnwind (lldb_private::Thread &thread, + lldb_private::unw_addr_space_t addr_space); + + virtual + ~UnwindLibUnwind() + { + } + + virtual void + Clear() + { + m_cursors.clear(); + } + + virtual uint32_t + GetFrameCount(); + + bool + GetFrameInfoAtIndex (uint32_t frame_idx, + lldb::addr_t& cfa, + lldb::addr_t& pc); + + lldb_private::RegisterContext * + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + lldb_private::Thread & + GetThread(); + +private: + lldb_private::unw_addr_space_t m_addr_space; + std::vector<lldb_private::unw_cursor_t> m_cursors; + uint32_t m_pc_regnum; + uint32_t m_sp_regnum; + //------------------------------------------------------------------ + // For UnwindLibUnwind only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (UnwindLibUnwind); +}; + +#endif // lldb_UnwindLibUnwind_h_ diff --git a/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp b/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp new file mode 100644 index 00000000000..586e3d78864 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp @@ -0,0 +1,243 @@ +//===-- UnwindMacOSXFrameBackchain.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "RegisterContextMacOSXFrameBackchain.h" + +using namespace lldb; +using namespace lldb_private; + +UnwindMacOSXFrameBackchain::UnwindMacOSXFrameBackchain (Thread &thread) : + Unwind (thread), + m_cursors() +{ +} + +uint32_t +UnwindMacOSXFrameBackchain::GetFrameCount() +{ + if (m_cursors.empty()) + { + const ArchSpec target_arch (m_thread.GetProcess().GetTarget().GetArchitecture ()); + // Frame zero should always be supplied by the thread... + StackFrameSP frame_sp (m_thread.GetStackFrameAtIndex (0)); + if (target_arch == ArchSpec("x86_64")) + GetStackFrameData_x86_64 (frame_sp.get()); + else if (target_arch == ArchSpec("i386")) + GetStackFrameData_i386 (frame_sp.get()); + + } + return m_cursors.size(); +} + +bool +UnwindMacOSXFrameBackchain::GetFrameInfoAtIndex (uint32_t idx, addr_t& cfa, addr_t& pc) +{ + const uint32_t frame_count = GetFrameCount(); + if (idx < frame_count) + { + if (m_cursors[idx].pc == LLDB_INVALID_ADDRESS) + return false; + if (m_cursors[idx].fp == LLDB_INVALID_ADDRESS) + return false; + + pc = m_cursors[idx].pc; + cfa = m_cursors[idx].fp; + + return true; + } + return false; +} + +RegisterContext * +UnwindMacOSXFrameBackchain::CreateRegisterContextForFrame (StackFrame *frame) +{ + uint32_t idx = frame->GetID(); + const uint32_t frame_count = GetFrameCount(); + if (idx < frame_count) + return new RegisterContextMacOSXFrameBackchain (m_thread, frame, m_cursors[idx]); + return NULL; +} + +size_t +UnwindMacOSXFrameBackchain::GetStackFrameData_i386 (StackFrame *first_frame) +{ + m_cursors.clear(); + + std::pair<lldb::addr_t, lldb::addr_t> fp_pc_pair; + + typedef struct Frame_i386 + { + uint32_t fp; + uint32_t pc; + }; + + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + assert (reg_ctx); + + Cursor cursor; + cursor.pc = reg_ctx->GetPC (LLDB_INVALID_ADDRESS); + cursor.fp = reg_ctx->GetFP (0); + + Frame_i386 frame = { cursor.fp, cursor.pc }; + + m_cursors.push_back(cursor); + + const size_t k_frame_size = sizeof(frame); + Error error; + while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) + { + // Read both the FP and PC (8 bytes) + if (m_thread.GetProcess().ReadMemory (frame.fp, &frame.fp, k_frame_size, error) != k_frame_size) + break; + if (frame.pc >= 0x1000) + { + cursor.pc = frame.pc; + cursor.fp = frame.fp; + m_cursors.push_back (cursor); + } + } + if (!m_cursors.empty()) + { + lldb::addr_t first_frame_pc = m_cursors.front().pc; + if (first_frame_pc != LLDB_INVALID_ADDRESS) + { + const uint32_t resolve_scope = eSymbolContextModule | + eSymbolContextCompUnit | + eSymbolContextFunction | + eSymbolContextSymbol; + + SymbolContext first_frame_sc (first_frame->GetSymbolContext(resolve_scope)); + const AddressRange *addr_range_ptr = NULL; + if (first_frame_sc.function) + addr_range_ptr = &first_frame_sc.function->GetAddressRange(); + else if (first_frame_sc.symbol) + addr_range_ptr = first_frame_sc.symbol->GetAddressRangePtr(); + + if (addr_range_ptr) + { + if (first_frame->GetPC() == addr_range_ptr->GetBaseAddress()) + { + // We are at the first instruction, so we can recover the + // previous PC by dereferencing the SP + lldb::addr_t first_frame_sp = reg_ctx->GetSP (0); + // Read the real second frame return address into frame.pc + if (first_frame_sp && m_thread.GetProcess().ReadMemory (first_frame_sp, &frame.pc, sizeof(frame.pc), error) == sizeof(frame.pc)) + { + cursor.fp = m_cursors.front().fp; + cursor.pc = frame.pc; // Set the new second frame PC + + // Insert the second frame + m_cursors.insert(m_cursors.begin()+1, cursor); + + m_cursors.front().fp = first_frame_sp; + } + } + } + } + } +// uint32_t i=0; +// printf(" PC FP\n"); +// printf(" ------------------ ------------------ \n"); +// for (i=0; i<m_cursors.size(); ++i) +// { +// printf("[%3u] 0x%16.16llx 0x%16.16llx\n", i, m_cursors[i].pc, m_cursors[i].fp); +// } + return m_cursors.size(); +} + + +size_t +UnwindMacOSXFrameBackchain::GetStackFrameData_x86_64 (StackFrame *first_frame) +{ + m_cursors.clear(); + + std::pair<lldb::addr_t, lldb::addr_t> fp_pc_pair; + + typedef struct Frame_x86_64 + { + uint64_t fp; + uint64_t pc; + }; + + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + assert (reg_ctx); + + Cursor cursor; + cursor.pc = reg_ctx->GetPC (LLDB_INVALID_ADDRESS); + cursor.fp = reg_ctx->GetFP (0); + + Frame_x86_64 frame = { cursor.fp, cursor.pc }; + + m_cursors.push_back(cursor); + Error error; + const size_t k_frame_size = sizeof(frame); + while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) + { + // Read both the FP and PC (16 bytes) + if (m_thread.GetProcess().ReadMemory (frame.fp, &frame.fp, k_frame_size, error) != k_frame_size) + break; + + if (frame.pc >= 0x1000) + { + cursor.pc = frame.pc; + cursor.fp = frame.fp; + m_cursors.push_back (cursor); + } + } + if (!m_cursors.empty()) + { + lldb::addr_t first_frame_pc = m_cursors.front().pc; + if (first_frame_pc != LLDB_INVALID_ADDRESS) + { + const uint32_t resolve_scope = eSymbolContextModule | + eSymbolContextCompUnit | + eSymbolContextFunction | + eSymbolContextSymbol; + + SymbolContext first_frame_sc(first_frame->GetSymbolContext(resolve_scope)); + const AddressRange *addr_range_ptr = NULL; + if (first_frame_sc.function) + addr_range_ptr = &first_frame_sc.function->GetAddressRange(); + else if (first_frame_sc.symbol) + addr_range_ptr = first_frame_sc.symbol->GetAddressRangePtr(); + + if (addr_range_ptr) + { + if (first_frame->GetPC() == addr_range_ptr->GetBaseAddress()) + { + // We are at the first instruction, so we can recover the + // previous PC by dereferencing the SP + lldb::addr_t first_frame_sp = reg_ctx->GetSP (0); + // Read the real second frame return address into frame.pc + if (m_thread.GetProcess().ReadMemory (first_frame_sp, &frame.pc, sizeof(frame.pc), error) == sizeof(frame.pc)) + { + cursor.fp = m_cursors.front().fp; + cursor.pc = frame.pc; // Set the new second frame PC + + // Insert the second frame + m_cursors.insert(m_cursors.begin()+1, cursor); + + m_cursors.front().fp = first_frame_sp; + } + } + } + } + } + return m_cursors.size(); +} + diff --git a/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h b/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h new file mode 100644 index 00000000000..86ba6e7ae7f --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h @@ -0,0 +1,77 @@ +//===-- UnwindMacOSXFrameBackchain.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_UnwindMacOSXFrameBackchain_h_ +#define lldb_UnwindMacOSXFrameBackchain_h_ + +// C Includes +// C++ Includes +#include <vector> + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/Unwind.h" + +class UnwindMacOSXFrameBackchain : public lldb_private::Unwind +{ +public: + UnwindMacOSXFrameBackchain (lldb_private::Thread &thread); + + virtual + ~UnwindMacOSXFrameBackchain() + { + } + + virtual void + Clear() + { + m_cursors.clear(); + } + + virtual uint32_t + GetFrameCount(); + + bool + GetFrameInfoAtIndex (uint32_t frame_idx, + lldb::addr_t& cfa, + lldb::addr_t& pc); + + lldb_private::RegisterContext * + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + lldb_private::Thread & + GetThread(); + +protected: + friend class RegisterContextMacOSXFrameBackchain; + + typedef struct Cursor + { + lldb::addr_t pc; // Program counter + lldb::addr_t fp; // Frame pointer for us with backchain + }; + +private: + std::vector<Cursor> m_cursors; + + size_t + GetStackFrameData_i386 (lldb_private::StackFrame *first_frame); + + size_t + GetStackFrameData_x86_64 (lldb_private::StackFrame *first_frame); + + //------------------------------------------------------------------ + // For UnwindMacOSXFrameBackchain only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (UnwindMacOSXFrameBackchain); +}; + +#endif // lldb_UnwindMacOSXFrameBackchain_h_ diff --git a/lldb/source/Plugins/Process/Utility/libunwind/include/libunwind.h b/lldb/source/Plugins/Process/Utility/libunwind/include/libunwind.h new file mode 100644 index 00000000000..63cc8ba2366 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/include/libunwind.h @@ -0,0 +1,509 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- libunwind.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// C interface to libuwind +// +// Source compatible with Level 1 Base ABI documented at: +// http://www.codesourcery.com/public/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + + +#ifndef __LIBUNWIND__ +#define __LIBUNWIND__ + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <mach/mach_types.h> +#include <Availability.h> + +namespace lldb_private { + +#pragma mark Error codes + +enum { + UNW_ESUCCESS = 0, /* no error */ + UNW_EUNSPEC = -6540, /* unspecified (general) error */ + UNW_ENOMEM = -6541, /* out of memory */ + UNW_EBADREG = -6542, /* bad register number */ + UNW_EREADONLYREG = -6543, /* attempt to write read-only register */ + UNW_ESTOPUNWIND = -6544, /* stop unwinding */ + UNW_EINVALIDIP = -6545, /* invalid IP */ + UNW_EBADFRAME = -6546, /* bad frame */ + UNW_EINVAL = -6547, /* unsupported operation or bad value */ + UNW_EBADVERSION = -6548, /* unwind info has unsupported version */ + UNW_ENOINFO = -6549, /* no unwind info found */ + UNW_EREGUNAVAILABLE = -6550 /* contents of requested reg are not available */ +}; + +#pragma mark General data structures + +struct unw_context_t { uint64_t data[128]; }; +typedef struct unw_context_t unw_context_t; + +struct unw_cursor_t { uint64_t data[140]; }; +typedef struct unw_cursor_t unw_cursor_t; + +enum unw_as_type { UNW_LOCAL, UNW_REMOTE }; +struct unw_addr_space +{ + enum unw_as_type type; + uint8_t data[]; +}; +typedef struct unw_addr_space* unw_addr_space_t; + +typedef int unw_regnum_t; +typedef uint64_t unw_word_t; +typedef double unw_fpreg_t; + +enum unw_vecreg_format { + UNW_VECREG_SIGNED, + UNW_VECREG_UNSIGNED, + UNW_VECREG_FLOAT +}; + +typedef struct +{ + union { + double doubles[8]; + float floats [16]; + + uint64_t dwords [8]; + uint32_t words [16]; + uint16_t hwords [32]; + uint8_t bytes [64]; + } data; + uint16_t unit_size; // bits + uint16_t num_units; + uint8_t format; +} unw_vecreg_t; + +struct unw_proc_info_t +{ + unw_word_t start_ip; /* start address of function */ + unw_word_t end_ip; /* address after end of function */ + unw_word_t lsda; /* address of language specific data area, or zero if not used */ + unw_word_t handler; /* personality routine, or zero if not used */ + unw_word_t gp; /* not used */ + unw_word_t flags; /* not used */ + uint32_t format; /* compact unwind encoding, or zero if none */ + uint32_t unwind_info_size; /* size of dwarf unwind info, or zero if none */ + unw_word_t unwind_info; /* address of dwarf unwind info, or zero if none */ + unw_word_t extra; /* mach_header of mach-o image containing function */ +}; +typedef struct unw_proc_info_t unw_proc_info_t; + +#pragma mark Local API + +#ifdef __cplusplus +extern "C" { +#endif + +extern int unw_getcontext(unw_context_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_init_local(unw_cursor_t*, unw_context_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_step(unw_cursor_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_get_reg(unw_cursor_t*, unw_regnum_t, unw_word_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_get_fpreg(unw_cursor_t*, unw_regnum_t, unw_fpreg_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_set_reg(unw_cursor_t*, unw_regnum_t, unw_word_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_set_fpreg(unw_cursor_t*, unw_regnum_t, unw_fpreg_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_resume(unw_cursor_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); + +extern const char* unw_regname(unw_cursor_t*, unw_regnum_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_get_proc_info(unw_cursor_t*, unw_proc_info_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_is_fpreg(unw_cursor_t*, unw_regnum_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_is_signal_frame(unw_cursor_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_get_proc_name(unw_cursor_t*, char*, size_t, unw_word_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +//extern int unw_get_save_loc(unw_cursor_t*, int, unw_save_loc_t*); + + +#pragma mark Remote data structures + +typedef enum { + UNW_NOT_A_REG = 0, + UNW_INTEGER_REG, + UNW_FLOATING_POINT_REG, + UNW_VECTOR_REG, + UNW_OTHER_REG +} unw_regtype_t; + +typedef enum { + UNW_TARGET_UNSPECIFIED = 0, + UNW_TARGET_I386, + UNW_TARGET_X86_64, + UNW_TARGET_PPC, + UNW_TARGET_ARM +} unw_targettype_t; + +typedef enum { + UNW_LOG_LEVEL_NONE = 0x00000000, + UNW_LOG_LEVEL_INFO = 0x00000001, + UNW_LOG_LEVEL_API = 0x00000002, + UNW_LOG_LEVEL_VERBOSE = 0x00000004, + UNW_LOG_LEVEL_TIMINGS = 0x00000008, + UNW_LOG_LEVEL_DEBUG = 0x00000010, + UNW_LOG_LEVEL_ALL = 0x0FFFFFFF +} unw_log_level_t; + +typedef enum { + UNW_CACHE_NONE = 0, + UNW_CACHE_GLOBAL, + UNW_CACHE_PER_THREAD +} unw_caching_policy_t; + +typedef struct { + int (*find_proc_info)(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pip, int need_unwind_info, void *arg); + int (*put_unwind_info)(unw_addr_space_t as, unw_proc_info_t *pip, void *arg); + int (*get_dyn_info_list_addr)(unw_addr_space_t as, unw_word_t *dilap, void *arg); + + // Reads or writes a memory object the size of a target pointer. + // Byte-swaps if necessary. + int (*access_mem)(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, int write, void *arg); + + // Register contents sent as-is (i.e. not byte-swapped). + // Register numbers are the driver program's numbering scheme as + // determined by the reg_info callbacks; libunwind will interrogate + // the driver program to figure out which numbers it uses to refer to + // which registers. + int (*access_reg)(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, int write, void *arg); + int (*access_fpreg)(unw_addr_space_t as, unw_regnum_t regnum, unw_fpreg_t *valp, int write, void *arg); + int (*resume)(unw_addr_space_t as, unw_cursor_t *cp, void *arg); + int (*get_proc_name)(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len, unw_word_t *offp, void *arg); + + + // Added to find the start of the image (executable, bundle, dylib, etc) + // for a given address. + // as - The address space to use + // ip - The address libunwind wants to know about + // mh - The Mach-O header address for this image + // text_start - The start of __TEXT segment (all its sections) + // text_end - The end address of __TEXT segment (all its sections) + // eh_frame - The start of __TEXT,__eh_frame + // eh_frame_len - The length of __TEXT,__eh_frame + // compact_unwind_start - The start of __TEXT,__unwind_info + // compact_unwind_len - The length of __TEXT,__unwind_info + // arg - The driver-provided generic argument + // All addresses are the in-memory, slid, addresses. + // If eh_frame or unwind_info are missing, addr and len is returned as 0. + int (*find_image_info)(unw_addr_space_t as, unw_word_t ip, unw_word_t *mh, + unw_word_t *text_start, unw_word_t *text_end, + unw_word_t *eh_frame, unw_word_t *eh_frame_len, + unw_word_t *compact_unwind_start, + unw_word_t *compact_unwind_len, void *arg); + + // Added to get the start and end address of a function without needing + // all of the information (and potential allocation) that the + // find_proc_info() call entails. + // as - The address space to use + // ip - The address libunwind wants to know about + // low - The start address of the function at 'ip' + // high - The first address past the function at 'ip' + // arg - The driver-provided generic argument + // If HIGH is unknown, it should be set to 0. All addresses + // are the in-memory, slid, addresses. + int (*get_proc_bounds)(unw_addr_space_t as, unw_word_t ip, + unw_word_t *low, unw_word_t *high, void *arg); + + // Added to support accessing non-word-size memory objects across + // platforms. No byte swapping should be done. + // as - The address space to use + // addr - The starting address to access + // extent - The extent of the region to access, in bytes + // valp - The local region to be written from / read into + // write - non-zero if the data is to be written into the target + // rather than read + // arg - The driver-provided generic argument (see unw_init_remote) + int (*access_raw)(unw_addr_space_t as, unw_word_t addr, unw_word_t extent, + uint8_t *valp, int write, void *arg); + + // Added to support identifying registers. + // libunwind will interrogate the driver program via this callback to + // identify what register numbers it is using; the register names are + // used to correlate that the driver program's register numbers with + // libunwind's internal register numbers. The driver program should + // use its own register numbers when requesting registers with + // unw_get_reg() and libunwind will provide the driver program's + // register numbers to the access_reg callback function. + // as - The address space to use + // regnum - The register number + // type - Write the register type to this address + // For a non-existent register, return UNW_ESUCCESS but + // write UNW_NOT_A_REG to type + // buf - If non-NULL, the register name is written to this address + // buf_len - The size of the buffer provided for the register name + // arg - The driver-provided generic argument (see unw_init_remote) + int (*reg_info)(unw_addr_space_t as, unw_regnum_t regnum, + unw_regtype_t* type, char *bufp, size_t buf_len, void *arg); + + // Added to read a vector register's value from the remote machine. + // as - The address space to use + // regnum - The register number + // valp - The local region to be written from / read into + // write - non-zero if the data is to be written into the target + // rather than read + // arg - The driver-specified generic argument + int (*access_vecreg)(unw_addr_space_t as, unw_regnum_t regnum, + unw_vecreg_t* valp, int write, void *arg); + + // Added to identify if an unwind cursor is pointing to _sigtramp(). + // After a _sigtramp we have an entire register set available and we should + // return any of the registers requested. + // as - The address space to use + // ip - The address of the function libunwind is examining + // arg - The driver-provided generic argument + // This function returns non-zero if ip is in _sigtramp. + int (*proc_is_sigtramp) (unw_addr_space_t as, unw_word_t ip, void *arg); + + // Added to identify if an unwind cursor is pointing to a debugger's + // inferior function call dummy frame. + // The driver program will need to provide the full register set (via the + // standard access_reg callback) for the function that was executing + // when the inferior function call was made; it will use these register + // values and not try to unwind out of the inferior function call dummy + // frame. + // After a inf func call we have an entire register set available and + // we should return any of the registers requested. + // as - The address space to use + // ip - The address of the function libunwind is examining + // sp - The stack pointer value of the frame + // arg - The driver-provided generic argument (see unw_init_remote) + // This function returns non-zero if ip/sp is an inferior function call + // dummy frame. + int (*proc_is_inferior_function_call) (unw_addr_space_t as, unw_word_t ip, + unw_word_t sp, void *arg); + + // Added to retrieve a register value from a above a debugger's inferior + // function call dummy frame. Similar to _sigtramp but the debugger will + // have the register context squirreled away in its own memory (or possibly + // saved on the stack somewhere). + // May be NULL if the program being unwound will not have a debugger + // calling functions mid-execution. + // as - The address space to use + // ip - The pc value for the dummy frame + // sp - The stack pointer for the dummy frame + // regnum - The register number in the driver program's register + // numbering scheme. + // valp - Pointer to a word of memory to be read/written + // write - Non-zero if libunwind is writing a new value to the reg, + // else it is reading the contents of that register. + // arg - The driver-provided generic argument (see unw_init_remote) + int (*access_reg_inf_func_call)(unw_addr_space_t as, unw_word_t ip, + unw_word_t sp, unw_regnum_t regnum, + unw_word_t *valp, int write, void *arg); + + // Added to iterate over unknown assembly instructions when analyzing a + // function prologue. Needed for ISAs with variable length instructions + // (i386, x86_64) or multiple instruction sizes (arm, thumb). + // Returns zero if the instruction length was successfully measured. + // as - The address space to use + // addr - The address of the instruction being measured + // length - Set to the length of the instruction + // arg - The driver-provided generic argument (see unw_init_remote) + int (*instruction_length)(unw_addr_space_t as, unw_word_t addr, + int *length, void *arg); + +} unw_accessors_t; + +extern int unw_init_remote(unw_cursor_t*, unw_addr_space_t, void*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern unw_accessors_t* unw_get_accessors(unw_addr_space_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern unw_addr_space_t unw_create_addr_space(unw_accessors_t*, unw_targettype_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern void unw_flush_caches(unw_addr_space_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern int unw_set_caching_policy(unw_addr_space_t, unw_caching_policy_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern void unw_destroy_addr_space(unw_addr_space_t asp) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); +extern void unw_set_logging_level(unw_addr_space_t, FILE *, unw_log_level_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); + +// Should be called when remote unwinding if a bundle in the remote process +// is unloaded +extern void unw_image_was_unloaded(unw_addr_space_t, unw_word_t mh) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); + +// Try to discern where the function's prologue instructions end +// start - start address of the function, required +// end - first address beyond the function, or zero if unknown +// endofprologue - set to the address after the last prologue instruction if successful +extern int unw_end_of_prologue_setup(unw_cursor_t*, unw_word_t start, unw_word_t end, unw_word_t *endofprologue) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA); + +/* + * Dynamic unwinding API + * NOT IMPLEMENTED on Mac OS X + * extern void _U_dyn_register(unw_dyn_info_t*); + * extern void _U_dyn_cancel(unw_dyn_info_t*); + */ + +#ifdef __cplusplus +} +#endif + +#pragma mark Register numbers + +// architecture independent register numbers +enum { + UNW_REG_IP = -1, // instruction pointer + UNW_REG_SP = -2, // stack pointer +}; + + +// 32-bit x86 registers +enum { + UNW_X86_EAX = 0, + UNW_X86_ECX = 1, + UNW_X86_EDX = 2, + UNW_X86_EBX = 3, + UNW_X86_EBP = 4, + UNW_X86_ESP = 5, + UNW_X86_ESI = 6, + UNW_X86_EDI = 7 +}; + + +// 64-bit x86_64 registers +enum { + UNW_X86_64_RAX = 0, + UNW_X86_64_RDX = 1, + UNW_X86_64_RCX = 2, + UNW_X86_64_RBX = 3, + UNW_X86_64_RSI = 4, + UNW_X86_64_RDI = 5, + UNW_X86_64_RBP = 6, + UNW_X86_64_RSP = 7, + UNW_X86_64_R8 = 8, + UNW_X86_64_R9 = 9, + UNW_X86_64_R10 = 10, + UNW_X86_64_R11 = 11, + UNW_X86_64_R12 = 12, + UNW_X86_64_R13 = 13, + UNW_X86_64_R14 = 14, + UNW_X86_64_R15 = 15 +}; + + +// 32-bit ppc register numbers +enum { + UNW_PPC_R0 = 0, + UNW_PPC_R1 = 1, + UNW_PPC_R2 = 2, + UNW_PPC_R3 = 3, + UNW_PPC_R4 = 4, + UNW_PPC_R5 = 5, + UNW_PPC_R6 = 6, + UNW_PPC_R7 = 7, + UNW_PPC_R8 = 8, + UNW_PPC_R9 = 9, + UNW_PPC_R10 = 10, + UNW_PPC_R11 = 11, + UNW_PPC_R12 = 12, + UNW_PPC_R13 = 13, + UNW_PPC_R14 = 14, + UNW_PPC_R15 = 15, + UNW_PPC_R16 = 16, + UNW_PPC_R17 = 17, + UNW_PPC_R18 = 18, + UNW_PPC_R19 = 19, + UNW_PPC_R20 = 20, + UNW_PPC_R21 = 21, + UNW_PPC_R22 = 22, + UNW_PPC_R23 = 23, + UNW_PPC_R24 = 24, + UNW_PPC_R25 = 25, + UNW_PPC_R26 = 26, + UNW_PPC_R27 = 27, + UNW_PPC_R28 = 28, + UNW_PPC_R29 = 29, + UNW_PPC_R30 = 30, + UNW_PPC_R31 = 31, + UNW_PPC_F0 = 32, + UNW_PPC_F1 = 33, + UNW_PPC_F2 = 34, + UNW_PPC_F3 = 35, + UNW_PPC_F4 = 36, + UNW_PPC_F5 = 37, + UNW_PPC_F6 = 38, + UNW_PPC_F7 = 39, + UNW_PPC_F8 = 40, + UNW_PPC_F9 = 41, + UNW_PPC_F10 = 42, + UNW_PPC_F11 = 43, + UNW_PPC_F12 = 44, + UNW_PPC_F13 = 45, + UNW_PPC_F14 = 46, + UNW_PPC_F15 = 47, + UNW_PPC_F16 = 48, + UNW_PPC_F17 = 49, + UNW_PPC_F18 = 50, + UNW_PPC_F19 = 51, + UNW_PPC_F20 = 52, + UNW_PPC_F21 = 53, + UNW_PPC_F22 = 54, + UNW_PPC_F23 = 55, + UNW_PPC_F24 = 56, + UNW_PPC_F25 = 57, + UNW_PPC_F26 = 58, + UNW_PPC_F27 = 59, + UNW_PPC_F28 = 60, + UNW_PPC_F29 = 61, + UNW_PPC_F30 = 62, + UNW_PPC_F31 = 63, + UNW_PPC_MQ = 64, + UNW_PPC_LR = 65, + UNW_PPC_CTR = 66, + UNW_PPC_AP = 67, + UNW_PPC_CR0 = 68, + UNW_PPC_CR1 = 69, + UNW_PPC_CR2 = 70, + UNW_PPC_CR3 = 71, + UNW_PPC_CR4 = 72, + UNW_PPC_CR5 = 73, + UNW_PPC_CR6 = 74, + UNW_PPC_CR7 = 75, + UNW_PPC_XER = 76, + UNW_PPC_V0 = 77, + UNW_PPC_V1 = 78, + UNW_PPC_V2 = 79, + UNW_PPC_V3 = 80, + UNW_PPC_V4 = 81, + UNW_PPC_V5 = 82, + UNW_PPC_V6 = 83, + UNW_PPC_V7 = 84, + UNW_PPC_V8 = 85, + UNW_PPC_V9 = 86, + UNW_PPC_V10 = 87, + UNW_PPC_V11 = 88, + UNW_PPC_V12 = 89, + UNW_PPC_V13 = 90, + UNW_PPC_V14 = 91, + UNW_PPC_V15 = 92, + UNW_PPC_V16 = 93, + UNW_PPC_V17 = 94, + UNW_PPC_V18 = 95, + UNW_PPC_V19 = 96, + UNW_PPC_V20 = 97, + UNW_PPC_V21 = 98, + UNW_PPC_V22 = 99, + UNW_PPC_V23 = 100, + UNW_PPC_V24 = 101, + UNW_PPC_V25 = 102, + UNW_PPC_V26 = 103, + UNW_PPC_V27 = 104, + UNW_PPC_V28 = 105, + UNW_PPC_V29 = 106, + UNW_PPC_V30 = 107, + UNW_PPC_V31 = 108, + UNW_PPC_VRSAVE = 109, + UNW_PPC_VSCR = 110, + UNW_PPC_SPE_ACC = 111, + UNW_PPC_SPEFSCR = 112 + +}; + + +}; // namespace lldb_private + + +#endif + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/include/mach-o/compact_unwind_encoding.h b/lldb/source/Plugins/Process/Utility/libunwind/include/mach-o/compact_unwind_encoding.h new file mode 100644 index 00000000000..bee2ad578d6 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/include/mach-o/compact_unwind_encoding.h @@ -0,0 +1,212 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- compact_unwind_encoding.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef __COMPACT_UNWIND_ENCODING__ +#define __COMPACT_UNWIND_ENCODING__ + +#include <stdint.h> + +namespace lldb_private { + +// +// Each final linked mach-o image has an optional __TEXT, __unwind_info section. +// This section is much smaller and faster to use than the __eh_frame section. +// + + + +// +// Compilers usually emit standard Dwarf FDEs. The linker recognizes standard FDEs and +// synthesizes a matching compact_unwind_encoding_t and adds it to the __unwind_info table. +// It is also possible for the compiler to emit __unwind_info entries for functions that +// have different unwind requirements at different ranges in the function. +// +typedef uint32_t compact_unwind_encoding_t; + + + +// +// The __unwind_info section is laid out for an efficient two level lookup. +// The header of the section contains a coarse index that maps function address +// to the page (4096 byte block) containing the unwind info for that function. +// + +#define UNWIND_SECTION_VERSION 1 +struct unwind_info_section_header +{ + uint32_t version; // UNWIND_SECTION_VERSION + uint32_t commonEncodingsArraySectionOffset; + uint32_t commonEncodingsArrayCount; + uint32_t personalityArraySectionOffset; + uint32_t personalityArrayCount; + uint32_t indexSectionOffset; + uint32_t indexCount; + // compact_unwind_encoding_t[] + // uintptr_t personalities[] + // unwind_info_section_header_index_entry[] + // unwind_info_section_header_lsda_index_entry[] +}; + +struct unwind_info_section_header_index_entry +{ + uint32_t functionOffset; + uint32_t secondLevelPagesSectionOffset; // section offset to start of regular or compress page + uint32_t lsdaIndexArraySectionOffset; // section offset to start of lsda_index array for this range +}; + +struct unwind_info_section_header_lsda_index_entry +{ + uint32_t functionOffset; + uint32_t lsdaOffset; +}; + +// +// There are two kinds of second level index pages: regular and compressed. +// A compressed page can hold up to 1021 entries, but it cannot be used +// if too many different encoding types are used. The regular page holds +// 511 entries. +// + +struct unwind_info_regular_second_level_entry +{ + uint32_t functionOffset; + compact_unwind_encoding_t encoding; +}; + +#define UNWIND_SECOND_LEVEL_REGULAR 2 +struct unwind_info_regular_second_level_page_header +{ + uint32_t kind; // UNWIND_SECOND_LEVEL_REGULAR + uint16_t entryPageOffset; + uint16_t entryCount; + // entry array +}; + +#define UNWIND_SECOND_LEVEL_COMPRESSED 3 +struct unwind_info_compressed_second_level_page_header +{ + uint32_t kind; // UNWIND_SECOND_LEVEL_COMPRESSED + uint16_t entryPageOffset; + uint16_t entryCount; + uint16_t encodingsPageOffset; + uint16_t encodingsCount; + // 32-bit entry array + // encodings array +}; + +#define UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry) (entry & 0x00FFFFFF) +#define UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry) ((entry >> 24) & 0xFF) + + + +// architecture independent bits +enum { + UNWIND_IS_NOT_FUNCTION_START = 0x80000000, + UNWIND_HAS_LSDA = 0x40000000, + UNWIND_PERSONALITY_MASK = 0x30000000, +}; + + +// x86_64 +// +// 1-bit: start +// 1-bit: has lsda +// 2-bit: personality index +// +// 4-bits: 0=old, 1=rbp based, 2=stack-imm, 3=stack-ind, 4=dwarf +// rbp based: +// 15-bits (5*3-bits per reg) register permutation +// 8-bits for stack offset +// frameless: +// 8-bits stack size +// 3-bits stack adjust +// 3-bits register count +// 10-bits register permutation +// +enum { + UNWIND_X86_64_MODE_MASK = 0x0F000000, + UNWIND_X86_64_MODE_COMPATIBILITY = 0x00000000, + UNWIND_X86_64_MODE_RBP_FRAME = 0x01000000, + UNWIND_X86_64_MODE_STACK_IMMD = 0x02000000, + UNWIND_X86_64_MODE_STACK_IND = 0x03000000, + UNWIND_X86_64_MODE_DWARF = 0x04000000, + + UNWIND_X86_64_RBP_FRAME_REGISTERS = 0x00007FFF, + UNWIND_X86_64_RBP_FRAME_OFFSET = 0x00FF0000, + + UNWIND_X86_64_FRAMELESS_STACK_SIZE = 0x00FF0000, + UNWIND_X86_64_FRAMELESS_STACK_ADJUST = 0x0000E000, + UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT = 0x00001C00, + UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF, + + UNWIND_X86_64_DWARF_SECTION_OFFSET = 0x00FFFFFF, +}; + +enum { + UNWIND_X86_64_REG_NONE = 0, + UNWIND_X86_64_REG_RBX = 1, + UNWIND_X86_64_REG_R12 = 2, + UNWIND_X86_64_REG_R13 = 3, + UNWIND_X86_64_REG_R14 = 4, + UNWIND_X86_64_REG_R15 = 5, + UNWIND_X86_64_REG_RBP = 6, +}; + + +// x86 +// +// 1-bit: start +// 1-bit: has lsda +// 2-bit: personality index +// +// 4-bits: 0=old, 1=ebp based, 2=stack-imm, 3=stack-ind, 4=dwarf +// ebp based: +// 15-bits (5*3-bits per reg) register permutation +// 8-bits for stack offset +// frameless: +// 8-bits stack size +// 3-bits stack adjust +// 3-bits register count +// 10-bits register permutation +// +enum { + UNWIND_X86_MODE_MASK = 0x0F000000, + UNWIND_X86_MODE_COMPATIBILITY = 0x00000000, + UNWIND_X86_MODE_EBP_FRAME = 0x01000000, + UNWIND_X86_MODE_STACK_IMMD = 0x02000000, + UNWIND_X86_MODE_STACK_IND = 0x03000000, + UNWIND_X86_MODE_DWARF = 0x04000000, + + UNWIND_X86_EBP_FRAME_REGISTERS = 0x00007FFF, + UNWIND_X86_EBP_FRAME_OFFSET = 0x00FF0000, + + UNWIND_X86_FRAMELESS_STACK_SIZE = 0x00FF0000, + UNWIND_X86_FRAMELESS_STACK_ADJUST = 0x0000E000, + UNWIND_X86_FRAMELESS_STACK_REG_COUNT = 0x00001C00, + UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF, + + UNWIND_X86_DWARF_SECTION_OFFSET = 0x00FFFFFF, +}; + +enum { + UNWIND_X86_REG_NONE = 0, + UNWIND_X86_REG_EBX = 1, + UNWIND_X86_REG_ECX = 2, + UNWIND_X86_REG_EDX = 3, + UNWIND_X86_REG_EDI = 4, + UNWIND_X86_REG_ESI = 5, + UNWIND_X86_REG_EBP = 6, +}; + +}; // namespace lldb_private + +#endif + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/include/unwind.h b/lldb/source/Plugins/Process/Utility/libunwind/include/unwind.h new file mode 100644 index 00000000000..80b9d2881c2 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/include/unwind.h @@ -0,0 +1,213 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- unwind.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// C interface to libuwind +// +// Source compatible with Level 1 Base ABI documented at: +// http://www.codesourcery.com/public/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + + +#ifndef __UNWIND_H__ +#define __UNWIND_H__ + +#include <stdint.h> +#include <stddef.h> +#include <Availability.h> + +namespace lldb_private { + +typedef enum { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8 +} _Unwind_Reason_Code; + +typedef enum { + _UA_SEARCH_PHASE = 1, + _UA_CLEANUP_PHASE = 2, + _UA_HANDLER_FRAME = 4, + _UA_FORCE_UNWIND = 8, + _UA_END_OF_STACK = 16 // gcc extension to C++ ABI +} _Unwind_Action; + + +struct _Unwind_Context; // opaque +struct _Unwind_Exception; // forward declaration + +struct _Unwind_Exception { + uint64_t exception_class; + void (*exception_cleanup)(_Unwind_Reason_Code reason, struct _Unwind_Exception* exc); + uintptr_t private_1; // non-zero means forced unwind + uintptr_t private_2; // holds sp that phase1 found for phase2 to use +}; + + +typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) + (int version, + _Unwind_Action actions, + uint64_t exceptionClass, + struct _Unwind_Exception* exceptionObject, + struct _Unwind_Context* context, + void* stop_parameter ); + + +typedef _Unwind_Reason_Code (*__personality_routine) + (int version, + _Unwind_Action actions, + uint64_t exceptionClass, + struct _Unwind_Exception* exceptionObject, + struct _Unwind_Context* context); + + + +#ifdef __cplusplus +extern "C" { +#endif + +// +// The following are the base functions documented by the C++ ABI +// +#if __arm__ + extern _Unwind_Reason_Code _Unwind_SjLj_RaiseException(struct _Unwind_Exception* exception_object); + extern void _Unwind_SjLj_Resume(struct _Unwind_Exception* exception_object); +#else + extern _Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception* exception_object); + extern void _Unwind_Resume(struct _Unwind_Exception* exception_object); +#endif +extern void _Unwind_DeleteException(struct _Unwind_Exception* exception_object); +extern uintptr_t _Unwind_GetGR(struct _Unwind_Context* context, int index); +extern void _Unwind_SetGR(struct _Unwind_Context* context, int index, uintptr_t new_value); +extern uintptr_t _Unwind_GetIP(struct _Unwind_Context* context); +extern void _Unwind_SetIP(struct _Unwind_Context*, uintptr_t new_value); +extern uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context* context); +extern uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context* context); +#if __arm__ + extern _Unwind_Reason_Code _Unwind_SjLj_ForcedUnwind(struct _Unwind_Exception* exception_object, _Unwind_Stop_Fn stop, void* stop_parameter ); +#else + extern _Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception* exception_object, _Unwind_Stop_Fn stop, void* stop_parameter ); +#endif + +#if __arm__ + typedef struct _Unwind_FunctionContext* _Unwind_FunctionContext_t; + extern void _Unwind_SjLj_Register(_Unwind_FunctionContext_t fc); + extern void _Unwind_SjLj_Unregister(_Unwind_FunctionContext_t fc); +#endif + +// +// The following are semi-suppoted extensions to the C++ ABI +// + + +// +// called by __cxa_rethrow(). +// +#if __arm__ + extern _Unwind_Reason_Code _Unwind_SjLj_Resume_or_Rethrow(struct _Unwind_Exception* exception_object); +#else + extern _Unwind_Reason_Code _Unwind_Resume_or_Rethrow(struct _Unwind_Exception* exception_object); +#endif + + +// +// _Unwind_Backtrace() is a gcc extension that walks the stack and calls the +// _Unwind_Trace_Fn once per frame until it reaches the bottom of the stack +// or the _Unwind_Trace_Fn function returns something other than _URC_NO_REASON. +// +typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context*, void*); +extern _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void*); + + +// +// _Unwind_GetCFA is a gcc extension that can be called from within a personality +// handler to get the CFA (stack pointer before call) of current frame. +// +extern uintptr_t _Unwind_GetCFA(struct _Unwind_Context*); + + +// +// _Unwind_GetIPInfo is a gcc extension that can be called from within a personality +// handler. Similar to _Unwind_GetIP() but also returns in *ipBefore a non-zero +// value if the instruction pointer is at or before the instruction causing +// the unwind. Normally, in a function call, the IP returned is the return address +// which is after the call instruction and may be past the end of the function +// containing the call instruction. +// +extern uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context* context, int* ipBefore); + + +// +// __register_frame() is used with dynamically generated code to register the FDE +// for a generated (JIT) code. The FDE must use pc-rel addressing to point to its +// function and optional LSDA. __register_frame() has existed in all versions of +// Mac OS X, but in 10.4 and 10.5 it was buggy and did not actually register the +// FDE with the unwinder. In 10.6 and later it does register properly. +// +extern void __register_frame(const void* fde); +extern void __deregister_frame(const void* fde); + + +// +// _Unwind_Find_FDE() will locate the FDE if the pc is in some function that has +// an associated FDE. Note, Mac OS X 10.6 and later, introduces "compact unwind info" +// which the runtime uses in preference to dwarf unwind info. This function +// will only work if the target function has an FDE but no compact unwind info. +// +struct dwarf_eh_bases +{ + uintptr_t tbase; + uintptr_t dbase; + uintptr_t func; +}; +extern const void* _Unwind_Find_FDE(const void* pc, struct dwarf_eh_bases*); + + +// +// This function attempts to find the start (address of first instruction) of +// a function given an address inside the function. It only works if the function +// has an FDE (dwarf unwind info). +// This function is unimplemented on Mac OS X 10.6 and later. Instead, use +// _Unwind_Find_FDE() and look at the dwarf_eh_bases.func result. +extern void* _Unwind_FindEnclosingFunction(void* pc); + + +// Mac OS X does not support text-rel and data-rel addressing so these functions are unimplemented +extern uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context* context) __attribute__((unavailable)); +extern uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context* context) __attribute__((unavailable)); + + + +// Mac OS X 10.4 and 10.5 had implementations of these functions in libgcc_s.dylib, +// but they never worked. These functions are no longer available. +extern void __register_frame_info_bases(const void* fde, void* ob, void* tb, void* db) __attribute__((unavailable)); +extern void __register_frame_info(const void* fde, void* ob) __attribute__((unavailable)); +extern void __register_frame_info_table_bases(const void* fde, void* ob,void* tb, void* db) __attribute__((unavailable)); +extern void __register_frame_info_table(const void* fde, void* ob) __attribute__((unavailable)); +extern void __register_frame_table(const void* fde) __attribute__((unavailable)); +extern void* __deregister_frame_info(const void* fde) __attribute__((unavailable)); +extern void* __deregister_frame_info_bases(const void* fde) __attribute__((unavailable)); + + +#ifdef __cplusplus +} +#endif + +}; // namespace lldb_private + +#endif // __UNWIND_H__ + + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/AddressSpace.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/AddressSpace.hpp new file mode 100644 index 00000000000..5173dc0068e --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/AddressSpace.hpp @@ -0,0 +1,456 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- AddressSpace.hpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// +// C++ interface to lower levels of libuwind +// + +#ifndef __ADDRESSSPACE_HPP__ +#define __ADDRESSSPACE_HPP__ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <dlfcn.h> +#include <mach-o/loader.h> +#include <mach-o/getsect.h> +#if !defined (SUPPORT_REMOTE_UNWINDING) +#include <mach-o/dyld_priv.h> +#endif +#include <mach/ppc/thread_status.h> +#include <mach/i386/thread_status.h> +#include <Availability.h> + +#include "FileAbstraction.hpp" +#include "libunwind.h" +#include "InternalMacros.h" +#include "dwarf2.h" +#include "RemoteProcInfo.hpp" + +#if defined (SUPPORT_REMOTE_UNWINDING) +bool _dyld_find_unwind_sections(void* addr, void* info) +{ + assert("unwinding with a non-remote process not supported."); + return false; +} +#endif // SUPPORT_REMOTE_UNWINDING + +namespace lldb_private { + +/// +/// LocalAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread +/// in the same process. It compiles away and making local unwinds very fast. +/// +class LocalAddressSpace +{ +public: + + #if __LP64__ + typedef uint64_t pint_t; + typedef int64_t sint_t; + #else + typedef uint32_t pint_t; + typedef int32_t sint_t; + #endif + int getBytes(pint_t addr, pint_t extent, uint8_t* buf) { memcpy(buf, (void*)addr, extent); return 1; } + uint8_t get8(pint_t addr) { return *((uint8_t*)addr); } + uint16_t get16(pint_t addr) { return *((uint16_t*)addr); } + uint32_t get32(pint_t addr) { return *((uint32_t*)addr); } + uint64_t get64(pint_t addr) { return *((uint64_t*)addr); } + double getDouble(pint_t addr) { return *((double*)addr); } + v128 getVector(pint_t addr) { return *((v128*)addr); } + + uint8_t get8(pint_t addr, int& err) { return *((uint8_t*)addr); err = 0; } + uint16_t get16(pint_t addr, int& err) { return *((uint16_t*)addr); err = 0; } + uint32_t get32(pint_t addr, int& err) { return *((uint32_t*)addr); err = 0; } + uint64_t get64(pint_t addr, int& err) { return *((uint64_t*)addr); err = 0; } + double getDouble(pint_t addr, int& err) { return *((double*)addr); err = 0; } + v128 getVector(pint_t addr, int& err) { return *((v128*)addr); err = 0; } + + uintptr_t getP(pint_t addr); + uintptr_t getP(pint_t addr, int &err); + static uint64_t getULEB128(pint_t& addr, pint_t end); + static int64_t getSLEB128(pint_t& addr, pint_t end); + + pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding); + bool findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset); + bool findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart); + +#if defined (SUPPORT_REMOTE_UNWINDING) + RemoteProcInfo* getRemoteProcInfo () { return NULL; } + unw_accessors_t* accessors() { return NULL; } + unw_addr_space_t wrap() { return NULL; } +#endif +}; + +LocalAddressSpace sThisAddress; + +inline uintptr_t LocalAddressSpace::getP(pint_t addr) +{ +#if __LP64__ + return get64(addr); +#else + return get32(addr); +#endif +} + +inline uintptr_t LocalAddressSpace::getP(pint_t addr, int &err) +{ +#if __LP64__ + return get64(addr); +#else + return get32(addr); +#endif + err = 0; +} + +/* Read a ULEB128 into a 64-bit word. */ +inline uint64_t +LocalAddressSpace::getULEB128(pint_t& addr, pint_t end) +{ + const uint8_t* p = (uint8_t*)addr; + const uint8_t* pend = (uint8_t*)end; + uint64_t result = 0; + int bit = 0; + do { + uint64_t b; + + if ( p == pend ) + ABORT("truncated uleb128 expression"); + + b = *p & 0x7f; + + if (bit >= 64 || b << bit >> bit != b) { + ABORT("malformed uleb128 expression"); + } + else { + result |= b << bit; + bit += 7; + } + } while ( *p++ >= 0x80 ); + addr = (pint_t)p; + return result; +} + +/* Read a SLEB128 into a 64-bit word. */ +inline int64_t +LocalAddressSpace::getSLEB128(pint_t& addr, pint_t end) +{ + const uint8_t* p = (uint8_t*)addr; + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + byte = *p++; + result |= ((byte & 0x7f) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ( (byte & 0x40) != 0 ) + result |= (-1LL) << bit; + addr = (pint_t)p; + return result; +} + +LocalAddressSpace::pint_t +LocalAddressSpace::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding) +{ + pint_t startAddr = addr; + const uint8_t* p = (uint8_t*)addr; + pint_t result; + + // first get value + switch (encoding & 0x0F) { + case DW_EH_PE_ptr: + result = getP(addr); + p += sizeof(pint_t); + addr = (pint_t)p; + break; + case DW_EH_PE_uleb128: + result = getULEB128(addr, end); + break; + case DW_EH_PE_udata2: + result = get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_udata4: + result = get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_udata8: + result = get64(addr); + p += 8; + addr = (pint_t)p; + break; + case DW_EH_PE_sleb128: + result = getSLEB128(addr, end); + break; + case DW_EH_PE_sdata2: + result = (int16_t)get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata4: + result = (int32_t)get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata8: + result = get64(addr); + p += 8; + addr = (pint_t)p; + break; + default: + ABORT("unknown pointer encoding"); + } + + // then add relative offset + switch ( encoding & 0x70 ) { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + result += startAddr; + break; + case DW_EH_PE_textrel: + ABORT("DW_EH_PE_textrel pointer encoding not supported"); + break; + case DW_EH_PE_datarel: + ABORT("DW_EH_PE_datarel pointer encoding not supported"); + break; + case DW_EH_PE_funcrel: + ABORT("DW_EH_PE_funcrel pointer encoding not supported"); + break; + case DW_EH_PE_aligned: + ABORT("DW_EH_PE_aligned pointer encoding not supported"); + break; + default: + ABORT("unknown pointer encoding"); + break; + } + + if ( encoding & DW_EH_PE_indirect ) + result = getP(result); + + return result; +} + + +inline bool LocalAddressSpace::findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart) +{ +#if !defined (SUPPORT_REMOTE_UNWINDING) + dyld_unwind_sections info; + if ( _dyld_find_unwind_sections((void*)addr, &info) ) { + mh = (pint_t)info.mh; + dwarfStart = (pint_t)info.dwarf_section; + dwarfLen = (pint_t)info.dwarf_section_length; + compactStart = (pint_t)info.compact_unwind_section; + return true; + } +#else + assert("unwinding with a non-remote process not supported."); +#endif + return false; +} + + +inline bool LocalAddressSpace::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset) +{ + dl_info dyldInfo; + if ( dladdr((void*)addr, &dyldInfo) ) { + if ( dyldInfo.dli_sname != NULL ) { + strlcpy(buf, dyldInfo.dli_sname, bufLen); + *offset = (addr - (pint_t)dyldInfo.dli_saddr); + return true; + } + } + return false; +} + +#if defined (SUPPORT_REMOTE_UNWINDING) +/// +/// OtherAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread +/// in the another process. The other process can be a different endianness and a different +/// pointer size and is handled by the P template parameter. +/// +template <typename P> +class OtherAddressSpace +{ +public: + OtherAddressSpace (unw_addr_space_t remote_addr_space, void* arg) : fAddrSpace ((unw_addr_space_remote *)remote_addr_space), fArg(arg) + { + if (fAddrSpace->type != UNW_REMOTE) + ABORT("OtherAddressSpace ctor called with non-remote address space."); + fRemoteProcInfo = fAddrSpace->ras; + } + + typedef typename P::uint_t pint_t; + typedef typename P::int_t sint_t; + + int getBytes(pint_t addr, pint_t extent, uint8_t* buf) { return fRemoteProcInfo->getBytes (addr, extent, buf, fArg); } + uint8_t get8(pint_t addr) { return fRemoteProcInfo->get8(addr, fArg); } + uint16_t get16(pint_t addr) { return fRemoteProcInfo->get16(addr, fArg); } + uint32_t get32(pint_t addr) { return fRemoteProcInfo->get32(addr, fArg); } + uint64_t get64(pint_t addr) { return fRemoteProcInfo->get64(addr, fArg); } + pint_t getP(pint_t addr) { return fRemoteProcInfo->getP(addr, fArg); } + + uint8_t get8(pint_t addr, int& err) { return fRemoteProcInfo->get8(addr, err, fArg); } + uint16_t get16(pint_t addr, int& err) { return fRemoteProcInfo->get16(addr, err, fArg); } + uint32_t get32(pint_t addr, int& err) { return fRemoteProcInfo->get32(addr, err, fArg); } + uint64_t get64(pint_t addr, int& err) { return fRemoteProcInfo->get64(addr, err, fArg); } + pint_t getP(pint_t addr, int &err) { return fRemoteProcInfo->getP(addr, err, fArg); } + + uint64_t getULEB128(pint_t& addr, pint_t end) { return fRemoteProcInfo->getULEB128 (addr, end, fArg); } + int64_t getSLEB128(pint_t& addr, pint_t end) { return fRemoteProcInfo->getSLEB128 (addr, end, fArg); } + pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding); + double getDouble(pint_t addr); + v128 getVector(pint_t addr); + bool findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset); + bool findFunctionExtent(pint_t addr, unw_word_t* begin, unw_word_t* end); + bool findUnwindSections(pint_t addr, pint_t& mh, pint_t& eh_frame_start, pint_t& eh_frame_len, pint_t& compactStart); + RemoteProcInfo* getRemoteProcInfo () { return fRemoteProcInfo; } + unw_accessors_t* accessors() { return fRemoteProcInfo->getAccessors(); } + unw_addr_space_t wrap() { return (unw_addr_space_t) fAddrSpace; } +private: + void* localCopy(pint_t addr); + unw_addr_space_remote *fAddrSpace; + RemoteProcInfo* fRemoteProcInfo; + void* fArg; +}; + +template <typename P> +typename OtherAddressSpace<P>::pint_t OtherAddressSpace<P>::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding) +{ + pint_t startAddr = addr; + pint_t p = addr; + pint_t result; + + // first get value + switch (encoding & 0x0F) { + case DW_EH_PE_ptr: + result = fRemoteProcInfo->getP(addr, fArg); + p += sizeof(pint_t); + addr = p; + break; + case DW_EH_PE_uleb128: + result = fRemoteProcInfo->getULEB128(addr, end, fArg); + break; + case DW_EH_PE_udata2: + result = fRemoteProcInfo->get16(addr, fArg); + p += 2; + addr = p; + break; + case DW_EH_PE_udata4: + result = fRemoteProcInfo->get32(addr, fArg); + p += 4; + addr = p; + break; + case DW_EH_PE_udata8: + result = fRemoteProcInfo->get64(addr, fArg); + p += 8; + addr = p; + break; + case DW_EH_PE_sleb128: + result = fRemoteProcInfo->getSLEB128(addr, end, fArg); + break; + case DW_EH_PE_sdata2: + result = (int16_t)fRemoteProcInfo->get16(addr, fArg); + p += 2; + addr = p; + break; + case DW_EH_PE_sdata4: + result = (int32_t)fRemoteProcInfo->get32(addr, fArg); + p += 4; + addr = p; + break; + case DW_EH_PE_sdata8: + result = fRemoteProcInfo->get64(addr, fArg); + p += 8; + addr = p; + break; + default: + ABORT("unknown pointer encoding"); + } + + // then add relative offset + switch ( encoding & 0x70 ) { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + result += startAddr; + break; + case DW_EH_PE_textrel: + ABORT("DW_EH_PE_textrel pointer encoding not supported"); + break; + case DW_EH_PE_datarel: + ABORT("DW_EH_PE_datarel pointer encoding not supported"); + break; + case DW_EH_PE_funcrel: + ABORT("DW_EH_PE_funcrel pointer encoding not supported"); + break; + case DW_EH_PE_aligned: + ABORT("DW_EH_PE_aligned pointer encoding not supported"); + break; + default: + ABORT("unknown pointer encoding"); + break; + } + + if ( encoding & DW_EH_PE_indirect ) + result = fRemoteProcInfo->getP(result, fArg); + + return result; +} + +template <typename P> +double OtherAddressSpace<P>::getDouble(pint_t addr) +{ + return fRemoteProcInfo->getDouble(addr, fArg); +} + +template <typename P> +v128 OtherAddressSpace<P>::getVector(pint_t addr) +{ + return fRemoteProcInfo->getVector(addr, fArg); +} + +template <typename P> +bool OtherAddressSpace<P>::findUnwindSections(pint_t addr, pint_t& mh, pint_t& eh_frame_start, pint_t& eh_frame_len, pint_t& compactStart) +{ + compactStart = 0; + uint64_t t_mh, t_text_start, t_text_end, t_eh_frame_start, t_eh_frame_len, t_compact_start; + if (fRemoteProcInfo->getImageAddresses (addr, t_mh, t_text_start, t_text_end, t_eh_frame_start, t_eh_frame_len, t_compact_start, fArg)) + { + mh = t_mh; + eh_frame_start = t_eh_frame_start; + eh_frame_len = t_eh_frame_len; + compactStart = t_compact_start; + return true; + } + return false; +} + +template <typename P> +bool OtherAddressSpace<P>::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset) +{ + return fRemoteProcInfo->findFunctionName (addr, buf, bufLen, offset, fArg); +} + +#endif // SUPPORT_REMOTE_UNWINDING + + +} // namespace lldb_private + + + +#endif // __ADDRESSSPACE_HPP__ diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/ArchDefaultUnwinder.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/ArchDefaultUnwinder.hpp new file mode 100644 index 00000000000..d19d7aea50f --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/ArchDefaultUnwinder.hpp @@ -0,0 +1,115 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- ArchDefaultUnwinder.hpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Unwind a stack frame using nothing but the default conventions on +// this architecture. + +#ifndef __ARCH_DEFAULT_UNWINDER_HPP +#define __ARCH_DEFAULT_UNWINDER_HPP + +#if defined (SUPPORT_REMOTE_UNWINDING) + +#include "AddressSpace.hpp" +#include "Registers.hpp" +#include "RemoteRegisterMap.hpp" +#include "RemoteProcInfo.hpp" + +namespace lldb_private +{ + +// As a last ditch attempt to unwind a stack frame, unwind by the +// architecture's typical conventions. We try compact unwind, eh frame CFI, +// and then assembly profiling if we have function bounds -- but if we're +// looking at an address with no function bounds or unwind info, make a best +// guess at how to get out. + +// In practice, this is usually hit when we try to step out of start() in a +// stripped application binary, we've jumped to 0x0, or we're in jitted code +// in the heap. + +template <typename A, typename R> +int stepByArchitectureDefault_x86 (A& addressSpace, R& registers, + uint64_t pc, int wordsize) { + R newRegisters(registers); + RemoteRegisterMap *rmap = addressSpace.getRemoteProcInfo()->getRegisterMap(); + int frame_reg = rmap->unwind_regno_for_frame_pointer(); + int stack_reg = rmap->unwind_regno_for_stack_pointer(); + int err; + + /* If the pc is 0x0 either we call'ed 0 (went thorugh a null function + pointer) or this is a thread in the middle of being created that has + no stack at all. + For the call-0x0 case, we know how to unwind that - the pc is at + the stack pointer. + + Otherwise follow the usual convention of trusting that RBP/EBP has the + start of the stack frame and we can find the caller's pc based on + that. */ + + uint64_t newpc, newframeptr; + newpc = 0; + newframeptr = -1; + if (pc == 0) { + uint64_t oldsp = registers.getRegister(stack_reg); + err = 0; + if (oldsp != 0) { + newpc = addressSpace.getP(registers.getRegister(stack_reg), err); + if (err != 0) + return UNW_EUNSPEC; + newRegisters.setIP (newpc); + newRegisters.setRegister (stack_reg, registers.getRegister(stack_reg) + + wordsize); + } + } + else { + newpc = addressSpace.getP(registers.getRegister(frame_reg) + + wordsize, err); + if (err != 0) + return UNW_EUNSPEC; + + newRegisters.setIP (newpc); + newframeptr = addressSpace.getP(registers.getRegister(frame_reg), + err); + if (err != 0) + return UNW_EUNSPEC; + + newRegisters.setRegister (frame_reg, newframeptr); + newRegisters.setRegister (stack_reg, registers.getRegister(frame_reg) + + (wordsize * 2)); + } + registers = newRegisters; + if (newpc == 0 || newframeptr == 0) + return UNW_STEP_END; + return UNW_STEP_SUCCESS; +} + +template <typename A> +int stepByArchitectureDefault (A& addressSpace, Registers_x86_64 ®isters, + uint64_t pc) { + return stepByArchitectureDefault_x86 (addressSpace, registers, pc, 8); +} + +template <typename A> +int stepByArchitectureDefault (A& addressSpace, Registers_x86& registers, + uint64_t pc) { + return stepByArchitectureDefault_x86 (addressSpace, registers, pc, 4); +} + +template <typename A> +int stepByArchitectureDefault (A& addressSpace, Registers_ppc& registers, + uint64_t pc) { + ABORT("Remote unwinding not supported for ppc."); + return UNW_EUNSPEC; +} + +}; // namespace lldb_private + +#endif // SUPPORT_REMOTE_UNWINDING +#endif // __ARCH_DEFAULT_UNWINDER_HPP diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyInstructions.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyInstructions.hpp new file mode 100644 index 00000000000..1e695d5e4f0 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyInstructions.hpp @@ -0,0 +1,147 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- AssemblyInstructions.hpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __ASSEMBLY_INSTRUCTIONS_HPP +#define __ASSEMBLY_INSTRUCTIONS_HPP + +#if defined (SUPPORT_REMOTE_UNWINDING) + +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif + +#include <limits.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <map> + +#include "libunwind.h" +#include "AssemblyParser.hpp" +#include "AddressSpace.hpp" +#include "Registers.hpp" +#include "RemoteUnwindProfile.h" + +namespace lldb_private +{ + +// A debug function to dump the contents of an RemoteUnwindProfile to +// stdout in a human readable form. + +template <typename A, typename R> +void printProfile (A& addressSpace, uint64_t pc, RemoteUnwindProfile* profile, R& registers) { + RemoteProcInfo *procinfo = addressSpace.getRemoteProcInfo(); + RemoteRegisterMap *regmap = procinfo->getRegisterMap(); + + procinfo->logDebug ("Print profile: given pc of 0x%llx, profile has range 0x%llx - 0x%llx", pc, profile->fStart, profile->fEnd); + procinfo->logDebug ("CFA locations:"); + std::map<uint64_t, RemoteUnwindProfile::CFALocation>::iterator i; + for (i = profile->cfa.begin(); i != profile->cfa.end(); ++i) { + procinfo->logDebug (" as of 0x%llx cfa is based off of reg %d (%s) offset %d", i->first, i->second.regno, regmap->unwind_regno_to_name(i->second.regno), i->second.offset); + } + procinfo->logDebug ("Caller's saved IP is at %d bytes offset from the cfa", (int)profile->returnAddress.value); + procinfo->logDebug ("Register saves:"); + std::map<uint64_t, std::vector<RemoteUnwindProfile::SavedReg> >::iterator j; + for (j = profile->saved_registers.begin(); j != profile->saved_registers.end(); ++j) { + char *tbuf1, *tbuf2, *tbuf3; + asprintf (&tbuf1, " at pc 0x%llx there are %d registers saved ", j->first, (int) j->second.size()); + std::vector<RemoteUnwindProfile::SavedReg>::iterator k; + for (k = j->second.begin(); k != j->second.end(); ++k) { + if (k->location == RemoteUnwindProfile::kRegisterOffsetFromCFA) { + asprintf (&tbuf2, "[reg %d (%s) is %d bytes from cfa] ", k->regno, regmap->unwind_regno_to_name(k->regno), (int) k->value); + int newlen = strlen (tbuf1) + strlen (tbuf2) + 1; + tbuf3 = (char *) malloc (newlen); + strcpy (tbuf3, tbuf1); + strcat (tbuf3, tbuf2); + free (tbuf1); + free (tbuf2); + tbuf1 = tbuf3; + } + if (k->location == RemoteUnwindProfile::kRegisterIsCFA) { + asprintf (&tbuf2, "[reg %d (%s) is the same as the cfa] ", k->regno, regmap->unwind_regno_to_name(k->regno)); + int newlen = strlen (tbuf1) + strlen (tbuf2) + 1; + tbuf3 = (char *) malloc (newlen); + strcpy (tbuf3, tbuf1); + strcat (tbuf3, tbuf2); + free (tbuf1); + free (tbuf2); + tbuf1 = tbuf3; + } + } + procinfo->logDebug ("%s", tbuf1); + free (tbuf1); + } +} + +template <typename A, typename R> +int stepWithAssembly (A& addressSpace, uint64_t pc, RemoteUnwindProfile* profile, R& registers) { + R newRegisters(registers); + RemoteProcInfo *procinfo = addressSpace.getRemoteProcInfo(); + if (pc > profile->fEnd) + ABORT("stepWithAssembly called with pc not in RemoteUnwindProfile's bounds"); + + if (procinfo && (procinfo->getDebugLoggingLevel() & UNW_LOG_LEVEL_DEBUG)) + printProfile (addressSpace, pc, profile, registers); + + std::map<uint64_t, RemoteUnwindProfile::CFALocation>::iterator i = profile->cfa.lower_bound (pc); + if (i == profile->cfa.begin() && i == profile->cfa.end()) + return UNW_EINVAL; + if (i == profile->cfa.end()) { + --i; + } else { + if (i != profile->cfa.begin() && i->first != pc) + --i; + } + + uint64_t cfa = registers.getRegister (i->second.regno) + i->second.offset; + + std::map<uint64_t, std::vector<RemoteUnwindProfile::SavedReg> >::iterator j; + + for (j = profile->saved_registers.begin(); j != profile->saved_registers.end() && j->first <= pc; ++j) { + std::vector<RemoteUnwindProfile::SavedReg>::iterator k = j->second.begin(); + for (; k != j->second.end(); ++k) { + RemoteUnwindProfile::SavedReg sr = *k; + if (sr.type == RemoteUnwindProfile::kGeneralPurposeRegister) { + uint64_t result; + int err = 0; + switch (sr.location) { + case RemoteUnwindProfile::kRegisterOffsetFromCFA: + result = addressSpace.getP(cfa + sr.value, err); + break; + case RemoteUnwindProfile::kRegisterIsCFA: + result = cfa; + break; + default: + ABORT("Unknown saved register location in stepWithAssembly."); + } + // If we failed to read remote memory, stop unwinding. + if (err) + return UNW_STEP_END; + newRegisters.setRegister (sr.regno, result); + } + } + } + newRegisters.setSP(cfa); + uint64_t ip = addressSpace.getP(cfa + profile->returnAddress.value); + if (ip == 0) + return UNW_STEP_END; + newRegisters.setIP(ip); + registers = newRegisters; + return UNW_STEP_SUCCESS; +} + +}; // namespace lldb_private + +#endif // SUPPORT_REMOTE_UNWINDING +#endif //ASSEMBLY_INSTRUCTIONS_HPP diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyParser.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyParser.hpp new file mode 100644 index 00000000000..b34f93f50c6 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyParser.hpp @@ -0,0 +1,409 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- AssemblyParser.hpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Disassemble the prologue instructions in functions, create a profile +// of stack movements and register saves performed therein. + +#ifndef __ASSEMBLY_PARSER_HPP +#define __ASSEMBLY_PARSER_HPP + +#if defined (SUPPORT_REMOTE_UNWINDING) + +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif +#include <limits.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <map> +#include <vector> + +#include "libunwind.h" +#include "RemoteProcInfo.hpp" +#include "Registers.hpp" +#include "FileAbstraction.hpp" +#include "AddressSpace.hpp" +#include "RemoteUnwindProfile.h" + +namespace lldb_private +{ + +// Analyze the instructions in an x86_64/i386 function prologue, fill out an RemoteUnwindProfile. + +class AssemblyParse_x86 { +public: + AssemblyParse_x86 (RemoteProcInfo& procinfo, unw_accessors_t *acc, unw_addr_space_t as, void *arg) : fArg(arg), fAccessors(acc), fAs(as), fRemoteProcInfo(procinfo) { + fRegisterMap = fRemoteProcInfo.getRegisterMap(); + if (fRemoteProcInfo.getTargetArch() == UNW_TARGET_X86_64) { + fStackPointerRegnum = UNW_X86_64_RSP; + fFramePointerRegnum = UNW_X86_64_RBP; + fWordSize = 8; + } else { + fStackPointerRegnum = UNW_X86_ESP; + fFramePointerRegnum = UNW_X86_EBP; + fWordSize = 4; + } + } + + uint32_t extract_4_LE (uint8_t *b) { + uint32_t v = 0; + for (int i = 3; i >= 0; i--) + v = (v << 8) | b[i]; + return v; + } + + bool push_rbp_pattern_p (); + bool push_0_pattern_p (); + bool mov_rsp_rbp_pattern_p (); + bool sub_rsp_pattern_p (int *amount); + bool push_reg_p (int *regno); + bool mov_reg_to_local_stack_frame_p (int *regno, int *rbp_offset); + bool ret_pattern_p (); + bool profileFunction (uint64_t start, uint64_t end, RemoteUnwindProfile& profile); + +private: + + void *fArg; + uint8_t* fCurInsnByteBuf; + int fCurInsnSize; + RemoteProcInfo& fRemoteProcInfo; + RemoteRegisterMap *fRegisterMap; + unw_accessors_t *fAccessors; + unw_addr_space_t fAs; + int fWordSize; + int fStackPointerRegnum; + int fFramePointerRegnum; +}; + +// Macro to detect if this is a REX mode prefix byte. +#define REX_W_PREFIX_P(opcode) (((opcode) & (~0x5)) == 0x48) + +// The high bit which should be added to the source register number (the "R" bit) +#define REX_W_SRCREG(opcode) (((opcode) & 0x4) >> 2) + +// The high bit which should be added to the destination register number (the "B" bit) +#define REX_W_DSTREG(opcode) ((opcode) & 0x1) + +// pushq %rbp [0x55] +bool AssemblyParse_x86::push_rbp_pattern_p () { + uint8_t *p = fCurInsnByteBuf; + if (*p == 0x55) + return true; + return false; +} + +// pushq $0 ; the first instruction in start() [0x6a 0x00] +bool AssemblyParse_x86::push_0_pattern_p () +{ + uint8_t *p = fCurInsnByteBuf; + if (*p == 0x6a && *(p + 1) == 0x0) + return true; + return false; +} + +// movq %rsp, %rbp [0x48 0x8b 0xec] or [0x48 0x89 0xe5] +// movl %esp, %ebp [0x8b 0xec] or [0x89 0xe5] +bool AssemblyParse_x86::mov_rsp_rbp_pattern_p () { + uint8_t *p = fCurInsnByteBuf; + if (fWordSize == 8 && *p == 0x48) + p++; + if (*(p) == 0x8b && *(p + 1) == 0xec) + return true; + if (*(p) == 0x89 && *(p + 1) == 0xe5) + return true; + return false; +} + +// subq $0x20, %rsp +bool AssemblyParse_x86::sub_rsp_pattern_p (int *amount) { + uint8_t *p = fCurInsnByteBuf; + if (fWordSize == 8 && *p == 0x48) + p++; + // 8-bit immediate operand + if (*p == 0x83 && *(p + 1) == 0xec) { + *amount = (int8_t) *(p + 2); + return true; + } + // 32-bit immediate operand + if (*p == 0x81 && *(p + 1) == 0xec) { + *amount = (int32_t) extract_4_LE (p + 2); + return true; + } + // Not handled: [0x83 0xc4] for imm8 with neg values + // [0x81 0xc4] for imm32 with neg values + return false; +} + +// pushq %rbx +// pushl $ebx +bool AssemblyParse_x86::push_reg_p (int *regno) { + uint8_t *p = fCurInsnByteBuf; + int regno_prefix_bit = 0; + // If we have a rex prefix byte, check to see if a B bit is set + if (fWordSize == 8 && *p == 0x41) { + regno_prefix_bit = 1 << 3; + p++; + } + if (*p >= 0x50 && *p <= 0x57) { + int r = (*p - 0x50) | regno_prefix_bit; + if (fRegisterMap->machine_regno_to_unwind_regno (r, *regno) == true) { + return true; + } + } + return false; +} + +// Look for an instruction sequence storing a nonvolatile register +// on to the stack frame. + +// movq %rax, -0x10(%rbp) [0x48 0x89 0x45 0xf0] +// movl %eax, -0xc(%ebp) [0x89 0x45 0xf4] +bool AssemblyParse_x86::mov_reg_to_local_stack_frame_p (int *regno, int *rbp_offset) { + uint8_t *p = fCurInsnByteBuf; + int src_reg_prefix_bit = 0; + int target_reg_prefix_bit = 0; + + if (fWordSize == 8 && REX_W_PREFIX_P (*p)) { + src_reg_prefix_bit = REX_W_SRCREG (*p) << 3; + target_reg_prefix_bit = REX_W_DSTREG (*p) << 3; + if (target_reg_prefix_bit == 1) { + // rbp/ebp don't need a prefix bit - we know this isn't the + // reg we care about. + return false; + } + p++; + } + + if (*p == 0x89) { + /* Mask off the 3-5 bits which indicate the destination register + if this is a ModR/M byte. */ + int opcode_destreg_masked_out = *(p + 1) & (~0x38); + + /* Is this a ModR/M byte with Mod bits 01 and R/M bits 101 + and three bits between them, e.g. 01nnn101 + We're looking for a destination of ebp-disp8 or ebp-disp32. */ + int immsize; + if (opcode_destreg_masked_out == 0x45) + immsize = 2; + else if (opcode_destreg_masked_out == 0x85) + immsize = 4; + else + return false; + + int offset = 0; + if (immsize == 2) + offset = (int8_t) *(p + 2); + if (immsize == 4) + offset = (uint32_t) extract_4_LE (p + 2); + if (offset > 0) + return false; + + int savedreg = ((*(p + 1) >> 3) & 0x7) | src_reg_prefix_bit; + if (fRegisterMap->machine_regno_to_unwind_regno (savedreg, *regno) == true) { + *rbp_offset = offset > 0 ? offset : -offset; + return true; + } + } + return false; +} + +// ret [0xc9] or [0xc2 imm8] or [0xca imm8] +bool AssemblyParse_x86::ret_pattern_p () { + uint8_t *p = fCurInsnByteBuf; + if (*p == 0xc9 || *p == 0xc2 || *p == 0xca || *p == 0xc3) + return true; + return false; +} + +bool AssemblyParse_x86::profileFunction (uint64_t start, uint64_t end, RemoteUnwindProfile& profile) { + if (start == -1 || end == 0) + return false; + + profile.fStart = start; + profile.fEnd = end; + profile.fRegSizes[RemoteUnwindProfile::kGeneralPurposeRegister] = fWordSize; + profile.fRegSizes[RemoteUnwindProfile::kFloatingPointRegister] = 8; + profile.fRegSizes[RemoteUnwindProfile::kVectorRegister] = 16; + + // On function entry, the CFA is rsp+fWordSize + + RemoteUnwindProfile::CFALocation initial_cfaloc; + initial_cfaloc.regno = fStackPointerRegnum; + initial_cfaloc.offset = fWordSize; + profile.cfa[start] = initial_cfaloc; + + // The return address is at CFA - fWordSize + // CFA doesn't change value during the lifetime of the function (hence "C") + // so the returnAddress is the same for the duration of the function. + + profile.returnAddress.regno = 0; + profile.returnAddress.location = RemoteUnwindProfile::kRegisterOffsetFromCFA; + profile.returnAddress.value = -fWordSize; + profile.returnAddress.adj = 0; + profile.returnAddress.type = RemoteUnwindProfile::kGeneralPurposeRegister; + + // The caller's rsp has the same value as the CFA at all points during + // this function's lifetime. + + RemoteUnwindProfile::SavedReg rsp_loc; + rsp_loc.regno = fStackPointerRegnum; + rsp_loc.location = RemoteUnwindProfile::kRegisterIsCFA; + rsp_loc.value = 0; + rsp_loc.adj = 0; + rsp_loc.type = RemoteUnwindProfile::kGeneralPurposeRegister; + profile.saved_registers[start].push_back(rsp_loc); + profile.fRegistersSaved[fStackPointerRegnum] = 1; + + int non_prologue_insn_count = 0; + int insn_count = 0; + uint64_t cur_addr = start; + uint64_t first_insn_past_prologue = start; + int push_rbp_seen = 0; + int current_cfa_register = fStackPointerRegnum; + int sp_adjustments = 0; + + while (cur_addr < end && non_prologue_insn_count < 10) + { + int offset, regno; + uint64_t next_addr; + insn_count++; + int is_prologue_insn = 0; + + if (fAccessors->instruction_length (fAs, cur_addr, &fCurInsnSize, fArg) != 0) { + /* An error parsing the instruction; stop scanning. */ + break; + } + fCurInsnByteBuf = (uint8_t *) malloc (fCurInsnSize); + if (fRemoteProcInfo.getBytes (cur_addr, fCurInsnSize, fCurInsnByteBuf, fArg) == 0) + return false; + next_addr = cur_addr + fCurInsnSize; + + // start () opens with a 'push $0x0' which is in the saved ip slot on the stack - + // so we know to stop backtracing here. We need to ignore this instruction. + if (push_0_pattern_p () && push_rbp_seen == 0 && insn_count == 1) + { + cur_addr = next_addr; + first_insn_past_prologue = next_addr; + continue; + } + + if (push_rbp_pattern_p () && push_rbp_seen == 0) + { + if (current_cfa_register == fStackPointerRegnum) { + sp_adjustments -= fWordSize; + RemoteUnwindProfile::CFALocation cfaloc; + cfaloc.regno = fStackPointerRegnum; + cfaloc.offset = abs (sp_adjustments - fWordSize); + profile.cfa[next_addr] = cfaloc; + } + + RemoteUnwindProfile::SavedReg sreg; + sreg.regno = fFramePointerRegnum; + sreg.location = RemoteUnwindProfile::kRegisterOffsetFromCFA; + sreg.value = sp_adjustments - fWordSize; + sreg.adj = 0; + sreg.type = RemoteUnwindProfile::kGeneralPurposeRegister; + profile.saved_registers[next_addr].push_back(sreg); + + push_rbp_seen = 1; + profile.fRegistersSaved[fFramePointerRegnum] = 1; + is_prologue_insn = 1; + goto next_iteration; + } + if (mov_rsp_rbp_pattern_p ()) { + RemoteUnwindProfile::CFALocation cfaloc; + cfaloc.regno = fFramePointerRegnum; + cfaloc.offset = abs (sp_adjustments - fWordSize); + profile.cfa[next_addr] = cfaloc; + current_cfa_register = fFramePointerRegnum; + is_prologue_insn = 1; + goto next_iteration; + } + if (ret_pattern_p ()) { + break; + } + if (sub_rsp_pattern_p (&offset)) { + sp_adjustments -= offset; + if (current_cfa_register == fStackPointerRegnum) { + RemoteUnwindProfile::CFALocation cfaloc; + cfaloc.regno = fStackPointerRegnum; + cfaloc.offset = abs (sp_adjustments - fWordSize); + profile.cfa[next_addr] = cfaloc; + } + is_prologue_insn = 1; + } + if (push_reg_p (®no)) { + sp_adjustments -= fWordSize; + if (current_cfa_register == fStackPointerRegnum) { + RemoteUnwindProfile::CFALocation cfaloc; + cfaloc.regno = fStackPointerRegnum; + cfaloc.offset = abs (sp_adjustments - fWordSize); + profile.cfa[next_addr] = cfaloc; + is_prologue_insn = 1; + } + if (fRegisterMap->nonvolatile_reg_p (regno) && profile.fRegistersSaved[regno] == 0) { + RemoteUnwindProfile::SavedReg sreg; + sreg.regno = regno; + sreg.location = RemoteUnwindProfile::kRegisterOffsetFromCFA; + sreg.value = sp_adjustments - fWordSize; + sreg.adj = 0; + sreg.type = RemoteUnwindProfile::kGeneralPurposeRegister; + profile.saved_registers[next_addr].push_back(sreg); + profile.fRegistersSaved[regno] = 1; + is_prologue_insn = 1; + } + } + if (mov_reg_to_local_stack_frame_p (®no, &offset) + && fRegisterMap->nonvolatile_reg_p (regno) + && profile.fRegistersSaved[regno] == 0) { + RemoteUnwindProfile::SavedReg sreg; + sreg.regno = regno; + sreg.location = RemoteUnwindProfile::kRegisterOffsetFromCFA; + sreg.value = offset - fWordSize; + sreg.adj = 0; + sreg.type = RemoteUnwindProfile::kGeneralPurposeRegister; + profile.saved_registers[next_addr].push_back(sreg); + profile.fRegistersSaved[regno] = 1; + is_prologue_insn = 1; + } +next_iteration: + if (is_prologue_insn) { + first_insn_past_prologue = next_addr; + non_prologue_insn_count = 0; + } + cur_addr = next_addr; + non_prologue_insn_count++; + } + profile.fFirstInsnPastPrologue = first_insn_past_prologue; + return true; +} + + + + +bool AssemblyParse (RemoteProcInfo *procinfo, unw_accessors_t *acc, unw_addr_space_t as, uint64_t start, uint64_t end, RemoteUnwindProfile &profile, void *arg) { + if (procinfo->getTargetArch() == UNW_TARGET_X86_64 || procinfo->getTargetArch() == UNW_TARGET_I386) { + AssemblyParse_x86 parser(*procinfo, acc, as, arg); + return parser.profileFunction (start, end, profile); + } else { + ABORT("Only x86_64 and i386 assembly parsing supported at this time"); + return false; + } +} + +}; // namespace lldb_private + +#endif // SUPPORT_REMOTE_UNWINDING +#endif //ASSEMBLY_PARSER_HPP diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/CompactUnwinder.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/CompactUnwinder.hpp new file mode 100644 index 00000000000..dda2308ada6 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/CompactUnwinder.hpp @@ -0,0 +1,1019 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- CompactUnwinder.hpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// +// C++ interface to lower levels of libuwind +// + +#ifndef __COMPACT_UNWINDER_HPP__ +#define __COMPACT_UNWINDER_HPP__ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include <libunwind.h> +#include <mach-o/compact_unwind_encoding.h> + +#include "AddressSpace.hpp" +#include "Registers.hpp" + + + +#define EXTRACT_BITS(value, mask) \ + ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) ) + +#define SUPPORT_OLD_BINARIES 0 + +namespace lldb_private { + + + +/// +/// CompactUnwinder_x86 uses a compact unwind info to virtually "step" (aka unwind) by +/// modifying a Registers_x86 register set +/// +template <typename A> +class CompactUnwinder_x86 +{ +public: + + static int stepWithCompactEncoding(compact_unwind_encoding_t info, uint32_t functionStart, A& addressSpace, Registers_x86& registers); + +private: + typename A::pint_t pint_t; + + static void frameUnwind(A& addressSpace, Registers_x86& registers); + static void framelessUnwind(A& addressSpace, typename A::pint_t returnAddressLocation, Registers_x86& registers); + static int stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers); + static int stepWithCompactEncodingFrameless(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers, bool indirectStackSize); +#if SUPPORT_OLD_BINARIES + static int stepWithCompactEncodingCompat(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers); +#endif +}; + + + +template <typename A> +int CompactUnwinder_x86<A>::stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers) +{ + //fprintf(stderr, "stepWithCompactEncoding(0x%08X)\n", compactEncoding); + switch ( compactEncoding & UNWIND_X86_MODE_MASK ) { +#if SUPPORT_OLD_BINARIES + case UNWIND_X86_MODE_COMPATIBILITY: + return stepWithCompactEncodingCompat(compactEncoding, functionStart, addressSpace, registers); +#endif + case UNWIND_X86_MODE_EBP_FRAME: + return stepWithCompactEncodingEBPFrame(compactEncoding, functionStart, addressSpace, registers); + case UNWIND_X86_MODE_STACK_IMMD: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, addressSpace, registers, false); + case UNWIND_X86_MODE_STACK_IND: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, addressSpace, registers, true); + } + ABORT("invalid compact unwind encoding"); +} + + +template <typename A> +int CompactUnwinder_x86<A>::stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, + A& addressSpace, Registers_x86& registers) +{ + uint32_t savedRegistersOffset = EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_OFFSET); + uint32_t savedRegistersLocations = EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_REGISTERS); + + uint64_t savedRegisters = registers.getEBP() - 4*savedRegistersOffset; + for (int i=0; i < 5; ++i) { + switch (savedRegistersLocations & 0x7) { + case UNWIND_X86_REG_NONE: + // no register saved in this slot + break; + case UNWIND_X86_REG_EBX: + registers.setEBX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ECX: + registers.setECX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDX: + registers.setEDX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDI: + registers.setEDI(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ESI: + registers.setESI(addressSpace.get32(savedRegisters)); + break; + default: + DEBUG_MESSAGE("bad register for EBP frame, encoding=%08X for function starting at 0x%X\n", compactEncoding, functionStart); + ABORT("invalid compact unwind encoding"); + } + savedRegisters += 4; + savedRegistersLocations = (savedRegistersLocations >> 3); + } + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; +} + + +template <typename A> +int CompactUnwinder_x86<A>::stepWithCompactEncodingFrameless(compact_unwind_encoding_t encoding, uint32_t functionStart, + A& addressSpace, Registers_x86& registers, bool indirectStackSize) +{ + uint32_t stackSizeEncoded = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); + uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST); + uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); + uint32_t stackSize = stackSizeEncoded*4; + if ( indirectStackSize ) { + // stack size is encoded in subl $xxx,%esp instruction + uint32_t subl = addressSpace.get32(functionStart+stackSizeEncoded); + stackSize = subl + 4*stackAdjust; + } + // decompress permutation + int permunreg[6]; + switch ( regCount ) { + case 6: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation/60; + permutation -= (permunreg[0]*60); + permunreg[1] = permutation/12; + permutation -= (permunreg[1]*12); + permunreg[2] = permutation/3; + permutation -= (permunreg[2]*3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation/20; + permutation -= (permunreg[0]*20); + permunreg[1] = permutation/4; + permutation -= (permunreg[1]*4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation/5; + permutation -= (permunreg[0]*5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + // re-number registers back to standard numbers + int registersSaved[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (uint32_t i=0; i < regCount; ++i) { + int renum = 0; + for (int u=1; u < 7; ++u) { + if ( !used[u] ) { + if ( renum == permunreg[i] ) { + registersSaved[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + uint64_t savedRegisters = registers.getSP() + stackSize - 4 - 4*regCount; + for (uint32_t i=0; i < regCount; ++i) { + switch ( registersSaved[i] ) { + case UNWIND_X86_REG_EBX: + registers.setEBX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ECX: + registers.setECX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDX: + registers.setEDX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDI: + registers.setEDI(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ESI: + registers.setESI(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EBP: + registers.setEBP(addressSpace.get32(savedRegisters)); + break; + default: + DEBUG_MESSAGE("bad register for frameless, encoding=%08X for function starting at 0x%X\n", encoding, functionStart); + ABORT("invalid compact unwind encoding"); + } + savedRegisters += 4; + } + framelessUnwind(addressSpace, savedRegisters, registers); + return UNW_STEP_SUCCESS; +} + + +#if SUPPORT_OLD_BINARIES +template <typename A> +int CompactUnwinder_x86<A>::stepWithCompactEncodingCompat(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers) +{ + //fprintf(stderr, "stepWithCompactEncoding(0x%08X)\n", compactEncoding); + typename A::pint_t savedRegisters; + uint32_t stackValue = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_SIZE); + uint32_t stackSize; + uint32_t stackAdjust; + switch (compactEncoding & UNWIND_X86_CASE_MASK ) { + case UNWIND_X86_UNWIND_INFO_UNSPECIFIED: + return UNW_ENOINFO; + + case UNWIND_X86_EBP_FRAME_NO_REGS: + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_EBP_FRAME_EBX: + savedRegisters = registers.getEBP() - 4; + registers.setEBX(addressSpace.get32(savedRegisters)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_EBP_FRAME_ESI: + savedRegisters = registers.getEBP() - 4; + registers.setESI(addressSpace.get32(savedRegisters)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_EBP_FRAME_EDI: + savedRegisters = registers.getEBP() - 4; + registers.setEDI(addressSpace.get32(savedRegisters)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_EBP_FRAME_EBX_ESI: + savedRegisters = registers.getEBP() - 8; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setESI(addressSpace.get32(savedRegisters+4)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_EBP_FRAME_ESI_EDI: + savedRegisters = registers.getEBP() - 8; + registers.setESI(addressSpace.get32(savedRegisters)); + registers.setEDI(addressSpace.get32(savedRegisters+4)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_EBP_FRAME_EBX_ESI_EDI: + savedRegisters = registers.getEBP() - 12; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setESI(addressSpace.get32(savedRegisters+4)); + registers.setEDI(addressSpace.get32(savedRegisters+8)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_EBP_FRAME_EBX_EDI: + savedRegisters = registers.getEBP() - 8; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setEDI(addressSpace.get32(savedRegisters+4)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_NO_REGS: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*0; + framelessUnwind(addressSpace, savedRegisters+4*0, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_EBX: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*1; + registers.setEBX(addressSpace.get32(savedRegisters)); + framelessUnwind(addressSpace, savedRegisters+4*1, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_ESI: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*1; + registers.setESI(addressSpace.get32(savedRegisters)); + framelessUnwind(addressSpace, savedRegisters+4*1, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_EDI: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*1; + registers.setEDI(addressSpace.get32(savedRegisters)); + framelessUnwind(addressSpace, savedRegisters+4*1, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_EBX_ESI: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*2; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setESI(addressSpace.get32(savedRegisters+4)); + framelessUnwind(addressSpace, savedRegisters+4*2, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_ESI_EDI: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*2; + registers.setESI(addressSpace.get32(savedRegisters)); + registers.setEDI(addressSpace.get32(savedRegisters+4)); + framelessUnwind(addressSpace, savedRegisters+4*2, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_ESI_EDI_EBP: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*3; + registers.setESI(addressSpace.get32(savedRegisters)); + registers.setEDI(addressSpace.get32(savedRegisters+4)); + registers.setEBP(addressSpace.get32(savedRegisters+8)); + framelessUnwind(addressSpace, savedRegisters+4*3, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_EBX_ESI_EDI: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*3; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setESI(addressSpace.get32(savedRegisters+4)); + registers.setEDI(addressSpace.get32(savedRegisters+8)); + framelessUnwind(addressSpace, savedRegisters+4*3, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IMM_STK_EBX_ESI_EDI_EBP: + stackSize = stackValue * 4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*4; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setESI(addressSpace.get32(savedRegisters+4)); + registers.setEDI(addressSpace.get32(savedRegisters+8)); + registers.setEBP(addressSpace.get32(savedRegisters+12)); + framelessUnwind(addressSpace, savedRegisters+4*4, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IND_STK_NO_REGS: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*0; + framelessUnwind(addressSpace, savedRegisters+4*0, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IND_STK_EBX: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*1; + registers.setEBX(addressSpace.get32(savedRegisters)); + framelessUnwind(addressSpace, savedRegisters+4*1, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IND_STK_ESI: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*1; + registers.setESI(addressSpace.get32(savedRegisters)); + framelessUnwind(addressSpace, savedRegisters+4*1, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IND_STK_EDI: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*1; + registers.setEDI(addressSpace.get32(savedRegisters)); + return UNW_STEP_SUCCESS; + framelessUnwind(addressSpace, savedRegisters+4*1, registers); + + case UNWIND_X86_IND_STK_EBX_ESI: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*2; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setESI(addressSpace.get32(savedRegisters+4)); + framelessUnwind(addressSpace, savedRegisters+4*2, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IND_STK_ESI_EDI: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*2; + registers.setESI(addressSpace.get32(savedRegisters)); + registers.setEDI(addressSpace.get32(savedRegisters+4)); + framelessUnwind(addressSpace, savedRegisters+4*2, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IND_STK_ESI_EDI_EBP: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*3; + registers.setESI(addressSpace.get32(savedRegisters)); + registers.setEDI(addressSpace.get32(savedRegisters+4)); + registers.setEBP(addressSpace.get32(savedRegisters+8)); + framelessUnwind(addressSpace, savedRegisters+4*3, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IND_STK_EBX_ESI_EDI: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*3; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setESI(addressSpace.get32(savedRegisters+4)); + registers.setEDI(addressSpace.get32(savedRegisters+8)); + framelessUnwind(addressSpace, savedRegisters+4*3, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_IND_STK_EBX_ESI_EDI_EBP: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); + stackSize += stackAdjust*4; + savedRegisters = registers.getSP() + stackSize - 4 - 4*4; + registers.setEBX(addressSpace.get32(savedRegisters)); + registers.setESI(addressSpace.get32(savedRegisters+4)); + registers.setEDI(addressSpace.get32(savedRegisters+8)); + registers.setEBP(addressSpace.get32(savedRegisters+12)); + framelessUnwind(addressSpace, savedRegisters+4*4, registers); + return UNW_STEP_SUCCESS; + + default: + DEBUG_MESSAGE("unknown compact unwind encoding %08X for function starting at 0x%X\n", + compactEncoding & UNWIND_X86_CASE_MASK, functionStart); + ABORT("unknown compact unwind encoding"); + } + return UNW_EINVAL; +} +#endif // SUPPORT_OLD_BINARIES + + + +template <typename A> +void CompactUnwinder_x86<A>::frameUnwind(A& addressSpace, Registers_x86& registers) +{ + typename A::pint_t bp = registers.getEBP(); + // ebp points to old ebp + registers.setEBP(addressSpace.get32(bp)); + // old esp is ebp less saved ebp and return address + registers.setSP(bp+8); + // pop return address into eip + registers.setIP(addressSpace.get32(bp+4)); +} + +template <typename A> +void CompactUnwinder_x86<A>::framelessUnwind(A& addressSpace, typename A::pint_t returnAddressLocation, Registers_x86& registers) +{ + // return address is on stack after last saved register + registers.setIP(addressSpace.get32(returnAddressLocation)); + // old esp is before return address + registers.setSP(returnAddressLocation+4); +} + + + + + +/// +/// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka unwind) by +/// modifying a Registers_x86_64 register set +/// +template <typename A> +class CompactUnwinder_x86_64 +{ +public: + + static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers); + +private: + typename A::pint_t pint_t; + + static void frameUnwind(A& addressSpace, Registers_x86_64& registers); + static void framelessUnwind(A& addressSpace, uint64_t returnAddressLocation, Registers_x86_64& registers); + static int stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers); + static int stepWithCompactEncodingFrameless(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers, bool indirectStackSize); +#if SUPPORT_OLD_BINARIES + static int stepWithCompactEncodingCompat(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers); +#endif +}; + + +template <typename A> +int CompactUnwinder_x86_64<A>::stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers) +{ + //fprintf(stderr, "stepWithCompactEncoding(0x%08X)\n", compactEncoding); + switch ( compactEncoding & UNWIND_X86_64_MODE_MASK ) { +#if SUPPORT_OLD_BINARIES + case UNWIND_X86_64_MODE_COMPATIBILITY: + return stepWithCompactEncodingCompat(compactEncoding, functionStart, addressSpace, registers); +#endif + case UNWIND_X86_64_MODE_RBP_FRAME: + return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart, addressSpace, registers); + case UNWIND_X86_64_MODE_STACK_IMMD: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, addressSpace, registers, false); + case UNWIND_X86_64_MODE_STACK_IND: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, addressSpace, registers, true); + } + ABORT("invalid compact unwind encoding"); +} + + +template <typename A> +int CompactUnwinder_x86_64<A>::stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A& addressSpace, Registers_x86_64& registers) +{ + uint32_t savedRegistersOffset = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET); + uint32_t savedRegistersLocations = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); + + uint64_t savedRegisters = registers.getRBP() - 8*savedRegistersOffset; + for (int i=0; i < 5; ++i) { + int readerr = 0; + switch (savedRegistersLocations & 0x7) { + case UNWIND_X86_64_REG_NONE: + // no register saved in this slot + break; + case UNWIND_X86_64_REG_RBX: + registers.setRBX(addressSpace.get64(savedRegisters, readerr)); + break; + case UNWIND_X86_64_REG_R12: + registers.setR12(addressSpace.get64(savedRegisters, readerr)); + break; + case UNWIND_X86_64_REG_R13: + registers.setR13(addressSpace.get64(savedRegisters, readerr)); + break; + case UNWIND_X86_64_REG_R14: + registers.setR14(addressSpace.get64(savedRegisters, readerr)); + break; + case UNWIND_X86_64_REG_R15: + registers.setR15(addressSpace.get64(savedRegisters, readerr)); + break; + default: + DEBUG_MESSAGE("bad register for RBP frame, encoding=%08X for function starting at 0x%llX\n", compactEncoding, functionStart); + ABORT("invalid compact unwind encoding"); + } + // Error reading memory while doing a remote unwind? + if (readerr) + return UNW_STEP_END; + + savedRegisters += 8; + savedRegistersLocations = (savedRegistersLocations >> 3); + } + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; +} + + +template <typename A> +int CompactUnwinder_x86_64<A>::stepWithCompactEncodingFrameless(compact_unwind_encoding_t encoding, uint64_t functionStart, + A& addressSpace, Registers_x86_64& registers, bool indirectStackSize) +{ + uint32_t stackSizeEncoded = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); + uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); + uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); + uint32_t stackSize = stackSizeEncoded*8; + if ( indirectStackSize ) { + // stack size is encoded in subl $xxx,%esp instruction + uint32_t subl = addressSpace.get32(functionStart+stackSizeEncoded); + stackSize = subl + 8*stackAdjust; + } + // decompress permutation + int permunreg[6]; + switch ( regCount ) { + case 6: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation/60; + permutation -= (permunreg[0]*60); + permunreg[1] = permutation/12; + permutation -= (permunreg[1]*12); + permunreg[2] = permutation/3; + permutation -= (permunreg[2]*3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation/20; + permutation -= (permunreg[0]*20); + permunreg[1] = permutation/4; + permutation -= (permunreg[1]*4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation/5; + permutation -= (permunreg[0]*5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + // re-number registers back to standard numbers + int registersSaved[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (uint32_t i=0; i < regCount; ++i) { + int renum = 0; + for (int u=1; u < 7; ++u) { + if ( !used[u] ) { + if ( renum == permunreg[i] ) { + registersSaved[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8*regCount; + for (uint32_t i=0; i < regCount; ++i) { + switch ( registersSaved[i] ) { + case UNWIND_X86_64_REG_RBX: + registers.setRBX(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R12: + registers.setR12(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R13: + registers.setR13(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R14: + registers.setR14(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R15: + registers.setR15(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_RBP: + registers.setRBP(addressSpace.get64(savedRegisters)); + break; + default: + DEBUG_MESSAGE("bad register for frameless, encoding=%08X for function starting at 0x%llX\n", encoding, functionStart); + ABORT("invalid compact unwind encoding"); + } + savedRegisters += 8; + } + framelessUnwind(addressSpace, savedRegisters, registers); + return UNW_STEP_SUCCESS; +} + +#if SUPPORT_OLD_BINARIES +template <typename A> +int CompactUnwinder_x86_64<A>::stepWithCompactEncodingCompat(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers) +{ + uint64_t savedRegisters; + uint32_t stackValue = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_SIZE); + uint64_t stackSize; + uint32_t stackAdjust; + + switch (compactEncoding & UNWIND_X86_64_CASE_MASK ) { + case UNWIND_X86_64_UNWIND_INFO_UNSPECIFIED: + return UNW_ENOINFO; + + case UNWIND_X86_64_RBP_FRAME_NO_REGS: + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_RBP_FRAME_RBX: + savedRegisters = registers.getRBP() - 8*1; + registers.setRBX(addressSpace.get64(savedRegisters)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_RBP_FRAME_RBX_R12: + savedRegisters = registers.getRBP() - 8*2; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_RBP_FRAME_RBX_R12_R13: + savedRegisters = registers.getRBP() - 8*3; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_RBP_FRAME_RBX_R12_R13_R14: + savedRegisters = registers.getRBP() - 8*4; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + registers.setR14(addressSpace.get64(savedRegisters+24)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_RBP_FRAME_RBX_R12_R13_R14_R15: + savedRegisters = registers.getRBP() - 8*5; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + registers.setR14(addressSpace.get64(savedRegisters+24)); + registers.setR15(addressSpace.get64(savedRegisters+32)); + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_NO_REGS: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*0; + framelessUnwind(addressSpace, savedRegisters+8*0, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*1; + registers.setRBX(addressSpace.get64(savedRegisters)); + framelessUnwind(addressSpace, savedRegisters+8*1, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_R12: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*2; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + framelessUnwind(addressSpace, savedRegisters+8*2, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_RBP: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*2; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + framelessUnwind(addressSpace, savedRegisters+8*2, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_R12_R13: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*3; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + framelessUnwind(addressSpace, savedRegisters+8*3, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_R12_R13_R14: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*4; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + registers.setR14(addressSpace.get64(savedRegisters+24)); + framelessUnwind(addressSpace, savedRegisters+8*4, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_R12_R13_R14_R15: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*5; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + registers.setR14(addressSpace.get64(savedRegisters+24)); + registers.setR15(addressSpace.get64(savedRegisters+32)); + framelessUnwind(addressSpace, savedRegisters+8*5, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_RBP_R12_R13_R14_R15: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*6; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + registers.setR12(addressSpace.get64(savedRegisters+16)); + registers.setR13(addressSpace.get64(savedRegisters+24)); + registers.setR14(addressSpace.get64(savedRegisters+32)); + registers.setR15(addressSpace.get64(savedRegisters+40)); + framelessUnwind(addressSpace, savedRegisters+8*6, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_RBP_R12: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*3; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + registers.setR12(addressSpace.get64(savedRegisters+16)); + framelessUnwind(addressSpace, savedRegisters+8*3, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_RBP_R12_R13: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*4; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + registers.setR12(addressSpace.get64(savedRegisters+16)); + registers.setR13(addressSpace.get64(savedRegisters+24)); + framelessUnwind(addressSpace, savedRegisters+8*4, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IMM_STK_RBX_RBP_R12_R13_R14: + stackSize = stackValue * 8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*5; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + registers.setR12(addressSpace.get64(savedRegisters+16)); + registers.setR13(addressSpace.get64(savedRegisters+24)); + registers.setR14(addressSpace.get64(savedRegisters+32)); + framelessUnwind(addressSpace, savedRegisters+8*5, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_NO_REGS: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*0; + framelessUnwind(addressSpace, savedRegisters+8*0, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*1; + registers.setRBX(addressSpace.get64(savedRegisters)); + framelessUnwind(addressSpace, savedRegisters+8*1, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_R12: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*2; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + framelessUnwind(addressSpace, savedRegisters+8*2, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_RBP: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*2; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + framelessUnwind(addressSpace, savedRegisters+8*2, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_R12_R13: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*3; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + framelessUnwind(addressSpace, savedRegisters+8*3, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_R12_R13_R14: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*4; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + registers.setR14(addressSpace.get64(savedRegisters+24)); + framelessUnwind(addressSpace, savedRegisters+8*4, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_R12_R13_R14_R15: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*5; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setR12(addressSpace.get64(savedRegisters+8)); + registers.setR13(addressSpace.get64(savedRegisters+16)); + registers.setR14(addressSpace.get64(savedRegisters+24)); + registers.setR15(addressSpace.get64(savedRegisters+32)); + framelessUnwind(addressSpace, savedRegisters+8*5, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_RBP_R12_R13_R14_R15: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*6; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + registers.setR12(addressSpace.get64(savedRegisters+16)); + registers.setR13(addressSpace.get64(savedRegisters+24)); + registers.setR14(addressSpace.get64(savedRegisters+32)); + registers.setR15(addressSpace.get64(savedRegisters+40)); + framelessUnwind(addressSpace, savedRegisters+8*6, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_RBP_R12: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*3; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + registers.setR12(addressSpace.get64(savedRegisters+16)); + framelessUnwind(addressSpace, savedRegisters+8*3, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_RBP_R12_R13: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*4; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + registers.setR12(addressSpace.get64(savedRegisters+16)); + registers.setR13(addressSpace.get64(savedRegisters+24)); + framelessUnwind(addressSpace, savedRegisters+8*4, registers); + return UNW_STEP_SUCCESS; + + case UNWIND_X86_64_IND_STK_RBX_RBP_R12_R13_R14: + stackSize = addressSpace.get32(functionStart+stackValue); + stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); + stackSize += stackAdjust*8; + savedRegisters = registers.getSP() + stackSize - 8 - 8*5; + registers.setRBX(addressSpace.get64(savedRegisters)); + registers.setRBP(addressSpace.get64(savedRegisters+8)); + registers.setR12(addressSpace.get64(savedRegisters+16)); + registers.setR13(addressSpace.get64(savedRegisters+24)); + registers.setR14(addressSpace.get64(savedRegisters+32)); + framelessUnwind(addressSpace, savedRegisters+8*5, registers); + return UNW_STEP_SUCCESS; + + default: + DEBUG_MESSAGE("unknown compact unwind encoding %08X for function starting at 0x%llX\n", + compactEncoding & UNWIND_X86_64_CASE_MASK, functionStart); + ABORT("unknown compact unwind encoding"); + } + return UNW_EINVAL; +} +#endif // SUPPORT_OLD_BINARIES + + +template <typename A> +void CompactUnwinder_x86_64<A>::frameUnwind(A& addressSpace, Registers_x86_64& registers) +{ + uint64_t rbp = registers.getRBP(); + // ebp points to old ebp + registers.setRBP(addressSpace.get64(rbp)); + // old esp is ebp less saved ebp and return address + registers.setSP(rbp+16); + // pop return address into eip + registers.setIP(addressSpace.get64(rbp+8)); +} + +template <typename A> +void CompactUnwinder_x86_64<A>::framelessUnwind(A& addressSpace, uint64_t returnAddressLocation, Registers_x86_64& registers) +{ + // return address is on stack after last saved register + registers.setIP(addressSpace.get64(returnAddressLocation)); + // old esp is before return address + registers.setSP(returnAddressLocation+8); +} + + +}; // namespace lldb_private + + + +#endif // __COMPACT_UNWINDER_HPP__ + + + + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfInstructions.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfInstructions.hpp new file mode 100644 index 00000000000..589c30b50b9 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfInstructions.hpp @@ -0,0 +1,1686 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- DwarfInstructions.hpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// +// processor specific parsing of dwarf unwind instructions +// + +#ifndef __DWARF_INSTRUCTIONS_HPP__ +#define __DWARF_INSTRUCTIONS_HPP__ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include <algorithm> +#include <vector> + +#include <libunwind.h> +#include <mach-o/compact_unwind_encoding.h> + +#include "dwarf2.h" +#include "AddressSpace.hpp" +#include "Registers.hpp" +#include "DwarfParser.hpp" +#include "InternalMacros.h" +//#include "CompactUnwinder.hpp" + +#define EXTRACT_BITS(value, mask) \ + ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) ) + +#define CFI_INVALID_ADDRESS ((pint_t)(-1)) + +namespace lldb_private { + +/// +/// Used by linker when parsing __eh_frame section +/// +template <typename A> +struct CFI_Reference { + typedef typename A::pint_t pint_t; + uint8_t encodingOfTargetAddress; + uint32_t offsetInCFI; + pint_t targetAddress; +}; +template <typename A> +struct CFI_Atom_Info { + typedef typename A::pint_t pint_t; + pint_t address; + uint32_t size; + bool isCIE; + union { + struct { + CFI_Reference<A> function; + CFI_Reference<A> cie; + CFI_Reference<A> lsda; + uint32_t compactUnwindInfo; + } fdeInfo; + struct { + CFI_Reference<A> personality; + } cieInfo; + } u; +}; + +typedef void (*WarnFunc)(void* ref, uint64_t funcAddr, const char* msg); + +/// +/// DwarfInstructions maps abtract dwarf unwind instructions to a particular architecture +/// +template <typename A, typename R> +class DwarfInstructions +{ +public: + typedef typename A::pint_t pint_t; + typedef typename A::sint_t sint_t; + + static const char* parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, + CFI_Atom_Info<A>* infos, uint32_t infosCount, void* ref, WarnFunc warn); + + + static compact_unwind_encoding_t createCompactEncodingFromFDE(A& addressSpace, pint_t fdeStart, + pint_t* lsda, pint_t* personality, + char warningBuffer[1024]); + + static int stepWithDwarf(A& addressSpace, pint_t pc, pint_t fdeStart, R& registers); + +private: + + enum { + DW_X86_64_RET_ADDR = 16 + }; + + enum { + DW_X86_RET_ADDR = 8 + }; + + static pint_t evaluateExpression(pint_t expression, A& addressSpace, const R& registers, pint_t initialStackValue); + static pint_t getSavedRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser<A>::RegisterLocation& savedReg); + static double getSavedFloatRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser<A>::RegisterLocation& savedReg); + static v128 getSavedVectorRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser<A>::RegisterLocation& savedReg); + + // x86 specific variants + static int lastRestoreReg(const Registers_x86&); + static bool isReturnAddressRegister(int regNum, const Registers_x86&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog, const Registers_x86&); + + static uint32_t getEBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_x86&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_x86&, const typename CFI_Parser<A>::PrologInfo& prolog, + char warningBuffer[1024]); + + // x86_64 specific variants + static int lastRestoreReg(const Registers_x86_64&); + static bool isReturnAddressRegister(int regNum, const Registers_x86_64&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog, const Registers_x86_64&); + + static uint32_t getRBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_x86_64&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_x86_64&, const typename CFI_Parser<A>::PrologInfo& prolog, + char warningBuffer[1024]); + + // ppc specific variants + static int lastRestoreReg(const Registers_ppc&); + static bool isReturnAddressRegister(int regNum, const Registers_ppc&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog, const Registers_ppc&); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_ppc&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_ppc&, const typename CFI_Parser<A>::PrologInfo& prolog, + char warningBuffer[1024]); +}; + + + + +template <typename A, typename R> +const char* DwarfInstructions<A,R>::parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, + CFI_Atom_Info<A>* infos, uint32_t infosCount, void* ref, WarnFunc warn) +{ + typename CFI_Parser<A>::CIE_Info cieInfo; + CFI_Atom_Info<A>* entry = infos; + CFI_Atom_Info<A>* end = &infos[infosCount]; + const pint_t ehSectionEnd = ehSectionStart + sectionLength; + for (pint_t p=ehSectionStart; p < ehSectionEnd; ) { + pint_t currentCFI = p; + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return NULL; // end marker + if ( entry >= end ) + return "too little space allocated for parseCFIs"; + pint_t nextCFI = p + cfiLength; + uint32_t id = addressSpace.get32(p); + if ( id == 0 ) { + // is CIE + const char* err = CFI_Parser<A>::parseCIE(addressSpace, currentCFI, &cieInfo); + if ( err != NULL ) + return err; + entry->address = currentCFI; + entry->size = nextCFI - currentCFI; + entry->isCIE = true; + entry->u.cieInfo.personality.targetAddress = cieInfo.personality; + entry->u.cieInfo.personality.offsetInCFI = cieInfo.personalityOffsetInCIE; + entry->u.cieInfo.personality.encodingOfTargetAddress = cieInfo.personalityEncoding; + ++entry; + } + else { + // is FDE + entry->address = currentCFI; + entry->size = nextCFI - currentCFI; + entry->isCIE = false; + entry->u.fdeInfo.function.targetAddress = CFI_INVALID_ADDRESS; + entry->u.fdeInfo.cie.targetAddress = CFI_INVALID_ADDRESS; + entry->u.fdeInfo.lsda.targetAddress = CFI_INVALID_ADDRESS; + uint32_t ciePointer = addressSpace.get32(p); + pint_t cieStart = p-ciePointer; + // validate pointer to CIE is within section + if ( (cieStart < ehSectionStart) || (cieStart > ehSectionEnd) ) + return "FDE points to CIE outside __eh_frame section"; + // optimize usual case where cie is same for all FDEs + if ( cieStart != cieInfo.cieStart ) { + const char* err = CFI_Parser<A>::parseCIE(addressSpace, cieStart, &cieInfo); + if ( err != NULL ) + return err; + } + entry->u.fdeInfo.cie.targetAddress = cieStart; + entry->u.fdeInfo.cie.offsetInCFI = p-currentCFI; + entry->u.fdeInfo.cie.encodingOfTargetAddress = DW_EH_PE_sdata4 | DW_EH_PE_pcrel; + p += 4; + // parse pc begin and range + pint_t offsetOfFunctionAddress = p-currentCFI; + pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F); + //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + // test if pc is within the function this FDE covers + entry->u.fdeInfo.function.targetAddress = pcStart; + entry->u.fdeInfo.function.offsetInCFI = offsetOfFunctionAddress; + entry->u.fdeInfo.function.encodingOfTargetAddress = cieInfo.pointerEncoding; + // check for augmentation length + if ( cieInfo.fdesHaveAugmentationData ) { + uintptr_t augLen = addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if ( cieInfo.lsdaEncoding != 0 ) { + // peek at value (without indirection). Zero means no lsda + pint_t lsdaStart = p; + if ( addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding & 0x0F) != 0 ) { + // reset pointer and re-parse lsda address + p = lsdaStart; + pint_t offsetOfLSDAAddress = p-currentCFI; + entry->u.fdeInfo.lsda.targetAddress = addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding); + entry->u.fdeInfo.lsda.offsetInCFI = offsetOfLSDAAddress; + entry->u.fdeInfo.lsda.encodingOfTargetAddress = cieInfo.lsdaEncoding; + } + } + p = endOfAug; + } + // compute compact unwind encoding + typename CFI_Parser<A>::FDE_Info fdeInfo; + fdeInfo.fdeStart = currentCFI; + fdeInfo.fdeLength = nextCFI - currentCFI; + fdeInfo.fdeInstructions = p; + fdeInfo.pcStart = pcStart; + fdeInfo.pcEnd = pcStart + pcRange; + fdeInfo.lsda = entry->u.fdeInfo.lsda.targetAddress; + typename CFI_Parser<A>::PrologInfo prolog; + R dummy; // for proper selection of architecture specific functions + if ( CFI_Parser<A>::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) { + char warningBuffer[1024]; + entry->u.fdeInfo.compactUnwindInfo = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer); + if ( fdeInfo.lsda != CFI_INVALID_ADDRESS ) + entry->u.fdeInfo.compactUnwindInfo |= UNWIND_HAS_LSDA; + if ( warningBuffer[0] != '\0' ) + warn(ref, fdeInfo.pcStart, warningBuffer); + } + else { + warn(ref, CFI_INVALID_ADDRESS, "dwarf unwind instructions could not be parsed"); + entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy); + } + ++entry; + } + p = nextCFI; + } + if ( entry != end ) + return "wrong entry count for parseCFIs"; + return NULL; // success +} + + + + +template <typename A, typename R> +compact_unwind_encoding_t DwarfInstructions<A,R>::createCompactEncodingFromFDE(A& addressSpace, pint_t fdeStart, + pint_t* lsda, pint_t* personality, + char warningBuffer[1024]) +{ + typename CFI_Parser<A>::FDE_Info fdeInfo; + typename CFI_Parser<A>::CIE_Info cieInfo; + R dummy; // for proper selection of architecture specific functions + if ( CFI_Parser<A>::decodeFDE(addressSpace, fdeStart, &fdeInfo, &cieInfo) == NULL ) { + typename CFI_Parser<A>::PrologInfo prolog; + if ( CFI_Parser<A>::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) { + *lsda = fdeInfo.lsda; + *personality = cieInfo.personality; + compact_unwind_encoding_t encoding; + encoding = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer); + if ( fdeInfo.lsda != 0 ) + encoding |= UNWIND_HAS_LSDA; + return encoding; + } + else { + strcpy(warningBuffer, "dwarf unwind instructions could not be parsed"); + return encodeToUseDwarf(dummy); + } + } + else { + strcpy(warningBuffer, "dwarf FDE could not be parsed"); + return encodeToUseDwarf(dummy); + } +} + + +template <typename A, typename R> +typename A::pint_t DwarfInstructions<A,R>::getSavedRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser<A>::RegisterLocation& savedReg) +{ + switch ( savedReg.location ) { + case CFI_Parser<A>::kRegisterInCFA: + return addressSpace.getP(cfa + savedReg.value); + + case CFI_Parser<A>::kRegisterAtExpression: + return addressSpace.getP(evaluateExpression(savedReg.value, addressSpace, registers, cfa)); + + case CFI_Parser<A>::kRegisterIsExpression: + return evaluateExpression(savedReg.value, addressSpace, registers, cfa); + + case CFI_Parser<A>::kRegisterInRegister: + return registers.getRegister(savedReg.value); + + case CFI_Parser<A>::kRegisterUnused: + case CFI_Parser<A>::kRegisterOffsetFromCFA: + // FIX ME + break; + } + ABORT("unsupported restore location for register"); +} + +template <typename A, typename R> +double DwarfInstructions<A,R>::getSavedFloatRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser<A>::RegisterLocation& savedReg) +{ + switch ( savedReg.location ) { + case CFI_Parser<A>::kRegisterInCFA: + return addressSpace.getDouble(cfa + savedReg.value); + + case CFI_Parser<A>::kRegisterAtExpression: + return addressSpace.getDouble(evaluateExpression(savedReg.value, addressSpace, registers, cfa)); + + case CFI_Parser<A>::kRegisterIsExpression: + case CFI_Parser<A>::kRegisterUnused: + case CFI_Parser<A>::kRegisterOffsetFromCFA: + case CFI_Parser<A>::kRegisterInRegister: + // FIX ME + break; + } + ABORT("unsupported restore location for float register"); +} + +template <typename A, typename R> +v128 DwarfInstructions<A,R>::getSavedVectorRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser<A>::RegisterLocation& savedReg) +{ + switch ( savedReg.location ) { + case CFI_Parser<A>::kRegisterInCFA: + return addressSpace.getVector(cfa + savedReg.value); + + case CFI_Parser<A>::kRegisterAtExpression: + return addressSpace.getVector(evaluateExpression(savedReg.value, addressSpace, registers, cfa)); + + case CFI_Parser<A>::kRegisterIsExpression: + case CFI_Parser<A>::kRegisterUnused: + case CFI_Parser<A>::kRegisterOffsetFromCFA: + case CFI_Parser<A>::kRegisterInRegister: + // FIX ME + break; + } + ABORT("unsupported restore location for vector register"); +} + + +template <typename A, typename R> +int DwarfInstructions<A,R>::stepWithDwarf(A& addressSpace, pint_t pc, pint_t fdeStart, R& registers) +{ + //fprintf(stderr, "stepWithDwarf(pc=0x%0llX, fdeStart=0x%0llX)\n", (uint64_t)pc, (uint64_t)fdeStart); + typename CFI_Parser<A>::FDE_Info fdeInfo; + typename CFI_Parser<A>::CIE_Info cieInfo; + if ( CFI_Parser<A>::decodeFDE(addressSpace, fdeStart, &fdeInfo, &cieInfo) == NULL ) { + typename CFI_Parser<A>::PrologInfo prolog; + if ( CFI_Parser<A>::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, pc, &prolog) ) { + R newRegisters = registers; + + // get pointer to cfa (architecture specific) + pint_t cfa = getCFA(addressSpace, prolog, registers); + + // restore registers that dwarf says were saved + pint_t returnAddress = 0; + for (int i=0; i <= lastRestoreReg(newRegisters); ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser<A>::kRegisterUnused ) { + if ( registers.validFloatRegister(i) ) + newRegisters.setFloatRegister(i, getSavedFloatRegister(addressSpace, registers, cfa, prolog.savedRegisters[i])); + else if ( registers.validVectorRegister(i) ) + newRegisters.setVectorRegister(i, getSavedVectorRegister(addressSpace, registers, cfa, prolog.savedRegisters[i])); + else if ( isReturnAddressRegister(i, registers) ) + returnAddress = getSavedRegister(addressSpace, registers, cfa, prolog.savedRegisters[i]); + else if ( registers.validRegister(i) ) + newRegisters.setRegister(i, getSavedRegister(addressSpace, registers, cfa, prolog.savedRegisters[i])); + else + return UNW_EBADREG; + } + } + + // by definition the CFA is the stack pointer at the call site, so restoring SP means setting it to CFA + newRegisters.setSP(cfa); + + // return address is address after call site instruction, so setting IP to that does a return + newRegisters.setIP(returnAddress); + + // do the actual step by replacing the register set with the new ones + registers = newRegisters; + + return UNW_STEP_SUCCESS; + } + } + return UNW_EBADFRAME; +} + + + +template <typename A, typename R> +typename A::pint_t DwarfInstructions<A,R>::evaluateExpression(pint_t expression, A& addressSpace, + const R& registers, pint_t initialStackValue) +{ + const bool log = false; + pint_t p = expression; + pint_t expressionEnd = expression+20; // just need something until length is read + uint64_t length = addressSpace.getULEB128(p, expressionEnd); + expressionEnd = p + length; + if (log) fprintf(stderr, "evaluateExpression(): length=%llu\n", length); + pint_t stack[100]; + pint_t* sp = stack; + *(++sp) = initialStackValue; + + while ( p < expressionEnd ) { + if (log) { + for(pint_t* t = sp; t > stack; --t) { + fprintf(stderr, "sp[] = 0x%llX\n", (uint64_t)(*t)); + } + } + uint8_t opcode = addressSpace.get8(p++); + sint_t svalue; + pint_t value; + uint32_t reg; + switch (opcode) { + case DW_OP_addr: + // push immediate address sized value + value = addressSpace.getP(p); + p += sizeof(pint_t); + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_deref: + // pop stack, dereference, push result + value = *sp--; + *(++sp) = addressSpace.getP(value); + if (log) fprintf(stderr, "dereference 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const1u: + // push immediate 1 byte value + value = addressSpace.get8(p); + p += 1; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const1s: + // push immediate 1 byte signed value + svalue = (int8_t)addressSpace.get8(p); + p += 1; + *(++sp) = svalue; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue); + break; + + case DW_OP_const2u: + // push immediate 2 byte value + value = addressSpace.get16(p); + p += 2; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const2s: + // push immediate 2 byte signed value + svalue = (int16_t)addressSpace.get16(p); + p += 2; + *(++sp) = svalue; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue); + break; + + case DW_OP_const4u: + // push immediate 4 byte value + value = addressSpace.get32(p); + p += 4; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const4s: + // push immediate 4 byte signed value + svalue = (int32_t)addressSpace.get32(p); + p += 4; + *(++sp) = svalue; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue); + break; + + case DW_OP_const8u: + // push immediate 8 byte value + value = addressSpace.get64(p); + p += 8; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const8s: + // push immediate 8 byte signed value + value = (int32_t)addressSpace.get64(p); + p += 8; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_constu: + // push immediate ULEB128 value + value = addressSpace.getULEB128(p, expressionEnd); + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_consts: + // push immediate SLEB128 value + svalue = addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = svalue; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue); + break; + + case DW_OP_dup: + // push top of stack + value = *sp; + *(++sp) = value; + if (log) fprintf(stderr, "duplicate top of stack\n"); + break; + + case DW_OP_drop: + // pop + --sp; + if (log) fprintf(stderr, "pop top of stack\n"); + break; + + case DW_OP_over: + // dup second + value = sp[-1]; + *(++sp) = value; + if (log) fprintf(stderr, "duplicate second in stack\n"); + break; + + case DW_OP_pick: + // pick from + reg = addressSpace.get8(p); + p += 1; + value = sp[-reg]; + *(++sp) = value; + if (log) fprintf(stderr, "duplicate %d in stack\n", reg); + break; + + case DW_OP_swap: + // swap top two + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = value; + if (log) fprintf(stderr, "swap top of stack\n"); + break; + + case DW_OP_rot: + // rotate top three + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = value; + if (log) fprintf(stderr, "rotate top three of stack\n"); + break; + + case DW_OP_xderef: + // pop stack, dereference, push result + value = *sp--; + *sp = *((uint64_t*)value); + if (log) fprintf(stderr, "x-dereference 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_abs: + svalue = *sp; + if ( svalue < 0 ) + *sp = -svalue; + if (log) fprintf(stderr, "abs\n"); + break; + + case DW_OP_and: + value = *sp--; + *sp &= value; + if (log) fprintf(stderr, "and\n"); + break; + + case DW_OP_div: + svalue = *sp--; + *sp = *sp / svalue; + if (log) fprintf(stderr, "div\n"); + break; + + case DW_OP_minus: + svalue = *sp--; + *sp = *sp - svalue; + if (log) fprintf(stderr, "minus\n"); + break; + + case DW_OP_mod: + svalue = *sp--; + *sp = *sp % svalue; + if (log) fprintf(stderr, "module\n"); + break; + + case DW_OP_mul: + svalue = *sp--; + *sp = *sp * svalue; + if (log) fprintf(stderr, "mul\n"); + break; + + case DW_OP_neg: + *sp = 0 - *sp; + if (log) fprintf(stderr, "neg\n"); + break; + + case DW_OP_not: + svalue = *sp; + *sp = ~svalue; + if (log) fprintf(stderr, "not\n"); + break; + + case DW_OP_or: + value = *sp--; + *sp |= value; + if (log) fprintf(stderr, "or\n"); + break; + + case DW_OP_plus: + value = *sp--; + *sp += value; + if (log) fprintf(stderr, "plus\n"); + break; + + case DW_OP_plus_uconst: + // pop stack, add uelb128 constant, push result + *sp += addressSpace.getULEB128(p, expressionEnd); + if (log) fprintf(stderr, "add constant\n"); + break; + + case DW_OP_shl: + value = *sp--; + *sp = *sp << value; + if (log) fprintf(stderr, "shift left\n"); + break; + + case DW_OP_shr: + value = *sp--; + *sp = *sp >> value; + if (log) fprintf(stderr, "shift left\n"); + break; + + case DW_OP_shra: + value = *sp--; + svalue = *sp; + *sp = svalue >> value; + if (log) fprintf(stderr, "shift left arithmetric\n"); + break; + + case DW_OP_xor: + value = *sp--; + *sp ^= value; + if (log) fprintf(stderr, "xor\n"); + break; + + case DW_OP_skip: + svalue = (int16_t)addressSpace.get16(p); + p += 2; + p += svalue; + if (log) fprintf(stderr, "skip %lld\n", (uint64_t)svalue); + break; + + case DW_OP_bra: + svalue = (int16_t)addressSpace.get16(p); + p += 2; + if ( *sp-- ) + p += svalue; + if (log) fprintf(stderr, "bra %lld\n", (uint64_t)svalue); + break; + + case DW_OP_eq: + value = *sp--; + *sp = (*sp == value); + if (log) fprintf(stderr, "eq\n"); + break; + + case DW_OP_ge: + value = *sp--; + *sp = (*sp >= value); + if (log) fprintf(stderr, "ge\n"); + break; + + case DW_OP_gt: + value = *sp--; + *sp = (*sp > value); + if (log) fprintf(stderr, "gt\n"); + break; + + case DW_OP_le: + value = *sp--; + *sp = (*sp <= value); + if (log) fprintf(stderr, "le\n"); + break; + + case DW_OP_lt: + value = *sp--; + *sp = (*sp < value); + if (log) fprintf(stderr, "lt\n"); + break; + + case DW_OP_ne: + value = *sp--; + *sp = (*sp != value); + if (log) fprintf(stderr, "ne\n"); + break; + + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + value = opcode - DW_OP_lit0; + *(++sp) = value; + if (log) fprintf(stderr, "push literal 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + reg = opcode - DW_OP_reg0; + *(++sp) = registers.getRegister(reg); + if (log) fprintf(stderr, "push reg %d\n", reg); + break; + + case DW_OP_regx: + reg = addressSpace.getULEB128(p, expressionEnd); + *(++sp) = registers.getRegister(reg); + if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue); + break; + + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + reg = opcode - DW_OP_breg0; + svalue = addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = registers.getRegister(reg) + svalue; + if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue); + break; + + case DW_OP_bregx: + reg = addressSpace.getULEB128(p, expressionEnd); + svalue = addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = registers.getRegister(reg) + svalue; + if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue); + break; + + case DW_OP_fbreg: + ABORT("DW_OP_fbreg not implemented"); + break; + + case DW_OP_piece: + ABORT("DW_OP_piece not implemented"); + break; + + case DW_OP_deref_size: + // pop stack, dereference, push result + value = *sp--; + switch ( addressSpace.get8(p++) ) { + case 1: + value = addressSpace.get8(value); + break; + case 2: + value = addressSpace.get16(value); + break; + case 4: + value = addressSpace.get32(value); + break; + case 8: + value = addressSpace.get64(value); + break; + default: + ABORT("DW_OP_deref_size with bad size"); + } + *(++sp) = value; + if (log) fprintf(stderr, "sized dereference 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_xderef_size: + case DW_OP_nop: + case DW_OP_push_object_addres: + case DW_OP_call2: + case DW_OP_call4: + case DW_OP_call_ref: + default: + ABORT("dwarf opcode not implemented"); + } + + } + if (log) fprintf(stderr, "expression evaluates to 0x%llX\n", (uint64_t)*sp); + return *sp; +} + + + +// +// x86_64 specific functions +// + +template <typename A, typename R> +int DwarfInstructions<A,R>::lastRestoreReg(const Registers_x86_64&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser<A>::kMaxRegisterNumber > (int)DW_X86_64_RET_ADDR ); + return DW_X86_64_RET_ADDR; +} + +template <typename A, typename R> +bool DwarfInstructions<A,R>::isReturnAddressRegister(int regNum, const Registers_x86_64&) +{ + return (regNum == DW_X86_64_RET_ADDR); +} + +template <typename A, typename R> +typename A::pint_t DwarfInstructions<A,R>::getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog, + const Registers_x86_64& registers) +{ + if ( prolog.cfaRegister != 0 ) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else if ( prolog.cfaExpression != 0 ) + return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0); + else + ABORT("getCFA(): unknown location for x86_64 cfa"); +} + + + +template <typename A, typename R> +compact_unwind_encoding_t DwarfInstructions<A,R>::encodeToUseDwarf(const Registers_x86_64&) +{ + return UNWIND_X86_64_MODE_DWARF; +} + +template <typename A, typename R> +compact_unwind_encoding_t DwarfInstructions<A,R>::encodeToUseDwarf(const Registers_x86&) +{ + return UNWIND_X86_MODE_DWARF; +} + + + +template <typename A, typename R> +uint32_t DwarfInstructions<A,R>::getRBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure) +{ + if ( (regOffsetFromBaseOffset < 0) || (regOffsetFromBaseOffset > 32) ) { + failure = true; + return 0; + } + unsigned int slotIndex = regOffsetFromBaseOffset/8; + + switch ( reg ) { + case UNW_X86_64_RBX: + return UNWIND_X86_64_REG_RBX << (slotIndex*3); + case UNW_X86_64_R12: + return UNWIND_X86_64_REG_R12 << (slotIndex*3); + case UNW_X86_64_R13: + return UNWIND_X86_64_REG_R13 << (slotIndex*3); + case UNW_X86_64_R14: + return UNWIND_X86_64_REG_R14 << (slotIndex*3); + case UNW_X86_64_R15: + return UNWIND_X86_64_REG_R15 << (slotIndex*3); + } + + // invalid register + failure = true; + return 0; +} + + + +template <typename A, typename R> +compact_unwind_encoding_t DwarfInstructions<A,R>::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_x86_64& r, const typename CFI_Parser<A>::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + + // don't create compact unwind info for unsupported dwarf kinds + if ( prolog.registerSavedMoreThanOnce ) { + strcpy(warningBuffer, "register saved more than once (might be shrink wrap)"); + return UNWIND_X86_64_MODE_DWARF; + } + if ( prolog.cfaOffsetWasNegative ) { + strcpy(warningBuffer, "cfa had negative offset (dwarf might contain epilog)"); + return UNWIND_X86_64_MODE_DWARF; + } + if ( prolog.spExtraArgSize != 0 ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_GNU_args_size"); + return UNWIND_X86_64_MODE_DWARF; + } + + // figure out which kind of frame this function uses + bool standardRBPframe = ( + (prolog.cfaRegister == UNW_X86_64_RBP) + && (prolog.cfaRegisterOffset == 16) + && (prolog.savedRegisters[UNW_X86_64_RBP].location == CFI_Parser<A>::kRegisterInCFA) + && (prolog.savedRegisters[UNW_X86_64_RBP].value == -16) ); + bool standardRSPframe = (prolog.cfaRegister == UNW_X86_64_RSP); + if ( !standardRBPframe && !standardRSPframe ) { + // no compact encoding for this + strcpy(warningBuffer, "does not use RBP or RSP based frame"); + return UNWIND_X86_64_MODE_DWARF; + } + + // scan which registers are saved + int saveRegisterCount = 0; + bool rbxSaved = false; + bool r12Saved = false; + bool r13Saved = false; + bool r14Saved = false; + bool r15Saved = false; + bool rbpSaved = false; + for (int i=0; i < 64; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser<A>::kRegisterUnused ) { + if ( prolog.savedRegisters[i].location != CFI_Parser<A>::kRegisterInCFA ) { + sprintf(warningBuffer, "register %d saved somewhere other that in frame", i); + return UNWIND_X86_64_MODE_DWARF; + } + switch (i) { + case UNW_X86_64_RBX: + rbxSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_R12: + r12Saved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_R13: + r13Saved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_R14: + r14Saved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_R15: + r15Saved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_RBP: + rbpSaved = true; + ++saveRegisterCount; + break; + case DW_X86_64_RET_ADDR: + break; + default: + sprintf(warningBuffer, "non-standard register %d being saved in prolog", i); + return UNWIND_X86_64_MODE_DWARF; + } + } + } + const int64_t cfaOffsetRBX = prolog.savedRegisters[UNW_X86_64_RBX].value; + const int64_t cfaOffsetR12 = prolog.savedRegisters[UNW_X86_64_R12].value; + const int64_t cfaOffsetR13 = prolog.savedRegisters[UNW_X86_64_R13].value; + const int64_t cfaOffsetR14 = prolog.savedRegisters[UNW_X86_64_R14].value; + const int64_t cfaOffsetR15 = prolog.savedRegisters[UNW_X86_64_R15].value; + const int64_t cfaOffsetRBP = prolog.savedRegisters[UNW_X86_64_RBP].value; + + // encode standard RBP frames + compact_unwind_encoding_t encoding = 0; + if ( standardRBPframe ) { + // | | + // +--------------+ <- CFA + // | ret addr | + // +--------------+ + // | rbp | + // +--------------+ <- rbp + // ~ ~ + // +--------------+ + // | saved reg3 | + // +--------------+ <- CFA - offset+16 + // | saved reg2 | + // +--------------+ <- CFA - offset+8 + // | saved reg1 | + // +--------------+ <- CFA - offset + // | | + // +--------------+ + // | | + // <- rsp + // + encoding = UNWIND_X86_64_MODE_RBP_FRAME; + + // find save location of farthest register from rbp + int furthestCfaOffset = 0; + if ( rbxSaved & (cfaOffsetRBX < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetRBX; + if ( r12Saved & (cfaOffsetR12 < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetR12; + if ( r13Saved & (cfaOffsetR13 < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetR13; + if ( r14Saved & (cfaOffsetR14 < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetR14; + if ( r15Saved & (cfaOffsetR15 < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetR15; + + if ( furthestCfaOffset == 0 ) { + // no registers saved, nothing more to encode + return encoding; + } + + // add stack offset to encoding + int rbpOffset = furthestCfaOffset + 16; + int encodedOffset = rbpOffset/(-8); + if ( encodedOffset > 255 ) { + strcpy(warningBuffer, "offset of saved registers too far to encode"); + return UNWIND_X86_64_MODE_DWARF; + } + encoding |= (encodedOffset << __builtin_ctz(UNWIND_X86_64_RBP_FRAME_OFFSET)); + + // add register saved from each stack location + bool encodingFailure = false; + if ( rbxSaved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_RBX, cfaOffsetRBX - furthestCfaOffset, encodingFailure); + if ( r12Saved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_R12, cfaOffsetR12 - furthestCfaOffset, encodingFailure); + if ( r13Saved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_R13, cfaOffsetR13 - furthestCfaOffset, encodingFailure); + if ( r14Saved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_R14, cfaOffsetR14 - furthestCfaOffset, encodingFailure); + if ( r15Saved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_R15, cfaOffsetR15 - furthestCfaOffset, encodingFailure); + + if ( encodingFailure ){ + strcpy(warningBuffer, "saved registers not contiguous"); + return UNWIND_X86_64_MODE_DWARF; + } + + return encoding; + } + else { + // | | + // +--------------+ <- CFA + // | ret addr | + // +--------------+ + // | saved reg1 | + // +--------------+ <- CFA - 16 + // | saved reg2 | + // +--------------+ <- CFA - 24 + // | saved reg3 | + // +--------------+ <- CFA - 32 + // | saved reg4 | + // +--------------+ <- CFA - 40 + // | saved reg5 | + // +--------------+ <- CFA - 48 + // | saved reg6 | + // +--------------+ <- CFA - 56 + // | | + // <- esp + // + + // for RSP based frames we need to encode stack size in unwind info + encoding = UNWIND_X86_64_MODE_STACK_IMMD; + uint64_t stackValue = prolog.cfaRegisterOffset / 8; + uint32_t stackAdjust = 0; + bool immedStackSize = true; + const uint32_t stackMaxImmedValue = EXTRACT_BITS(0xFFFFFFFF,UNWIND_X86_64_FRAMELESS_STACK_SIZE); + if ( stackValue > stackMaxImmedValue ) { + // stack size is too big to fit as an immediate value, so encode offset of subq instruction in function + pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; + uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); + stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/8; + stackValue = functionContentAdjustStackIns - funcAddr; + immedStackSize = false; + if ( stackAdjust > 7 ) { + strcpy(warningBuffer, "stack subq instruction is too different from dwarf stack size"); + return UNWIND_X86_64_MODE_DWARF; + } + encoding = UNWIND_X86_64_MODE_STACK_IND; + } + + + // validate that saved registers are all within 6 slots abutting return address + int registers[6]; + for (int i=0; i < 6;++i) + registers[i] = 0; + if ( r15Saved ) { + if ( cfaOffsetR15 < -56 ) { + strcpy(warningBuffer, "r15 is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetR15+56)/8] = UNWIND_X86_64_REG_R15; + } + if ( r14Saved ) { + if ( cfaOffsetR14 < -56 ) { + strcpy(warningBuffer, "r14 is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetR14+56)/8] = UNWIND_X86_64_REG_R14; + } + if ( r13Saved ) { + if ( cfaOffsetR13 < -56 ) { + strcpy(warningBuffer, "r13 is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetR13+56)/8] = UNWIND_X86_64_REG_R13; + } + if ( r12Saved ) { + if ( cfaOffsetR12 < -56 ) { + strcpy(warningBuffer, "r12 is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetR12+56)/8] = UNWIND_X86_64_REG_R12; + } + if ( rbxSaved ) { + if ( cfaOffsetRBX < -56 ) { + strcpy(warningBuffer, "rbx is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetRBX+56)/8] = UNWIND_X86_64_REG_RBX; + } + if ( rbpSaved ) { + if ( cfaOffsetRBP < -56 ) { + strcpy(warningBuffer, "rbp is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetRBP+56)/8] = UNWIND_X86_64_REG_RBP; + } + + // validate that saved registers are contiguous and abut return address on stack + for (int i=0; i < saveRegisterCount; ++i) { + if ( registers[5-i] == 0 ) { + strcpy(warningBuffer, "registers not save contiguously in stack"); + return UNWIND_X86_64_MODE_DWARF; + } + } + + // encode register permutation + // the 10-bits are encoded differently depending on the number of registers saved + int renumregs[6]; + for (int i=6-saveRegisterCount; i < 6; ++i) { + int countless = 0; + for (int j=6-saveRegisterCount; j < i; ++j) { + if ( registers[j] < registers[i] ) + ++countless; + } + renumregs[i] = registers[i] - countless -1; + } + uint32_t permutationEncoding = 0; + switch ( saveRegisterCount ) { + case 6: + permutationEncoding |= (120*renumregs[0] + 24*renumregs[1] + 6*renumregs[2] + 2*renumregs[3] + renumregs[4]); + break; + case 5: + permutationEncoding |= (120*renumregs[1] + 24*renumregs[2] + 6*renumregs[3] + 2*renumregs[4] + renumregs[5]); + break; + case 4: + permutationEncoding |= (60*renumregs[2] + 12*renumregs[3] + 3*renumregs[4] + renumregs[5]); + break; + case 3: + permutationEncoding |= (20*renumregs[3] + 4*renumregs[4] + renumregs[5]); + break; + case 2: + permutationEncoding |= (5*renumregs[4] + renumregs[5]); + break; + case 1: + permutationEncoding |= (renumregs[5]); + break; + } + + encoding |= (stackValue << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_SIZE)); + encoding |= (stackAdjust << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_ADJUST)); + encoding |= (saveRegisterCount << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT)); + encoding |= (permutationEncoding << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION)); + return encoding; + } +} + + + + +// +// x86 specific functions +// +template <typename A, typename R> +int DwarfInstructions<A,R>::lastRestoreReg(const Registers_x86&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser<A>::kMaxRegisterNumber > (int)DW_X86_RET_ADDR ); + return DW_X86_RET_ADDR; +} + +template <typename A, typename R> +bool DwarfInstructions<A,R>::isReturnAddressRegister(int regNum, const Registers_x86&) +{ + return (regNum == DW_X86_RET_ADDR); +} + +template <typename A, typename R> +typename A::pint_t DwarfInstructions<A,R>::getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog, + const Registers_x86& registers) +{ + if ( prolog.cfaRegister != 0 ) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else if ( prolog.cfaExpression != 0 ) + return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0); + else + ABORT("getCFA(): unknown location for x86 cfa"); +} + + + + + +template <typename A, typename R> +uint32_t DwarfInstructions<A,R>::getEBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure) +{ + if ( (regOffsetFromBaseOffset < 0) || (regOffsetFromBaseOffset > 16) ) { + failure = true; + return 0; + } + unsigned int slotIndex = regOffsetFromBaseOffset/4; + + switch ( reg ) { + case UNW_X86_EBX: + return UNWIND_X86_REG_EBX << (slotIndex*3); + case UNW_X86_ECX: + return UNWIND_X86_REG_ECX << (slotIndex*3); + case UNW_X86_EDX: + return UNWIND_X86_REG_EDX << (slotIndex*3); + case UNW_X86_EDI: + return UNWIND_X86_REG_EDI << (slotIndex*3); + case UNW_X86_ESI: + return UNWIND_X86_REG_ESI << (slotIndex*3); + } + + // invalid register + failure = true; + return 0; +} + +template <typename A, typename R> +compact_unwind_encoding_t DwarfInstructions<A,R>::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_x86& r, const typename CFI_Parser<A>::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + + // don't create compact unwind info for unsupported dwarf kinds + if ( prolog.registerSavedMoreThanOnce ) { + strcpy(warningBuffer, "register saved more than once (might be shrink wrap)"); + return UNWIND_X86_MODE_DWARF; + } + if ( prolog.spExtraArgSize != 0 ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_GNU_args_size"); + return UNWIND_X86_MODE_DWARF; + } + + // figure out which kind of frame this function uses + bool standardEBPframe = ( + (prolog.cfaRegister == UNW_X86_EBP) + && (prolog.cfaRegisterOffset == 8) + && (prolog.savedRegisters[UNW_X86_EBP].location == CFI_Parser<A>::kRegisterInCFA) + && (prolog.savedRegisters[UNW_X86_EBP].value == -8) ); + bool standardESPframe = (prolog.cfaRegister == UNW_X86_ESP); + if ( !standardEBPframe && !standardESPframe ) { + // no compact encoding for this + strcpy(warningBuffer, "does not use EBP or ESP based frame"); + return UNWIND_X86_MODE_DWARF; + } + + // scan which registers are saved + int saveRegisterCount = 0; + bool ebxSaved = false; + bool ecxSaved = false; + bool edxSaved = false; + bool esiSaved = false; + bool ediSaved = false; + bool ebpSaved = false; + for (int i=0; i < 64; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser<A>::kRegisterUnused ) { + if ( prolog.savedRegisters[i].location != CFI_Parser<A>::kRegisterInCFA ) { + sprintf(warningBuffer, "register %d saved somewhere other that in frame", i); + return UNWIND_X86_MODE_DWARF; + } + switch (i) { + case UNW_X86_EBX: + ebxSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_ECX: + ecxSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_EDX: + edxSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_ESI: + esiSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_EDI: + ediSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_EBP: + ebpSaved = true; + ++saveRegisterCount; + break; + case DW_X86_RET_ADDR: + break; + default: + sprintf(warningBuffer, "non-standard register %d being saved in prolog", i); + return UNWIND_X86_MODE_DWARF; + } + } + } + const int32_t cfaOffsetEBX = prolog.savedRegisters[UNW_X86_EBX].value; + const int32_t cfaOffsetECX = prolog.savedRegisters[UNW_X86_ECX].value; + const int32_t cfaOffsetEDX = prolog.savedRegisters[UNW_X86_EDX].value; + const int32_t cfaOffsetEDI = prolog.savedRegisters[UNW_X86_EDI].value; + const int32_t cfaOffsetESI = prolog.savedRegisters[UNW_X86_ESI].value; + const int32_t cfaOffsetEBP = prolog.savedRegisters[UNW_X86_EBP].value; + + // encode standard RBP frames + compact_unwind_encoding_t encoding = 0; + if ( standardEBPframe ) { + // | | + // +--------------+ <- CFA + // | ret addr | + // +--------------+ + // | ebp | + // +--------------+ <- ebp + // ~ ~ + // +--------------+ + // | saved reg3 | + // +--------------+ <- CFA - offset+8 + // | saved reg2 | + // +--------------+ <- CFA - offset+e + // | saved reg1 | + // +--------------+ <- CFA - offset + // | | + // +--------------+ + // | | + // <- esp + // + encoding = UNWIND_X86_MODE_EBP_FRAME; + + // find save location of farthest register from ebp + int furthestCfaOffset = 0; + if ( ebxSaved & (cfaOffsetEBX < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetEBX; + if ( ecxSaved & (cfaOffsetECX < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetECX; + if ( edxSaved & (cfaOffsetEDX < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetEDX; + if ( ediSaved & (cfaOffsetEDI < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetEDI; + if ( esiSaved & (cfaOffsetESI < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetESI; + + if ( furthestCfaOffset == 0 ) { + // no registers saved, nothing more to encode + return encoding; + } + + // add stack offset to encoding + int ebpOffset = furthestCfaOffset + 8; + int encodedOffset = ebpOffset/(-4); + if ( encodedOffset > 255 ) { + strcpy(warningBuffer, "offset of saved registers too far to encode"); + return UNWIND_X86_MODE_DWARF; + } + encoding |= (encodedOffset << __builtin_ctz(UNWIND_X86_EBP_FRAME_OFFSET)); + + // add register saved from each stack location + bool encodingFailure = false; + if ( ebxSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_EBX, cfaOffsetEBX - furthestCfaOffset, encodingFailure); + if ( ecxSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_ECX, cfaOffsetECX - furthestCfaOffset, encodingFailure); + if ( edxSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_EDX, cfaOffsetEDX - furthestCfaOffset, encodingFailure); + if ( ediSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_EDI, cfaOffsetEDI - furthestCfaOffset, encodingFailure); + if ( esiSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_ESI, cfaOffsetESI - furthestCfaOffset, encodingFailure); + + if ( encodingFailure ){ + strcpy(warningBuffer, "saved registers not contiguous"); + return UNWIND_X86_MODE_DWARF; + } + + return encoding; + } + else { + // | | + // +--------------+ <- CFA + // | ret addr | + // +--------------+ + // | saved reg1 | + // +--------------+ <- CFA - 8 + // | saved reg2 | + // +--------------+ <- CFA - 12 + // | saved reg3 | + // +--------------+ <- CFA - 16 + // | saved reg4 | + // +--------------+ <- CFA - 20 + // | saved reg5 | + // +--------------+ <- CFA - 24 + // | saved reg6 | + // +--------------+ <- CFA - 28 + // | | + // <- esp + // + + // for ESP based frames we need to encode stack size in unwind info + encoding = UNWIND_X86_MODE_STACK_IMMD; + uint64_t stackValue = prolog.cfaRegisterOffset / 4; + uint32_t stackAdjust = 0; + bool immedStackSize = true; + const uint32_t stackMaxImmedValue = EXTRACT_BITS(0xFFFFFFFF,UNWIND_X86_FRAMELESS_STACK_SIZE); + if ( stackValue > stackMaxImmedValue ) { + // stack size is too big to fit as an immediate value, so encode offset of subq instruction in function + pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; + uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); + stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/4; + stackValue = functionContentAdjustStackIns - funcAddr; + immedStackSize = false; + if ( stackAdjust > 7 ) { + strcpy(warningBuffer, "stack subq instruction is too different from dwarf stack size"); + return UNWIND_X86_MODE_DWARF; + } + encoding = UNWIND_X86_MODE_STACK_IND; + } + + + // validate that saved registers are all within 6 slots abutting return address + int registers[6]; + for (int i=0; i < 6;++i) + registers[i] = 0; + if ( ebxSaved ) { + if ( cfaOffsetEBX < -28 ) { + strcpy(warningBuffer, "ebx is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetEBX+28)/4] = UNWIND_X86_REG_EBX; + } + if ( ecxSaved ) { + if ( cfaOffsetECX < -28 ) { + strcpy(warningBuffer, "ecx is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetECX+28)/4] = UNWIND_X86_REG_ECX; + } + if ( edxSaved ) { + if ( cfaOffsetEDX < -28 ) { + strcpy(warningBuffer, "edx is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetEDX+28)/4] = UNWIND_X86_REG_EDX; + } + if ( ediSaved ) { + if ( cfaOffsetEDI < -28 ) { + strcpy(warningBuffer, "edi is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetEDI+28)/4] = UNWIND_X86_REG_EDI; + } + if ( esiSaved ) { + if ( cfaOffsetESI < -28 ) { + strcpy(warningBuffer, "esi is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetESI+28)/4] = UNWIND_X86_REG_ESI; + } + if ( ebpSaved ) { + if ( cfaOffsetEBP < -28 ) { + strcpy(warningBuffer, "ebp is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetEBP+28)/4] = UNWIND_X86_REG_EBP; + } + + // validate that saved registers are contiguous and abut return address on stack + for (int i=0; i < saveRegisterCount; ++i) { + if ( registers[5-i] == 0 ) { + strcpy(warningBuffer, "registers not save contiguously in stack"); + return UNWIND_X86_MODE_DWARF; + } + } + + // encode register permutation + // the 10-bits are encoded differently depending on the number of registers saved + int renumregs[6]; + for (int i=6-saveRegisterCount; i < 6; ++i) { + int countless = 0; + for (int j=6-saveRegisterCount; j < i; ++j) { + if ( registers[j] < registers[i] ) + ++countless; + } + renumregs[i] = registers[i] - countless -1; + } + uint32_t permutationEncoding = 0; + switch ( saveRegisterCount ) { + case 6: + permutationEncoding |= (120*renumregs[0] + 24*renumregs[1] + 6*renumregs[2] + 2*renumregs[3] + renumregs[4]); + break; + case 5: + permutationEncoding |= (120*renumregs[1] + 24*renumregs[2] + 6*renumregs[3] + 2*renumregs[4] + renumregs[5]); + break; + case 4: + permutationEncoding |= (60*renumregs[2] + 12*renumregs[3] + 3*renumregs[4] + renumregs[5]); + break; + case 3: + permutationEncoding |= (20*renumregs[3] + 4*renumregs[4] + renumregs[5]); + break; + case 2: + permutationEncoding |= (5*renumregs[4] + renumregs[5]); + break; + case 1: + permutationEncoding |= (renumregs[5]); + break; + } + + encoding |= (stackValue << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_SIZE)); + encoding |= (stackAdjust << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_ADJUST)); + encoding |= (saveRegisterCount << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_REG_COUNT)); + encoding |= (permutationEncoding << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION)); + return encoding; + } +} + + + + + + + +// +// ppc specific functions +// +template <typename A, typename R> +int DwarfInstructions<A,R>::lastRestoreReg(const Registers_ppc&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser<A>::kMaxRegisterNumber > (int)UNW_PPC_SPEFSCR ); + return UNW_PPC_SPEFSCR; +} + +template <typename A, typename R> +bool DwarfInstructions<A,R>::isReturnAddressRegister(int regNum, const Registers_ppc&) +{ + return (regNum == UNW_PPC_LR); +} + +template <typename A, typename R> +typename A::pint_t DwarfInstructions<A,R>::getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog, + const Registers_ppc& registers) +{ + if ( prolog.cfaRegister != 0 ) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else if ( prolog.cfaExpression != 0 ) + return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0); + else + ABORT("getCFA(): unknown location for ppc cfa"); +} + + +template <typename A, typename R> +compact_unwind_encoding_t DwarfInstructions<A,R>::encodeToUseDwarf(const Registers_ppc&) +{ + return UNWIND_X86_MODE_DWARF; +} + + +template <typename A, typename R> +compact_unwind_encoding_t DwarfInstructions<A,R>::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_ppc& r, const typename CFI_Parser<A>::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + return UNWIND_X86_MODE_DWARF; +} + + + + +} // namespace lldb_private + + +#endif // __DWARF_INSTRUCTIONS_HPP__ + + + + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfParser.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfParser.hpp new file mode 100644 index 00000000000..b11cb8ce441 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfParser.hpp @@ -0,0 +1,869 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- DwarfParser.hpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// +// processor specific parsing of dwarf unwind instructions +// + +#ifndef __DWARF_PARSER_HPP__ +#define __DWARF_PARSER_HPP__ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include <vector> + +#include "libunwind.h" +#include "dwarf2.h" + +#include "AddressSpace.hpp" +#include "RemoteUnwindProfile.h" + +namespace lldb_private { + + +/// +/// CFI_Parser does basic parsing of a CFI (Call Frame Information) records. +/// See Dwarf Spec for details: +/// http://www.linux-foundation.org/spec/booksets/LSB-Core-generic/LSB-Core-generic/ehframechpt.html +/// +template <typename A> +class CFI_Parser +{ +public: + typedef typename A::pint_t pint_t; + + /// + /// Information encoded in a CIE (Common Information Entry) + /// + struct CIE_Info { + pint_t cieStart; + pint_t cieLength; + pint_t cieInstructions; + uint8_t pointerEncoding; + uint8_t lsdaEncoding; + uint8_t personalityEncoding; + uint8_t personalityOffsetInCIE; + pint_t personality; + int codeAlignFactor; + int dataAlignFactor; + bool isSignalFrame; + bool fdesHaveAugmentationData; + }; + + /// + /// Information about an FDE (Frame Description Entry) + /// + struct FDE_Info { + pint_t fdeStart; + pint_t fdeLength; + pint_t fdeInstructions; + pint_t pcStart; + pint_t pcEnd; + pint_t lsda; + }; + + /// + /// Used by linker when parsing __eh_frame section + /// + struct FDE_Reference { + pint_t address; + uint32_t offsetInFDE; + uint8_t encodingOfAddress; + }; + struct FDE_Atom_Info { + pint_t fdeAddress; + FDE_Reference function; + FDE_Reference cie; + FDE_Reference lsda; + }; + struct CIE_Atom_Info { + pint_t cieAddress; + FDE_Reference personality; + }; + + + /// + /// Information about a frame layout and registers saved determined + /// by "running" the dwarf FDE "instructions" + /// + enum { kMaxRegisterNumber = 120 }; + enum RegisterSavedWhere { kRegisterUnused, kRegisterInCFA, kRegisterOffsetFromCFA, + kRegisterInRegister, kRegisterAtExpression, kRegisterIsExpression } ; + struct RegisterLocation { + RegisterSavedWhere location; + int64_t value; + }; + struct PrologInfo { + uint32_t cfaRegister; + int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset + int64_t cfaExpression; // CFA = expression + bool registersInOtherRegisters; + bool registerSavedMoreThanOnce; + bool cfaOffsetWasNegative; + uint32_t spExtraArgSize; + uint32_t codeOffsetAtStackDecrement; + + RegisterLocation savedRegisters[kMaxRegisterNumber]; // from where to restore registers + }; + + struct PrologInfoStackEntry { + PrologInfoStackEntry(PrologInfoStackEntry* n, const PrologInfo& i) + : next(n), info(i) {} + PrologInfoStackEntry* next; + PrologInfo info; + }; + + static bool findFDE(A& addressSpace, pint_t pc, pint_t ehSectionStart, uint32_t sectionLength, pint_t fdeHint, FDE_Info* fdeInfo, CIE_Info* cieInfo); + +#if defined (SUPPORT_REMOTE_UNWINDING) + static bool functionFuncBoundsViaFDE(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, std::vector<FuncBounds> &funcbounds); +#endif + + static const char* decodeFDE(A& addressSpace, pint_t fdeStart, FDE_Info* fdeInfo, CIE_Info* cieInfo); + static bool parseFDEInstructions(A& addressSpace, const FDE_Info& fdeInfo, const CIE_Info& cieInfo, pint_t upToPC, PrologInfo* results); + static const char* getCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, + std::vector<FDE_Atom_Info>& fdes, std::vector<CIE_Atom_Info>& cies); + static uint32_t getCFICount(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength); + + static const char* parseCIE(A& addressSpace, pint_t cie, CIE_Info* cieInfo); + +private: + static bool parseInstructions(A& addressSpace, pint_t instructions, pint_t instructionsEnd, const CIE_Info& cieInfo, + pint_t pcoffset, PrologInfoStackEntry*& rememberStack, PrologInfo* results); + +}; + + +/// +/// Parse a FDE into a CIE_Info and an FDE_Info +/// +template <typename A> +const char* CFI_Parser<A>::decodeFDE(A& addressSpace, pint_t fdeStart, FDE_Info* fdeInfo, CIE_Info* cieInfo) +{ + pint_t p = fdeStart; + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return "FDE has zero length"; // end marker + uint32_t ciePointer = addressSpace.get32(p); + if ( ciePointer == 0 ) + return "FDE is really a CIE"; // this is a CIE not an FDE + pint_t nextCFI = p + cfiLength; + pint_t cieStart = p-ciePointer; + const char* err = parseCIE(addressSpace, cieStart, cieInfo); + if (err != NULL) + return err; + p += 4; + // parse pc begin and range + pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F); + // parse rest of info + fdeInfo->lsda = 0; + // check for augmentation length + if ( cieInfo->fdesHaveAugmentationData ) { + uintptr_t augLen = addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if ( cieInfo->lsdaEncoding != 0 ) { + // peek at value (without indirection). Zero means no lsda + pint_t lsdaStart = p; + if ( addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0 ) { + // reset pointer and re-parse lsda address + p = lsdaStart; + fdeInfo->lsda = addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding); + } + } + p = endOfAug; + } + fdeInfo->fdeStart = fdeStart; + fdeInfo->fdeLength = nextCFI - fdeStart; + fdeInfo->fdeInstructions = p; + fdeInfo->pcStart = pcStart; + fdeInfo->pcEnd = pcStart+pcRange; + return NULL; // success +} + + +/// +/// Scan an eh_frame section to find an FDE for a pc +/// +template <typename A> +bool CFI_Parser<A>::findFDE(A& addressSpace, pint_t pc, pint_t ehSectionStart, uint32_t sectionLength, pint_t fdeHint, FDE_Info* fdeInfo, CIE_Info* cieInfo) +{ + //fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc); + pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart; + const pint_t ehSectionEnd = p + sectionLength; + while ( p < ehSectionEnd ) { + pint_t currentCFI = p; + //fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p); + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return false; // end marker + uint32_t id = addressSpace.get32(p); + if ( id == 0 ) { + // skip over CIEs + p += cfiLength; + } + else { + // process FDE to see if it covers pc + pint_t nextCFI = p + cfiLength; + uint32_t ciePointer = addressSpace.get32(p); + pint_t cieStart = p-ciePointer; + // validate pointer to CIE is within section + if ( (ehSectionStart <= cieStart) && (cieStart < ehSectionEnd) ) { + if ( parseCIE(addressSpace, cieStart, cieInfo) == NULL ) { + p += 4; + // parse pc begin and range + pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F); + //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + // test if pc is within the function this FDE covers + if ( (pcStart < pc) && (pc <= pcStart+pcRange) ) { + // parse rest of info + fdeInfo->lsda = 0; + // check for augmentation length + if ( cieInfo->fdesHaveAugmentationData ) { + uintptr_t augLen = addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if ( cieInfo->lsdaEncoding != 0 ) { + // peek at value (without indirection). Zero means no lsda + pint_t lsdaStart = p; + if ( addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0 ) { + // reset pointer and re-parse lsda address + p = lsdaStart; + fdeInfo->lsda = addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding); + } + } + p = endOfAug; + } + fdeInfo->fdeStart = currentCFI; + fdeInfo->fdeLength = nextCFI - currentCFI; + fdeInfo->fdeInstructions = p; + fdeInfo->pcStart = pcStart; + fdeInfo->pcEnd = pcStart+pcRange; + //fprintf(stderr, "findFDE(pc=0x%llX) found with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pc, (uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + return true; + } + else { + //fprintf(stderr, "findFDE(pc=0x%llX) not found with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pc, (uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + // pc is not in begin/range, skip this FDE + } + } + else { + // malformed CIE, now augmentation describing pc range encoding + //fprintf(stderr, "malformed CIE\n"); + } + } + else { + // malformed FDE. CIE is bad + //fprintf(stderr, "malformed FDE, cieStart=0x%llX, ehSectionStart=0x%llX, ehSectionEnd=0x%llX\n", + // (uint64_t)cieStart, (uint64_t)ehSectionStart, (uint64_t)ehSectionEnd); + } + p = nextCFI; + } + } + //fprintf(stderr, "findFDE(pc=0x%llX) not found\n",(uint64_t)pc); + return false; +} + +#if defined (SUPPORT_REMOTE_UNWINDING) +/// Scan an eh_frame section to find all the function start addresses +/// This is only made for working with libunwind-remote. It copies +/// the eh_frame section into local memory and steps through it quickly +/// to find the start addresses of the CFIs. +/// +template <typename A> +bool CFI_Parser<A>::functionFuncBoundsViaFDE(A& addressSpace, pint_t ehSectionStart, + uint32_t sectionLength, std::vector<FuncBounds> &funcbounds) +{ + //fprintf(stderr, "functionFuncBoundsViaFDE(0x%llX)\n", (long long)pc); + pint_t p = ehSectionStart; + const pint_t ehSectionEnd = p + sectionLength; + pint_t lastCieSeen = (pint_t) -1; + CIE_Info cieInfo; + while ( p < ehSectionEnd ) { + //fprintf(stderr, "functionFuncBoundsViaFDE() CFI at 0x%llX\n", (long long)p); + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return false; // end marker + uint32_t id = addressSpace.get32(p); + if ( id == 0 ) { + // skip over CIEs + p += cfiLength; + } + else { + // process FDE to see if it covers pc + pint_t nextCFI = p + cfiLength; + uint32_t ciePointer = addressSpace.get32(p); + pint_t cieStart = p-ciePointer; + // validate pointer to CIE is within section + if ( (ehSectionStart <= cieStart) && (cieStart < ehSectionEnd) ) { + const char *errmsg; + // don't re-parse the cie if this fde is pointing to one we already parsed + if (cieStart == lastCieSeen) { + errmsg = NULL; + } + else { + errmsg = parseCIE(addressSpace, cieStart, &cieInfo); + if (errmsg == NULL) + lastCieSeen = cieStart; + } + if ( errmsg == NULL ) { + p += 4; + // parse pc begin and range + pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F); + //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + funcbounds.push_back(FuncBounds(pcStart, pcStart + pcRange)); + } + else { + // malformed CIE, now augmentation describing pc range encoding + //fprintf(stderr, "malformed CIE\n"); + return false; + } + } + else { + // malformed FDE. CIE is bad + //fprintf(stderr, "malformed FDE, cieStart=0x%llX, ehSectionStart=0x%llX, ehSectionEnd=0x%llX\n", + // (uint64_t)cieStart, (uint64_t)ehSectionStart, (uint64_t)ehSectionEnd); + return false; + } + p = nextCFI; + } + } + return true; +} +#endif // SUPPORT_REMOTE_UNWINDING + + + +/// +/// Extract info from a CIE +/// +template <typename A> +const char* CFI_Parser<A>::parseCIE(A& addressSpace, pint_t cie, CIE_Info* cieInfo) +{ + //fprintf(stderr, "parseCIE(0x%llX)\n", (long long)cie); + cieInfo->pointerEncoding = 0; + cieInfo->lsdaEncoding = 0; + cieInfo->personalityEncoding = 0; + cieInfo->personalityOffsetInCIE = 0; + cieInfo->personality = 0; + cieInfo->codeAlignFactor = 0; + cieInfo->dataAlignFactor = 0; + cieInfo->isSignalFrame = false; + cieInfo->fdesHaveAugmentationData = false; + cieInfo->cieStart = cie; + pint_t p = cie; + uint64_t cieLength = addressSpace.get32(p); + p += 4; + pint_t cieContentEnd = p + cieLength; + if ( cieLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cieLength = addressSpace.get64(p); + p += 8; + cieContentEnd = p + cieLength; + } + if ( cieLength == 0 ) + return false; + // CIE ID is always 0 + if ( addressSpace.get32(p) != 0 ) + return "CIE ID is not zero"; + p += 4; + // Version is always 1 or 3 + uint8_t version = addressSpace.get8(p); + if ( (version != 1) && (version != 3) ) + return "CIE version is not 1 or 3"; + ++p; + // save start of augmentation string and find end + pint_t strStart = p; + while ( addressSpace.get8(p) != 0 ) + ++p; + ++p; + // parse code aligment factor + cieInfo->codeAlignFactor = addressSpace.getULEB128(p, cieContentEnd); + // parse data alignment factor + cieInfo->dataAlignFactor = addressSpace.getSLEB128(p, cieContentEnd); + // parse return address register + addressSpace.getULEB128(p, cieContentEnd); + // parse augmentation data based on augmentation string + const char* result = NULL; + if ( addressSpace.get8(strStart) == 'z' ) { + // parse augmentation data length + addressSpace.getULEB128(p, cieContentEnd); + for (pint_t s=strStart; addressSpace.get8(s) != '\0'; ++s) { + switch ( addressSpace.get8(s) ) { + case 'z': + cieInfo->fdesHaveAugmentationData = true; + break; + case 'P': + cieInfo->personalityEncoding = addressSpace.get8(p); + ++p; + cieInfo->personalityOffsetInCIE = p-cie; + cieInfo->personality = addressSpace.getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding); + break; + case 'L': + cieInfo->lsdaEncoding = addressSpace.get8(p); + ++p; + break; + case 'R': + cieInfo->pointerEncoding = addressSpace.get8(p); + ++p; + break; + case 'S': + cieInfo->isSignalFrame = true; + break; + default: + // ignore unknown letters + break; + } + } + } + cieInfo->cieLength = cieContentEnd - cieInfo->cieStart; + cieInfo->cieInstructions = p; + return result; +} + + +template <typename A> +uint32_t CFI_Parser<A>::getCFICount(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength) +{ + uint32_t count = 0; + const pint_t ehSectionEnd = ehSectionStart + sectionLength; + for (pint_t p=ehSectionStart; p < ehSectionEnd; ) { + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return count; // end marker + ++count; + p += cfiLength; + } + return count; +} + + + +template <typename A> +const char* CFI_Parser<A>::getCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, + std::vector<FDE_Atom_Info>& fdes, std::vector<CIE_Atom_Info>& cies) +{ + const pint_t ehSectionEnd = ehSectionStart + sectionLength; + for (pint_t p=ehSectionStart; p < ehSectionEnd; ) { + pint_t currentCFI = p; + uint64_t cfiLength = addressSpace.get32(p); + p += 4; + if ( cfiLength == 0xffffffff ) { + // 0xffffffff means length is really next 8 bytes + cfiLength = addressSpace.get64(p); + p += 8; + } + if ( cfiLength == 0 ) + return NULL; // end marker + uint32_t id = addressSpace.get32(p); + if ( id == 0 ) { + // is CIE + CIE_Info cieInfo; + const char* err = parseCIE(addressSpace, currentCFI, &cieInfo); + if ( err != NULL ) + return err; + CIE_Atom_Info entry; + entry.cieAddress = currentCFI; + entry.personality.address = cieInfo.personality; + entry.personality.offsetInFDE = cieInfo.personalityOffsetInCIE; + entry.personality.encodingOfAddress = cieInfo.personalityEncoding; + cies.push_back(entry); + p += cfiLength; + } + else { + // is FDE + FDE_Atom_Info entry; + entry.fdeAddress = currentCFI; + entry.function.address = 0; + entry.cie.address = 0; + entry.lsda.address = 0; + pint_t nextCFI = p + cfiLength; + uint32_t ciePointer = addressSpace.get32(p); + pint_t cieStart = p-ciePointer; + // validate pointer to CIE is within section + if ( (cieStart < ehSectionStart) || (cieStart > ehSectionEnd) ) + return "FDE points to CIE outside __eh_frame section"; + CIE_Info cieInfo; + const char* err = parseCIE(addressSpace, cieStart, &cieInfo); + if ( err != NULL ) + return err; + entry.cie.address = cieStart; + entry.cie.offsetInFDE = p-currentCFI; + entry.cie.encodingOfAddress = DW_EH_PE_sdata4 | DW_EH_PE_pcrel; + p += 4; + // parse pc begin and range + pint_t offsetOfFunctionAddress = p-currentCFI; + pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F); + //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); + // test if pc is within the function this FDE covers + entry.function.address = pcStart; + entry.function.offsetInFDE = offsetOfFunctionAddress; + entry.function.encodingOfAddress = cieInfo.pointerEncoding; + // skip over augmentation length + if ( cieInfo.fdesHaveAugmentationData ) { + uintptr_t augLen = addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if ( (cieInfo.lsdaEncoding != 0) && (addressSpace.getP(p) != 0) ) { + pint_t offsetOfLSDAAddress = p-currentCFI; + entry.lsda.address = addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding); + entry.lsda.offsetInFDE = offsetOfLSDAAddress; + entry.lsda.encodingOfAddress = cieInfo.lsdaEncoding; + } + p = endOfAug; + } + fdes.push_back(entry); + p = nextCFI; + } + } + return NULL; // success +} + + + +/// +/// "run" the dwarf instructions and create the abstact PrologInfo for an FDE +/// +template <typename A> +bool CFI_Parser<A>::parseFDEInstructions(A& addressSpace, const FDE_Info& fdeInfo, const CIE_Info& cieInfo, pint_t upToPC, PrologInfo* results) +{ + // clear results + bzero(results, sizeof(PrologInfo)); + PrologInfoStackEntry* rememberStack = NULL; + + // parse CIE then FDE instructions + return parseInstructions(addressSpace, cieInfo.cieInstructions, cieInfo.cieStart+cieInfo.cieLength, + cieInfo, (pint_t)(-1), rememberStack, results) + && parseInstructions(addressSpace, fdeInfo.fdeInstructions, fdeInfo.fdeStart+fdeInfo.fdeLength, + cieInfo, upToPC-fdeInfo.pcStart, rememberStack, results); +} + + +/// +/// "run" the dwarf instructions +/// +template <typename A> +bool CFI_Parser<A>::parseInstructions(A& addressSpace, pint_t instructions, pint_t instructionsEnd, const CIE_Info& cieInfo, + pint_t pcoffset, PrologInfoStackEntry*& rememberStack, PrologInfo* results) +{ + const bool logDwarf = false; + pint_t p = instructions; + uint32_t codeOffset = 0; + PrologInfo initialState = *results; + + // see Dwarf Spec, section 6.4.2 for details on unwind opcodes + while ( (p < instructionsEnd) && (codeOffset < pcoffset) ) { + uint64_t reg; + uint64_t reg2; + int64_t offset; + uint64_t length; + uint8_t opcode = addressSpace.get8(p); + uint8_t operand; + PrologInfoStackEntry* entry; + ++p; + switch (opcode) { + case DW_CFA_nop: + if ( logDwarf ) fprintf(stderr, "DW_CFA_nop\n"); + break; + case DW_CFA_set_loc: + codeOffset = addressSpace.getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding); + if ( logDwarf ) fprintf(stderr, "DW_CFA_set_loc\n"); + break; + case DW_CFA_advance_loc1: + codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor); + p += 1; + if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc1: new offset=%u\n", codeOffset); + break; + case DW_CFA_advance_loc2: + codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor); + p += 2; + if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc2: new offset=%u\n", codeOffset); + break; + case DW_CFA_advance_loc4: + codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor); + p += 4; + if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc4: new offset=%u\n", codeOffset); + break; + case DW_CFA_offset_extended: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_offset_extended dwarf unwind, reg too big\n"); + return false; + } + if ( results->savedRegisters[reg].location != kRegisterUnused ) + results->registerSavedMoreThanOnce = true; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_offset_extended(reg=%lld, offset=%lld)\n", reg, offset); + break; + case DW_CFA_restore_extended: + reg = addressSpace.getULEB128(p, instructionsEnd);; + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_restore_extended dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg] = initialState.savedRegisters[reg]; + if ( logDwarf ) fprintf(stderr, "DW_CFA_restore_extended(reg=%lld)\n", reg); + break; + case DW_CFA_undefined: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_undefined dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterUnused; + if ( logDwarf ) fprintf(stderr, "DW_CFA_undefined(reg=%lld)\n", reg); + break; + case DW_CFA_same_value: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_same_value dwarf unwind, reg too big\n"); + return false; + } + if ( logDwarf ) fprintf(stderr, "DW_CFA_same_value(reg=%lld)\n", reg); + break; + case DW_CFA_register: + reg = addressSpace.getULEB128(p, instructionsEnd); + reg2 = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_register dwarf unwind, reg too big\n"); + return false; + } + if ( reg2 > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_register dwarf unwind, reg2 too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterInRegister; + results->savedRegisters[reg].value = reg2; + results->registersInOtherRegisters = true; + if ( logDwarf ) fprintf(stderr, "DW_CFA_register(reg=%lld, reg2=%lld)\n", reg, reg2); + break; + case DW_CFA_remember_state: + // avoid operator new, because that would be an upward dependency + entry = (PrologInfoStackEntry*)malloc(sizeof(PrologInfoStackEntry)); + if ( entry != NULL ) { + entry->next = rememberStack; + entry->info = *results; + rememberStack = entry; + } + else { + return false; + } + if ( logDwarf ) fprintf(stderr, "DW_CFA_remember_state\n"); + break; + case DW_CFA_restore_state: + if ( rememberStack != NULL ) { + PrologInfoStackEntry* top = rememberStack; + *results = top->info; + rememberStack = top->next; + free((char*)top); + } + else { + return false; + } + if ( logDwarf ) fprintf(stderr, "DW_CFA_restore_state\n"); + break; + case DW_CFA_def_cfa: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_def_cfa dwarf unwind, reg too big\n"); + return false; + } + results->cfaRegister = reg; + results->cfaRegisterOffset = offset; + if ( offset > 0x80000000 ) + results->cfaOffsetWasNegative = true; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa(reg=%lld, offset=%lld)\n", reg, offset); + break; + case DW_CFA_def_cfa_register: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_def_cfa_register dwarf unwind, reg too big\n"); + return false; + } + results->cfaRegister = reg; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_register(%lld)\n", reg); + break; + case DW_CFA_def_cfa_offset: + results->cfaRegisterOffset = addressSpace.getULEB128(p, instructionsEnd); + results->codeOffsetAtStackDecrement = codeOffset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_offset(%d)\n", results->cfaRegisterOffset); + break; + case DW_CFA_def_cfa_expression: + results->cfaRegister = 0; + results->cfaExpression = p; + length = addressSpace.getULEB128(p, instructionsEnd); + p += length; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_expression(expression=0x%llX, length=%llu)\n", + results->cfaExpression, length); + break; + case DW_CFA_expression: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_expression dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterAtExpression; + results->savedRegisters[reg].value = p; + length = addressSpace.getULEB128(p, instructionsEnd); + p += length; + if ( logDwarf ) fprintf(stderr, "DW_CFA_expression(reg=%lld, expression=0x%llX, length=%llu)\n", + reg, results->savedRegisters[reg].value, length); + break; + case DW_CFA_offset_extended_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_offset_extended_sf dwarf unwind, reg too big\n"); + return false; + } + offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if ( results->savedRegisters[reg].location != kRegisterUnused ) + results->registerSavedMoreThanOnce = true; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_offset_extended_sf(reg=%lld, offset=%lld)\n", reg, offset); + break; + case DW_CFA_def_cfa_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_def_cfa_sf dwarf unwind, reg too big\n"); + return false; + } + results->cfaRegister = reg; + results->cfaRegisterOffset = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_sf(reg=%lld, offset=%lld)\n", reg, offset); + break; + case DW_CFA_def_cfa_offset_sf: + results->cfaRegisterOffset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->codeOffsetAtStackDecrement = codeOffset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_offset_sf(%d)\n", results->cfaRegisterOffset); + break; + case DW_CFA_val_offset: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterOffsetFromCFA; + results->savedRegisters[reg].value = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_val_offset(reg=%lld, offset=%lld\n", reg, offset); + break; + case DW_CFA_val_offset_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_val_offset_sf dwarf unwind, reg too big\n"); + return false; + } + offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + results->savedRegisters[reg].location = kRegisterOffsetFromCFA; + results->savedRegisters[reg].value = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_val_offset_sf(reg=%lld, offset=%lld\n", reg, offset); + break; + case DW_CFA_val_expression: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_val_expression dwarf unwind, reg too big\n"); + return false; + } + results->savedRegisters[reg].location = kRegisterIsExpression; + results->savedRegisters[reg].value = p; + length = addressSpace.getULEB128(p, instructionsEnd); + p += length; + if ( logDwarf ) fprintf(stderr, "DW_CFA_val_expression(reg=%lld, expression=0x%llX, length=%lld)\n", + reg, results->savedRegisters[reg].value, length); + break; + case DW_CFA_GNU_args_size: + offset = addressSpace.getULEB128(p, instructionsEnd); + results->spExtraArgSize = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_GNU_args_size(%lld)\n", offset); + break; + case DW_CFA_GNU_negative_offset_extended: + reg = addressSpace.getULEB128(p, instructionsEnd); + if ( reg > kMaxRegisterNumber ) { + fprintf(stderr, "malformed DW_CFA_GNU_negative_offset_extended dwarf unwind, reg too big\n"); + return false; + } + offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if ( results->savedRegisters[reg].location != kRegisterUnused ) + results->registerSavedMoreThanOnce = true; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = -offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_GNU_negative_offset_extended(%lld)\n", offset); + break; + default: + operand = opcode & 0x3F; + switch ( opcode & 0xC0 ) { + case DW_CFA_offset: + reg = operand; + offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; + if ( results->savedRegisters[reg].location != kRegisterUnused ) + results->registerSavedMoreThanOnce = true; + results->savedRegisters[reg].location = kRegisterInCFA; + results->savedRegisters[reg].value = offset; + if ( logDwarf ) fprintf(stderr, "DW_CFA_offset(reg=%d, offset=%lld)\n", operand, offset); + break; + case DW_CFA_advance_loc: + codeOffset += operand * cieInfo.codeAlignFactor; + if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc: new offset=%u\n", codeOffset); + break; + case DW_CFA_restore: + // <rdar://problem/7503075> Python crashes when handling an exception thrown by an obj-c object + // libffi uses DW_CFA_restore in the middle of some custom dward, so it is not a good epilog flag + //return true; // gcc-4.5 starts the epilog with this + reg = operand; + results->savedRegisters[reg] = initialState.savedRegisters[reg]; + if ( logDwarf ) fprintf(stderr, "DW_CFA_restore(reg=%lld)\n", reg); + break; + default: + if ( logDwarf ) fprintf(stderr, "unknown CFA opcode 0x%02X\n", opcode); + return false; + } + } + } + + return true; +} + + +} // namespace lldb_private + + +#endif // __DWARF_PARSER_HPP__ + + + + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/FileAbstraction.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/FileAbstraction.hpp new file mode 100644 index 00000000000..a4af02051a5 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/FileAbstraction.hpp @@ -0,0 +1,135 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- FileAbstraction.hpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __FILE_ABSTRACTION__ +#define __FILE_ABSTRACTION__ + + +#include <stdint.h> +#include <string.h> +#include <libkern/OSByteOrder.h> + +#ifdef __OPTIMIZE__ +#define INLINE __attribute__((always_inline)) +#else +#define INLINE +#endif + +// +// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants +// +// For example: to make a utility that handles 32-bit little enidan files use: Pointer32<LittleEndian> +// +// +// get16() read a 16-bit number from an E endian struct +// set16() write a 16-bit number to an E endian struct +// get32() read a 32-bit number from an E endian struct +// set32() write a 32-bit number to an E endian struct +// get64() read a 64-bit number from an E endian struct +// set64() write a 64-bit number to an E endian struct +// +// getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// +// getBitsRaw() read a bit field from a struct with native endianness +// setBitsRaw() write a bit field from a struct with native endianness +// + +class BigEndian +{ +public: + static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); } + static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); } + + static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); } + static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } + + static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); } + static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); } + + static uint32_t getBits(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); } + static void setBits(uint32_t& into, uint32_t value, + uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); } + + static uint32_t getBitsRaw(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<<bitCount)-1)); } + static void setBitsRaw(uint32_t& into, uint32_t value, + uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = into; + const uint32_t mask = ((1<<bitCount)-1); + temp &= ~(mask << (32-firstBit-bitCount)); + temp |= ((value & mask) << (32-firstBit-bitCount)); + into = temp; } + enum { little_endian = 0 }; +}; + + +class LittleEndian +{ +public: + static uint16_t get16(const uint16_t& from) INLINE { return OSReadLittleInt16(&from, 0); } + static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteLittleInt16(&into, 0, value); } + + static uint32_t get32(const uint32_t& from) INLINE { return OSReadLittleInt32(&from, 0); } + static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteLittleInt32(&into, 0, value); } + + static uint64_t get64(const uint64_t& from) INLINE { return OSReadLittleInt64(&from, 0); } + static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteLittleInt64(&into, 0, value); } + + static uint32_t getBits(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); } + static void setBits(uint32_t& into, uint32_t value, + uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); } + + static uint32_t getBitsRaw(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> firstBit) & ((1<<bitCount)-1)); } + static void setBitsRaw(uint32_t& into, uint32_t value, + uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = into; + const uint32_t mask = ((1<<bitCount)-1); + temp &= ~(mask << firstBit); + temp |= ((value & mask) << firstBit); + into = temp; } + enum { little_endian = 1 }; +}; + + +template <typename _E> +class Pointer32 +{ +public: + typedef uint32_t uint_t; + typedef int32_t int_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, value); } +}; + + +template <typename _E> +class Pointer64 +{ +public: + typedef uint64_t uint_t; + typedef int64_t int_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); } +}; + + + + + + +#endif // __FILE_ABSTRACTION__ + + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/InternalMacros.h b/lldb/source/Plugins/Process/Utility/libunwind/src/InternalMacros.h new file mode 100644 index 00000000000..40483900d38 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/InternalMacros.h @@ -0,0 +1,89 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- InternalMacros.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef INTERNAL_MACROS_H +#define INTERNAL_MACROS_H + +#include <assert.h> + +#ifdef __cplusplus +extern "C" { +#endif + extern void __assert_rtn(const char *, const char *, int, const char *) __attribute__((noreturn)); +#ifdef __cplusplus +} +#endif + +#define UNW_STEP_SUCCESS 1 +#define UNW_STEP_END 0 + + +struct v128 { unsigned int vec[4]; }; + + +#define EXPORT __attribute__((visibility("default"))) + +#define COMPILE_TIME_ASSERT( expr ) \ + extern int compile_time_assert_failed[ ( expr ) ? 1 : -1 ] __attribute__( ( unused ) ); + +#define ABORT(msg) __assert_rtn(__func__, __FILE__, __LINE__, msg) + +#if NDEBUG + #define DEBUG_MESSAGE(msg, ...) + #define DEBUG_PRINT_API(msg, ...) + #define DEBUG_PRINT_UNWINDING_TEST 0 + #define DEBUG_PRINT_UNWINDING(msg, ...) + #define DEBUG_LOG_NON_ZERO(x) x; + #define INITIALIZE_DEBUG_PRINT_API + #define INITIALIZE_DEBUG_PRINT_UNWINDING +#else + #define DEBUG_MESSAGE(msg, ...) fprintf(stderr, "libuwind: " msg, __VA_ARGS__) + #ifdef __cplusplus + extern "C" { + #endif + extern bool logAPIs(); + extern bool logUnwinding(); + #ifdef __cplusplus + } + #endif + #define DEBUG_LOG_NON_ZERO(x) { int _err = x; if ( _err != 0 ) fprintf(stderr, "libuwind: " #x "=%d in %s", _err, __FUNCTION__); } + #define DEBUG_PRINT_API(msg, ...) do { if ( logAPIs() ) fprintf(stderr, msg, __VA_ARGS__); } while(0) + #define DEBUG_PRINT_UNWINDING(msg, ...) do { if ( logUnwinding() ) fprintf(stderr, msg, __VA_ARGS__); } while(0) + #define DEBUG_PRINT_UNWINDING_TEST logUnwinding() + #define INITIALIZE_DEBUG_PRINT_API bool logAPIs() { static bool log = (getenv("LIBUNWIND_PRINT_APIS") != NULL); return log; } + #define INITIALIZE_DEBUG_PRINT_UNWINDING bool logUnwinding() { static bool log = (getenv("LIBUNWIND_PRINT_UNWINDING") != NULL); return log; } +#endif + + +// note hack for <rdar://problem/6175741> +// Once libgcc_s.dylib vectors to libSystem, then we can remove the $ld$hide$os10.6$ lines +#if __ppc__ + #define NOT_HERE_BEFORE_10_6(sym) \ + extern const char sym##_tmp3 __asm("$ld$hide$os10.3$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp3 = 0; \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; + #define NEVER_HERE(sym) \ + extern const char sym##_tmp3 __asm("$ld$hide$os10.3$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp3 = 0; \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \ + extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp6 = 0; +#else + #define NOT_HERE_BEFORE_10_6(sym) \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; + #define NEVER_HERE(sym) \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \ + extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp6 = 0; +#endif + + + +#endif // INTERNAL_MACROS_H diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.hpp new file mode 100644 index 00000000000..291c72425d4 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.hpp @@ -0,0 +1,985 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- Registers.hpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// +// C++ interface to lower levels of libuwind +// + +#ifndef __REGISTERS_HPP__ +#define __REGISTERS_HPP__ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <dlfcn.h> +#include <mach-o/loader.h> +#include <mach-o/getsect.h> +#include <mach/ppc/thread_status.h> +#include <mach/i386/thread_status.h> + +#include "libunwind.h" +#include "InternalMacros.h" + +namespace lldb_private { + + +/// +/// Registers_x86 holds the register state of a thread in a 32-bit intel process. +/// +class Registers_x86 +{ +public: + Registers_x86(); + Registers_x86(const void* registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const { return false; } + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const { return false; } + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char* getRegisterName(int num); + void jumpto() {} + + uint32_t getSP() const { return fRegisters.__esp; } + void setSP(uint32_t value) { fRegisters.__esp = value; } + uint32_t getIP() const { return fRegisters.__eip; } + void setIP(uint32_t value) { fRegisters.__eip = value; } + uint32_t getEBP() const { return fRegisters.__ebp; } + void setEBP(uint32_t value) { fRegisters.__ebp = value; } + uint32_t getEBX() const { return fRegisters.__ebx; } + void setEBX(uint32_t value) { fRegisters.__ebx = value; } + uint32_t getECX() const { return fRegisters.__ecx; } + void setECX(uint32_t value) { fRegisters.__ecx = value; } + uint32_t getEDX() const { return fRegisters.__edx; } + void setEDX(uint32_t value) { fRegisters.__edx = value; } + uint32_t getESI() const { return fRegisters.__esi; } + void setESI(uint32_t value) { fRegisters.__esi = value; } + uint32_t getEDI() const { return fRegisters.__edi; } + void setEDI(uint32_t value) { fRegisters.__edi = value; } + +private: + i386_thread_state_t fRegisters; +}; + +inline Registers_x86::Registers_x86(const void* registers) +{ + COMPILE_TIME_ASSERT( sizeof(Registers_x86) < sizeof(unw_context_t) ); + fRegisters = *((i386_thread_state_t*)registers); +} + +inline Registers_x86::Registers_x86() +{ + bzero(&fRegisters, sizeof(fRegisters)); +} + + +inline bool Registers_x86::validRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return true; + if ( regNum == UNW_REG_SP ) + return true; + if ( regNum < 0 ) + return false; + if ( regNum > 7 ) + return false; + return true; +} + +inline uint32_t Registers_x86::getRegister(int regNum) const +{ + switch ( regNum ) { + case UNW_REG_IP: + return fRegisters.__eip; + case UNW_REG_SP: + return fRegisters.__esp; + case UNW_X86_EAX: + return fRegisters.__eax; + case UNW_X86_ECX: + return fRegisters.__ecx; + case UNW_X86_EDX: + return fRegisters.__edx; + case UNW_X86_EBX: + return fRegisters.__ebx; + case UNW_X86_EBP: + return fRegisters.__ebp; + case UNW_X86_ESP: + return fRegisters.__esp; + case UNW_X86_ESI: + return fRegisters.__esi; + case UNW_X86_EDI: + return fRegisters.__edi; + } + ABORT("unsupported x86 register"); +} + +inline void Registers_x86::setRegister(int regNum, uint32_t value) +{ + switch ( regNum ) { + case UNW_REG_IP: + fRegisters.__eip = value; + return; + case UNW_REG_SP: + fRegisters.__esp = value; + return; + case UNW_X86_EAX: + fRegisters.__eax = value; + return; + case UNW_X86_ECX: + fRegisters.__ecx = value; + return; + case UNW_X86_EDX: + fRegisters.__edx = value; + return; + case UNW_X86_EBX: + fRegisters.__ebx = value; + return; + case UNW_X86_EBP: + fRegisters.__ebp = value; + return; + case UNW_X86_ESP: + fRegisters.__esp = value; + return; + case UNW_X86_ESI: + fRegisters.__esi = value; + return; + case UNW_X86_EDI: + fRegisters.__edi = value; + return; + } + ABORT("unsupported x86 register"); +} + +inline const char* Registers_x86::getRegisterName(int regNum) +{ + switch ( regNum ) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "esp"; + case UNW_X86_EAX: + return "eax"; + case UNW_X86_ECX: + return "ecx"; + case UNW_X86_EDX: + return "edx"; + case UNW_X86_EBX: + return "ebx"; + case UNW_X86_EBP: + return "ebp"; + case UNW_X86_ESP: + return "esp"; + case UNW_X86_ESI: + return "esi"; + case UNW_X86_EDI: + return "edi"; + default: + return "unknown register"; + } +} + +inline double Registers_x86::getFloatRegister(int num) const +{ + ABORT("no x86 float registers"); +} + +inline void Registers_x86::setFloatRegister(int num, double value) +{ + ABORT("no x86 float registers"); +} + +inline v128 Registers_x86::getVectorRegister(int num) const +{ + ABORT("no x86 vector registers"); +} + +inline void Registers_x86::setVectorRegister(int num, v128 value) +{ + ABORT("no x86 vector registers"); +} + + + + +/// +/// Registers_x86_64 holds the register state of a thread in a 64-bit intel process. +/// +class Registers_x86_64 +{ +public: + Registers_x86_64(); + Registers_x86_64(const void* registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const{ return false; } + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const { return false; } + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char* getRegisterName(int num); + void jumpto() {} + uint64_t getSP() const { return fRegisters.__rsp; } + void setSP(uint64_t value) { fRegisters.__rsp = value; } + uint64_t getIP() const { return fRegisters.__rip; } + void setIP(uint64_t value) { fRegisters.__rip = value; } + uint64_t getRBP() const { return fRegisters.__rbp; } + void setRBP(uint64_t value) { fRegisters.__rbp = value; } + uint64_t getRBX() const { return fRegisters.__rbx; } + void setRBX(uint64_t value) { fRegisters.__rbx = value; } + uint64_t getR12() const { return fRegisters.__r12; } + void setR12(uint64_t value) { fRegisters.__r12 = value; } + uint64_t getR13() const { return fRegisters.__r13; } + void setR13(uint64_t value) { fRegisters.__r13 = value; } + uint64_t getR14() const { return fRegisters.__r14; } + void setR14(uint64_t value) { fRegisters.__r14 = value; } + uint64_t getR15() const { return fRegisters.__r15; } + void setR15(uint64_t value) { fRegisters.__r15 = value; } +private: + x86_thread_state64_t fRegisters; +}; + +inline Registers_x86_64::Registers_x86_64(const void* registers) +{ + COMPILE_TIME_ASSERT( sizeof(Registers_x86_64) < sizeof(unw_context_t) ); + fRegisters = *((x86_thread_state64_t*)registers); +} + +inline Registers_x86_64::Registers_x86_64() +{ + bzero(&fRegisters, sizeof(fRegisters)); +} + + +inline bool Registers_x86_64::validRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return true; + if ( regNum == UNW_REG_SP ) + return true; + if ( regNum < 0 ) + return false; + if ( regNum > 15 ) + return false; + return true; +} + +inline uint64_t Registers_x86_64::getRegister(int regNum) const +{ + switch ( regNum ) { + case UNW_REG_IP: + return fRegisters.__rip; + case UNW_REG_SP: + return fRegisters.__rsp; + case UNW_X86_64_RAX: + return fRegisters.__rax; + case UNW_X86_64_RDX: + return fRegisters.__rdx; + case UNW_X86_64_RCX: + return fRegisters.__rcx; + case UNW_X86_64_RBX: + return fRegisters.__rbx; + case UNW_X86_64_RSI: + return fRegisters.__rsi; + case UNW_X86_64_RDI: + return fRegisters.__rdi; + case UNW_X86_64_RBP: + return fRegisters.__rbp; + case UNW_X86_64_RSP: + return fRegisters.__rsp; + case UNW_X86_64_R8: + return fRegisters.__r8; + case UNW_X86_64_R9: + return fRegisters.__r9; + case UNW_X86_64_R10: + return fRegisters.__r10; + case UNW_X86_64_R11: + return fRegisters.__r11; + case UNW_X86_64_R12: + return fRegisters.__r12; + case UNW_X86_64_R13: + return fRegisters.__r13; + case UNW_X86_64_R14: + return fRegisters.__r14; + case UNW_X86_64_R15: + return fRegisters.__r15; + } + ABORT("unsupported x86_64 register"); +} + +inline void Registers_x86_64::setRegister(int regNum, uint64_t value) +{ + switch ( regNum ) { + case UNW_REG_IP: + fRegisters.__rip = value; + return; + case UNW_REG_SP: + fRegisters.__rsp = value; + return; + case UNW_X86_64_RAX: + fRegisters.__rax = value; + return; + case UNW_X86_64_RDX: + fRegisters.__rdx = value; + return; + case UNW_X86_64_RCX: + fRegisters.__rcx = value; + return; + case UNW_X86_64_RBX: + fRegisters.__rbx = value; + return; + case UNW_X86_64_RSI: + fRegisters.__rsi = value; + return; + case UNW_X86_64_RDI: + fRegisters.__rdi = value; + return; + case UNW_X86_64_RBP: + fRegisters.__rbp = value; + return; + case UNW_X86_64_RSP: + fRegisters.__rsp = value; + return; + case UNW_X86_64_R8: + fRegisters.__r8 = value; + return; + case UNW_X86_64_R9: + fRegisters.__r9 = value; + return; + case UNW_X86_64_R10: + fRegisters.__r10 = value; + return; + case UNW_X86_64_R11: + fRegisters.__r11 = value; + return; + case UNW_X86_64_R12: + fRegisters.__r12 = value; + return; + case UNW_X86_64_R13: + fRegisters.__r13 = value; + return; + case UNW_X86_64_R14: + fRegisters.__r14 = value; + return; + case UNW_X86_64_R15: + fRegisters.__r15 = value; + return; + } + ABORT("unsupported x86_64 register"); +} + +inline const char* Registers_x86_64::getRegisterName(int regNum) +{ + switch ( regNum ) { + case UNW_REG_IP: + return "rip"; + case UNW_REG_SP: + return "rsp"; + case UNW_X86_64_RAX: + return "rax"; + case UNW_X86_64_RDX: + return "rdx"; + case UNW_X86_64_RCX: + return "rcx"; + case UNW_X86_64_RBX: + return "rbx"; + case UNW_X86_64_RSI: + return "rsi"; + case UNW_X86_64_RDI: + return "rdi"; + case UNW_X86_64_RBP: + return "rbp"; + case UNW_X86_64_RSP: + return "rsp"; + case UNW_X86_64_R8: + return "r8"; + case UNW_X86_64_R9: + return "r9"; + case UNW_X86_64_R10: + return "r10"; + case UNW_X86_64_R11: + return "r11"; + case UNW_X86_64_R12: + return "r12"; + case UNW_X86_64_R13: + return "r13"; + case UNW_X86_64_R14: + return "r14"; + case UNW_X86_64_R15: + return "r15"; + default: + return "unknown register"; + } +} + +double Registers_x86_64::getFloatRegister(int num) const +{ + ABORT("no x86_64 float registers"); +} + +void Registers_x86_64::setFloatRegister(int num, double value) +{ + ABORT("no x86_64 float registers"); +} + +inline v128 Registers_x86_64::getVectorRegister(int num) const +{ + ABORT("no x86_64 vector registers"); +} + +inline void Registers_x86_64::setVectorRegister(int num, v128 value) +{ + ABORT("no x86_64 vector registers"); +} + + +/// +/// Registers_ppc holds the register state of a thread in a 32-bit PowerPC process. +/// +class Registers_ppc +{ +public: + Registers_ppc(); + Registers_ppc(const void* registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + void jumpto() {} + const char* getRegisterName(int num); + uint64_t getSP() const { return fRegisters.__r1; } + void setSP(uint64_t value) { fRegisters.__r1 = value; } + uint64_t getIP() const { return fRegisters.__srr0; } + void setIP(uint64_t value) { fRegisters.__srr0 = value; } +private: + ppc_thread_state_t fRegisters; + ppc_float_state_t fFloatRegisters; + v128 fVectorRegisters[32]; // offset 424 +}; + + + +inline Registers_ppc::Registers_ppc(const void* registers) +{ + COMPILE_TIME_ASSERT( sizeof(Registers_ppc) < sizeof(unw_context_t) ); + fRegisters = *((ppc_thread_state_t*)registers); + fFloatRegisters = *((ppc_float_state_t*)((char*)registers+160)); + memcpy(fVectorRegisters, ((char*)registers+424), sizeof(fVectorRegisters)); +} + +inline Registers_ppc::Registers_ppc() +{ + bzero(&fRegisters, sizeof(fRegisters)); + bzero(&fFloatRegisters, sizeof(fFloatRegisters)); + bzero(&fVectorRegisters, sizeof(fVectorRegisters)); +} + + +inline bool Registers_ppc::validRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return true; + if ( regNum == UNW_REG_SP ) + return true; + if ( regNum == UNW_PPC_VRSAVE ) + return true; + if ( regNum < 0 ) + return false; + if ( regNum <= UNW_PPC_R31 ) + return true; + if ( regNum == UNW_PPC_MQ ) + return true; + if ( regNum == UNW_PPC_LR ) + return true; + if ( regNum == UNW_PPC_CTR ) + return true; + if ( (UNW_PPC_CR0 <= regNum) && (regNum <= UNW_PPC_CR7) ) + return true; + return false; +} + + +inline uint32_t Registers_ppc::getRegister(int regNum) const +{ + switch ( regNum ) { + case UNW_REG_IP: + return fRegisters.__srr0; + case UNW_REG_SP: + return fRegisters.__r1; + case UNW_PPC_R0: + return fRegisters.__r0; + case UNW_PPC_R1: + return fRegisters.__r1; + case UNW_PPC_R2: + return fRegisters.__r2; + case UNW_PPC_R3: + return fRegisters.__r3; + case UNW_PPC_R4: + return fRegisters.__r4; + case UNW_PPC_R5: + return fRegisters.__r5; + case UNW_PPC_R6: + return fRegisters.__r6; + case UNW_PPC_R7: + return fRegisters.__r7; + case UNW_PPC_R8: + return fRegisters.__r8; + case UNW_PPC_R9: + return fRegisters.__r9; + case UNW_PPC_R10: + return fRegisters.__r10; + case UNW_PPC_R11: + return fRegisters.__r11; + case UNW_PPC_R12: + return fRegisters.__r12; + case UNW_PPC_R13: + return fRegisters.__r13; + case UNW_PPC_R14: + return fRegisters.__r14; + case UNW_PPC_R15: + return fRegisters.__r15; + case UNW_PPC_R16: + return fRegisters.__r16; + case UNW_PPC_R17: + return fRegisters.__r17; + case UNW_PPC_R18: + return fRegisters.__r18; + case UNW_PPC_R19: + return fRegisters.__r19; + case UNW_PPC_R20: + return fRegisters.__r20; + case UNW_PPC_R21: + return fRegisters.__r21; + case UNW_PPC_R22: + return fRegisters.__r22; + case UNW_PPC_R23: + return fRegisters.__r23; + case UNW_PPC_R24: + return fRegisters.__r24; + case UNW_PPC_R25: + return fRegisters.__r25; + case UNW_PPC_R26: + return fRegisters.__r26; + case UNW_PPC_R27: + return fRegisters.__r27; + case UNW_PPC_R28: + return fRegisters.__r28; + case UNW_PPC_R29: + return fRegisters.__r29; + case UNW_PPC_R30: + return fRegisters.__r30; + case UNW_PPC_R31: + return fRegisters.__r31; + case UNW_PPC_LR: + return fRegisters.__lr; + case UNW_PPC_CR0: + return (fRegisters.__cr & 0xF0000000); + case UNW_PPC_CR1: + return (fRegisters.__cr & 0x0F000000); + case UNW_PPC_CR2: + return (fRegisters.__cr & 0x00F00000); + case UNW_PPC_CR3: + return (fRegisters.__cr & 0x000F0000); + case UNW_PPC_CR4: + return (fRegisters.__cr & 0x0000F000); + case UNW_PPC_CR5: + return (fRegisters.__cr & 0x00000F00); + case UNW_PPC_CR6: + return (fRegisters.__cr & 0x000000F0); + case UNW_PPC_CR7: + return (fRegisters.__cr & 0x0000000F); + case UNW_PPC_VRSAVE: + return fRegisters.__vrsave; + } + ABORT("unsupported ppc register"); +} + + +inline void Registers_ppc::setRegister(int regNum, uint32_t value) +{ + //fprintf(stderr, "Registers_ppc::setRegister(%d, 0x%08X)\n", regNum, value); + switch ( regNum ) { + case UNW_REG_IP: + fRegisters.__srr0 = value; + return; + case UNW_REG_SP: + fRegisters.__r1 = value; + return; + case UNW_PPC_R0: + fRegisters.__r0 = value; + return; + case UNW_PPC_R1: + fRegisters.__r1 = value; + return; + case UNW_PPC_R2: + fRegisters.__r2 = value; + return; + case UNW_PPC_R3: + fRegisters.__r3 = value; + return; + case UNW_PPC_R4: + fRegisters.__r4 = value; + return; + case UNW_PPC_R5: + fRegisters.__r5 = value; + return; + case UNW_PPC_R6: + fRegisters.__r6 = value; + return; + case UNW_PPC_R7: + fRegisters.__r7 = value; + return; + case UNW_PPC_R8: + fRegisters.__r8 = value; + return; + case UNW_PPC_R9: + fRegisters.__r9 = value; + return; + case UNW_PPC_R10: + fRegisters.__r10 = value; + return; + case UNW_PPC_R11: + fRegisters.__r11 = value; + return; + case UNW_PPC_R12: + fRegisters.__r12 = value; + return; + case UNW_PPC_R13: + fRegisters.__r13 = value; + return; + case UNW_PPC_R14: + fRegisters.__r14 = value; + return; + case UNW_PPC_R15: + fRegisters.__r15 = value; + return; + case UNW_PPC_R16: + fRegisters.__r16 = value; + return; + case UNW_PPC_R17: + fRegisters.__r17 = value; + return; + case UNW_PPC_R18: + fRegisters.__r18 = value; + return; + case UNW_PPC_R19: + fRegisters.__r19 = value; + return; + case UNW_PPC_R20: + fRegisters.__r20 = value; + return; + case UNW_PPC_R21: + fRegisters.__r21 = value; + return; + case UNW_PPC_R22: + fRegisters.__r22 = value; + return; + case UNW_PPC_R23: + fRegisters.__r23 = value; + return; + case UNW_PPC_R24: + fRegisters.__r24 = value; + return; + case UNW_PPC_R25: + fRegisters.__r25 = value; + return; + case UNW_PPC_R26: + fRegisters.__r26 = value; + return; + case UNW_PPC_R27: + fRegisters.__r27 = value; + return; + case UNW_PPC_R28: + fRegisters.__r28 = value; + return; + case UNW_PPC_R29: + fRegisters.__r29 = value; + return; + case UNW_PPC_R30: + fRegisters.__r30 = value; + return; + case UNW_PPC_R31: + fRegisters.__r31 = value; + return; + case UNW_PPC_MQ: + fRegisters.__mq = value; + return; + case UNW_PPC_LR: + fRegisters.__lr = value; + return; + case UNW_PPC_CTR: + fRegisters.__ctr = value; + return; + case UNW_PPC_CR0: + fRegisters.__cr &= 0x0FFFFFFF; + fRegisters.__cr |= (value & 0xF0000000); + return; + case UNW_PPC_CR1: + fRegisters.__cr &= 0xF0FFFFFF; + fRegisters.__cr |= (value & 0x0F000000); + return; + case UNW_PPC_CR2: + fRegisters.__cr &= 0xFF0FFFFF; + fRegisters.__cr |= (value & 0x00F00000); + return; + case UNW_PPC_CR3: + fRegisters.__cr &= 0xFFF0FFFF; + fRegisters.__cr |= (value & 0x000F0000); + return; + case UNW_PPC_CR4: + fRegisters.__cr &= 0xFFFF0FFF; + fRegisters.__cr |= (value & 0x0000F000); + return; + case UNW_PPC_CR5: + fRegisters.__cr &= 0xFFFFF0FF; + fRegisters.__cr |= (value & 0x00000F00); + return; + case UNW_PPC_CR6: + fRegisters.__cr &= 0xFFFFFF0F; + fRegisters.__cr |= (value & 0x000000F0); + return; + case UNW_PPC_CR7: + fRegisters.__cr &= 0xFFFFFFF0; + fRegisters.__cr |= (value & 0x0000000F); + return; + case UNW_PPC_VRSAVE: + fRegisters.__vrsave = value; + return; + // not saved + return; + case UNW_PPC_XER: + fRegisters.__xer = value; + return; + case UNW_PPC_AP: + case UNW_PPC_VSCR: + case UNW_PPC_SPEFSCR: + // not saved + return; + } + ABORT("unsupported ppc register"); +} + +inline bool Registers_ppc::validFloatRegister(int regNum) const +{ + if ( regNum < UNW_PPC_F0 ) + return false; + if ( regNum > UNW_PPC_F31 ) + return false; + return true; +} + +inline double Registers_ppc::getFloatRegister(int regNum) const +{ + assert(validFloatRegister(regNum)); + return fFloatRegisters.__fpregs[regNum-UNW_PPC_F0]; +} + +inline void Registers_ppc::setFloatRegister(int regNum, double value) +{ + //fprintf(stderr, "Registers_ppc::setFloatRegister(%d, %g))\n", regNum, value); + assert(validFloatRegister(regNum)); + fFloatRegisters.__fpregs[regNum-UNW_PPC_F0] = value; +} + + +inline bool Registers_ppc::validVectorRegister(int regNum) const +{ + if ( regNum < UNW_PPC_V0 ) + return false; + if ( regNum > UNW_PPC_V31 ) + return false; + return true; +} + +v128 Registers_ppc::getVectorRegister(int regNum) const +{ + assert(validVectorRegister(regNum)); + v128 result = fVectorRegisters[regNum-UNW_PPC_V0]; + //fprintf(stderr, "Registers_ppc::getVectorRegister(this=%p, %d) => <0x%08X, 0x%08X, 0x%08X, 0x%08X> \n", + // this, regNum, result.vec[0], result.vec[1], result.vec[2], result.vec[3]); + return result; +} + +void Registers_ppc::setVectorRegister(int regNum, v128 value) +{ + assert(validVectorRegister(regNum)); + //fprintf(stderr, "Registers_ppc::setVectorRegister(this=%p, %d) <0x%08X, 0x%08X, 0x%08X, 0x%08X> => <0x%08X, 0x%08X, 0x%08X, 0x%08X> \n", + // this, regNum, fVectorRegisters[regNum-UNW_PPC_V0].vec[0], fVectorRegisters[regNum-UNW_PPC_V0].vec[1], fVectorRegisters[regNum-UNW_PPC_V0].vec[2], + // fVectorRegisters[regNum-UNW_PPC_V0].vec[3], value.vec[0], value.vec[1], value.vec[2], value.vec[3]); + fVectorRegisters[regNum-UNW_PPC_V0] = value; +} + + +inline const char* Registers_ppc::getRegisterName(int regNum) +{ + switch ( regNum ) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "sp"; + case UNW_PPC_R0: + return "r0"; + case UNW_PPC_R1: + return "r1"; + case UNW_PPC_R2: + return "r2"; + case UNW_PPC_R3: + return "r3"; + case UNW_PPC_R4: + return "r4"; + case UNW_PPC_R5: + return "r5"; + case UNW_PPC_R6: + return "r6"; + case UNW_PPC_R7: + return "r7"; + case UNW_PPC_R8: + return "r8"; + case UNW_PPC_R9: + return "r9"; + case UNW_PPC_R10: + return "r10"; + case UNW_PPC_R11: + return "r11"; + case UNW_PPC_R12: + return "r12"; + case UNW_PPC_R13: + return "r13"; + case UNW_PPC_R14: + return "r14"; + case UNW_PPC_R15: + return "r15"; + case UNW_PPC_R16: + return "r16"; + case UNW_PPC_R17: + return "r17"; + case UNW_PPC_R18: + return "r18"; + case UNW_PPC_R19: + return "r19"; + case UNW_PPC_R20: + return "r20"; + case UNW_PPC_R21: + return "r21"; + case UNW_PPC_R22: + return "r22"; + case UNW_PPC_R23: + return "r23"; + case UNW_PPC_R24: + return "r24"; + case UNW_PPC_R25: + return "r25"; + case UNW_PPC_R26: + return "r26"; + case UNW_PPC_R27: + return "r27"; + case UNW_PPC_R28: + return "r28"; + case UNW_PPC_R29: + return "r29"; + case UNW_PPC_R30: + return "r30"; + case UNW_PPC_R31: + return "r31"; + case UNW_PPC_F0: + return "fp0"; + case UNW_PPC_F1: + return "fp1"; + case UNW_PPC_F2: + return "fp2"; + case UNW_PPC_F3: + return "fp3"; + case UNW_PPC_F4: + return "fp4"; + case UNW_PPC_F5: + return "fp5"; + case UNW_PPC_F6: + return "fp6"; + case UNW_PPC_F7: + return "fp7"; + case UNW_PPC_F8: + return "fp8"; + case UNW_PPC_F9: + return "fp9"; + case UNW_PPC_F10: + return "fp10"; + case UNW_PPC_F11: + return "fp11"; + case UNW_PPC_F12: + return "fp12"; + case UNW_PPC_F13: + return "fp13"; + case UNW_PPC_F14: + return "fp14"; + case UNW_PPC_F15: + return "fp15"; + case UNW_PPC_F16: + return "fp16"; + case UNW_PPC_F17: + return "fp17"; + case UNW_PPC_F18: + return "fp18"; + case UNW_PPC_F19: + return "fp19"; + case UNW_PPC_F20: + return "fp20"; + case UNW_PPC_F21: + return "fp21"; + case UNW_PPC_F22: + return "fp22"; + case UNW_PPC_F23: + return "fp23"; + case UNW_PPC_F24: + return "fp24"; + case UNW_PPC_F25: + return "fp25"; + case UNW_PPC_F26: + return "fp26"; + case UNW_PPC_F27: + return "fp27"; + case UNW_PPC_F28: + return "fp28"; + case UNW_PPC_F29: + return "fp29"; + case UNW_PPC_F30: + return "fp30"; + case UNW_PPC_F31: + return "fp31"; + case UNW_PPC_LR: + return "lr"; + default: + return "unknown register"; + } + + +} + + +} // namespace lldb_private + + + +#endif // __REGISTERS_HPP__ + + + + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.s b/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.s new file mode 100644 index 00000000000..45dae3bcbfc --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.s @@ -0,0 +1,261 @@ + + +#if __i386__ + .text + .globl __ZN12lldb_private13Registers_x866jumptoEv + .private_extern __ZN12lldb_private13Registers_x866jumptoEv +__ZN12lldb_private13Registers_x866jumptoEv: +# +# void lldb_private::Registers_x86::jumpto() +# +# On entry: +# + + +# +-----------------------+ +# + thread_state pointer + +# +-----------------------+ +# + return address + +# +-----------------------+ <-- SP +# + + + movl 4(%esp), %eax + # set up eax and ret on new stack location + movl 28(%eax), %edx # edx holds new stack pointer + subl $8,%edx + movl %edx, 28(%eax) + movl 0(%eax), %ebx + movl %ebx, 0(%edx) + movl 40(%eax), %ebx + movl %ebx, 4(%edx) + # we now have ret and eax pushed onto where new stack will be + # restore all registers + movl 4(%eax), %ebx + movl 8(%eax), %ecx + movl 12(%eax), %edx + movl 16(%eax), %edi + movl 20(%eax), %esi + movl 24(%eax), %ebp + movl 28(%eax), %esp + # skip ss + # skip eflags + pop %eax # eax was already pushed on new stack + ret # eip was already pushed on new stack + # skip cs + # skip ds + # skip es + # skip fs + # skip gs + +#elif __x86_64__ + + .text + .globl __ZN12lldb_private16Registers_x86_646jumptoEv + .private_extern __ZN12lldb_private16Registers_x86_646jumptoEv +__ZN12lldb_private16Registers_x86_646jumptoEv: +# +# void lldb_private::Registers_x86_64::jumpto() +# +# On entry, thread_state pointer is in rdi + + movq 56(%rdi), %rax # rax holds new stack pointer + subq $16, %rax + movq %rax, 56(%rdi) + movq 32(%rdi), %rbx # store new rdi on new stack + movq %rbx, 0(%rax) + movq 128(%rdi), %rbx # store new rip on new stack + movq %rbx, 8(%rax) + # restore all registers + movq 0(%rdi), %rax + movq 8(%rdi), %rbx + movq 16(%rdi), %rcx + movq 24(%rdi), %rdx + # restore rdi later + movq 40(%rdi), %rsi + movq 48(%rdi), %rbp + # restore rsp later + movq 64(%rdi), %r8 + movq 72(%rdi), %r9 + movq 80(%rdi), %r10 + movq 88(%rdi), %r11 + movq 96(%rdi), %r12 + movq 104(%rdi), %r13 + movq 112(%rdi), %r14 + movq 120(%rdi), %r15 + # skip rflags + # skip cs + # skip fs + # skip gs + movq 56(%rdi), %rsp # cut back rsp to new location + pop %rdi # rdi was saved here earlier + ret # rip was saved here + + +#elif __ppc__ + + .text + .globl __ZN12lldb_private13Registers_ppc6jumptoEv + .private_extern __ZN12lldb_private13Registers_ppc6jumptoEv +__ZN12lldb_private13Registers_ppc6jumptoEv: +; +; void lldb_private::Registers_ppc::jumpto() +; +; On entry: +; thread_state pointer is in r3 +; + + ; restore integral registerrs + ; skip r0 for now + ; skip r1 for now + lwz r2, 16(r3) + ; skip r3 for now + ; skip r4 for now + ; skip r5 for now + lwz r6, 32(r3) + lwz r7, 36(r3) + lwz r8, 40(r3) + lwz r9, 44(r3) + lwz r10, 48(r3) + lwz r11, 52(r3) + lwz r12, 56(r3) + lwz r13, 60(r3) + lwz r14, 64(r3) + lwz r15, 68(r3) + lwz r16, 72(r3) + lwz r17, 76(r3) + lwz r18, 80(r3) + lwz r19, 84(r3) + lwz r20, 88(r3) + lwz r21, 92(r3) + lwz r22, 96(r3) + lwz r23,100(r3) + lwz r24,104(r3) + lwz r25,108(r3) + lwz r26,112(r3) + lwz r27,116(r3) + lwz r28,120(r3) + lwz r29,124(r3) + lwz r30,128(r3) + lwz r31,132(r3) + + ; restore float registers + lfd f0, 160(r3) + lfd f1, 168(r3) + lfd f2, 176(r3) + lfd f3, 184(r3) + lfd f4, 192(r3) + lfd f5, 200(r3) + lfd f6, 208(r3) + lfd f7, 216(r3) + lfd f8, 224(r3) + lfd f9, 232(r3) + lfd f10,240(r3) + lfd f11,248(r3) + lfd f12,256(r3) + lfd f13,264(r3) + lfd f14,272(r3) + lfd f15,280(r3) + lfd f16,288(r3) + lfd f17,296(r3) + lfd f18,304(r3) + lfd f19,312(r3) + lfd f20,320(r3) + lfd f21,328(r3) + lfd f22,336(r3) + lfd f23,344(r3) + lfd f24,352(r3) + lfd f25,360(r3) + lfd f26,368(r3) + lfd f27,376(r3) + lfd f28,384(r3) + lfd f29,392(r3) + lfd f30,400(r3) + lfd f31,408(r3) + + ; restore vector registers if any are in use + lwz r5,156(r3) ; test VRsave + cmpwi r5,0 + beq Lnovec + + subi r4,r1,16 + rlwinm r4,r4,0,0,27 ; mask low 4-bits + ; r4 is now a 16-byte aligned pointer into the red zone + ; the fVectorRegisters may not be 16-byte aligned so copy via red zone temp buffer + + +#define LOAD_VECTOR_UNALIGNEDl(_index) \ + andis. r0,r5,(1<<(15-_index)) @\ + beq Ldone ## _index @\ + lwz r0, 424+_index*16(r3) @\ + stw r0, 0(r4) @\ + lwz r0, 424+_index*16+4(r3) @\ + stw r0, 4(r4) @\ + lwz r0, 424+_index*16+8(r3) @\ + stw r0, 8(r4) @\ + lwz r0, 424+_index*16+12(r3)@\ + stw r0, 12(r4) @\ + lvx v ## _index,0,r4 @\ +Ldone ## _index: + +#define LOAD_VECTOR_UNALIGNEDh(_index) \ + andi. r0,r5,(1<<(31-_index)) @\ + beq Ldone ## _index @\ + lwz r0, 424+_index*16(r3) @\ + stw r0, 0(r4) @\ + lwz r0, 424+_index*16+4(r3) @\ + stw r0, 4(r4) @\ + lwz r0, 424+_index*16+8(r3) @\ + stw r0, 8(r4) @\ + lwz r0, 424+_index*16+12(r3)@\ + stw r0, 12(r4) @\ + lvx v ## _index,0,r4 @\ + Ldone ## _index: + + + LOAD_VECTOR_UNALIGNEDl(0) + LOAD_VECTOR_UNALIGNEDl(1) + LOAD_VECTOR_UNALIGNEDl(2) + LOAD_VECTOR_UNALIGNEDl(3) + LOAD_VECTOR_UNALIGNEDl(4) + LOAD_VECTOR_UNALIGNEDl(5) + LOAD_VECTOR_UNALIGNEDl(6) + LOAD_VECTOR_UNALIGNEDl(7) + LOAD_VECTOR_UNALIGNEDl(8) + LOAD_VECTOR_UNALIGNEDl(9) + LOAD_VECTOR_UNALIGNEDl(10) + LOAD_VECTOR_UNALIGNEDl(11) + LOAD_VECTOR_UNALIGNEDl(12) + LOAD_VECTOR_UNALIGNEDl(13) + LOAD_VECTOR_UNALIGNEDl(14) + LOAD_VECTOR_UNALIGNEDl(15) + LOAD_VECTOR_UNALIGNEDh(16) + LOAD_VECTOR_UNALIGNEDh(17) + LOAD_VECTOR_UNALIGNEDh(18) + LOAD_VECTOR_UNALIGNEDh(19) + LOAD_VECTOR_UNALIGNEDh(20) + LOAD_VECTOR_UNALIGNEDh(21) + LOAD_VECTOR_UNALIGNEDh(22) + LOAD_VECTOR_UNALIGNEDh(23) + LOAD_VECTOR_UNALIGNEDh(24) + LOAD_VECTOR_UNALIGNEDh(25) + LOAD_VECTOR_UNALIGNEDh(26) + LOAD_VECTOR_UNALIGNEDh(27) + LOAD_VECTOR_UNALIGNEDh(28) + LOAD_VECTOR_UNALIGNEDh(29) + LOAD_VECTOR_UNALIGNEDh(30) + LOAD_VECTOR_UNALIGNEDh(31) + +Lnovec: + lwz r0, 136(r3) ; __cr + mtocrf 255,r0 + lwz r0, 148(r3) ; __ctr + mtctr r0 + lwz r0, 0(r3) ; __ssr0 + mtctr r0 + lwz r0, 8(r3) ; do r0 now + lwz r5,28(r3) ; do r5 now + lwz r4,24(r3) ; do r4 now + lwz r1,12(r3) ; do sp now + lwz r3,20(r3) ; do r3 last + bctr + + +#endif + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteDebuggerDummyUnwinder.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteDebuggerDummyUnwinder.hpp new file mode 100644 index 00000000000..1db3faffd10 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteDebuggerDummyUnwinder.hpp @@ -0,0 +1,88 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- RemoteDebuggerDummyUnwinder.hpp -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Code to unwind past a debugger's dummy frame inserted when it does an +// inferior function call. +// In this case we'll need to get the saved register context from the debugger - +// it may be in the debugger's local memory or it may be saved in a nonstandard +// location in the inferior process' memory. + +#ifndef __REMOTE_DEBUGGER_DUMMY_UNWINDER_HPP__ +#define __REMOTE_DEBUGGER_DUMMY_UNWINDER_HPP__ + +#if defined (SUPPORT_REMOTE_UNWINDING) + +#include "libunwind.h" +#include "Registers.hpp" +#include "AddressSpace.hpp" +#include "RemoteRegisterMap.hpp" +#include "RemoteProcInfo.hpp" + +namespace lldb_private +{ + +template <typename A> +int stepOutOfDebuggerDummyFrame (A& addressSpace, Registers_x86_64& registers, + RemoteProcInfo *procinfo, uint64_t ip, + uint64_t sp, void* arg) +{ + Registers_x86_64 newRegisters(registers); + RemoteRegisterMap *rmap = addressSpace.getRemoteProcInfo()->getRegisterMap(); + unw_word_t regv; + for (int i = UNW_X86_64_RAX; i <= UNW_X86_64_R15; i++) { + int driver_regnum; + if (!rmap->unwind_regno_to_caller_regno (i, driver_regnum)) + continue; + if (addressSpace.accessors()->access_reg_inf_func_call (procinfo->wrap(), ip, sp, driver_regnum, ®v, 0, arg)) + newRegisters.setRegister(i, regv); + } + if (!addressSpace.accessors()->access_reg_inf_func_call (procinfo->wrap(), ip, sp, rmap->caller_regno_for_ip(), ®v, 0, arg)) + return UNW_EUNSPEC; + newRegisters.setIP (regv); + registers = newRegisters; + return UNW_STEP_SUCCESS; +} + +template <typename A> +int stepOutOfDebuggerDummyFrame (A& addressSpace, Registers_x86& registers, + RemoteProcInfo *procinfo, uint64_t ip, + uint64_t sp, void* arg) +{ + Registers_x86 newRegisters(registers); + RemoteRegisterMap *rmap = addressSpace.getRemoteProcInfo()->getRegisterMap(); + unw_word_t regv; + for (int i = UNW_X86_EAX; i <= UNW_X86_EDI; i++) { + int driver_regnum; + if (!rmap->unwind_regno_to_caller_regno (i, driver_regnum)) + continue; + if (addressSpace.accessors()->access_reg_inf_func_call (procinfo->wrap(), ip, sp, driver_regnum, ®v, 0, arg)) + newRegisters.setRegister(i, regv); + } + if (!addressSpace.accessors()->access_reg_inf_func_call (procinfo->wrap(), ip, sp, rmap->caller_regno_for_ip(), ®v, 0, arg)) + return UNW_EUNSPEC; + newRegisters.setIP (regv); + registers = newRegisters; + return UNW_STEP_SUCCESS; +} + +template <typename A> +int stepOutOfDebuggerDummyFrame (A& addressSpace, Registers_ppc& registers, + uint64_t ip, uint64_t sp) +{ + ABORT ("stepping out of a debugger dummy frame not supported on ppc"); + return UNW_EUNSPEC; +} + +}; // namespace lldb_private + +#endif // SUPPORT_REMOTE_UNWINDING + +#endif // __REMOTE_DEBUGGER_DUMMY_UNWINDER_HPP__ + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteProcInfo.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteProcInfo.hpp new file mode 100644 index 00000000000..3640dc6195c --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteProcInfo.hpp @@ -0,0 +1,977 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- RemoteProcInfo.hpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This file defines the primary object created when unw_create_addr_space() +// is called. This object tracks the list of known images in memory +// (dylibs, bundles, etc), it maintains a link to a RemoteRegisterMap for this +// architecture, it caches the remote process memory in a local store and all +// read/writes are filtered through its accessors which will use the memory +// caches. It maintains a logging level set by the driver program and puts +// timing/debug messages out on a FILE* provided to it. + +// RemoteProcInfo is not specific to any particular unwind so it does not +// maintain an "arg" argument (an opaque pointer that the driver program uses +// to track the process/thread being unwound). + +#ifndef __REMOTE_PROC_INFO_HPP__ +#define __REMOTE_PROC_INFO_HPP__ + +#if defined (SUPPORT_REMOTE_UNWINDING) + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <dlfcn.h> +#include <stdarg.h> +#include <sys/time.h> +#include <mach-o/loader.h> +#include <mach-o/getsect.h> +#include <mach/ppc/thread_status.h> +#include <mach/i386/thread_status.h> +#include <Availability.h> + +#include <map> +#include <vector> +#include <algorithm> + +#include "FileAbstraction.hpp" +#include "libunwind.h" +#include "InternalMacros.h" +#include "dwarf2.h" +#include "RemoteUnwindProfile.h" +#include "Registers.hpp" +#include "RemoteRegisterMap.hpp" + +namespace lldb_private +{ +class RemoteProcInfo; + +/// +/// unw_addr_space_remote is the concrete instance that a unw_addr_space_t points to when examining +/// a remote process. +/// +struct unw_addr_space_remote +{ + enum unw_as_type type; // should always be UNW_REMOTE + RemoteProcInfo* ras; +}; + +class RemoteMemoryBlob +{ +public: + typedef void (*free_callback_with_arg)(void *, void*); + typedef void (*free_callback)(void *); + + /* This object is constructed with a callback to free the memory; + that callback takes a pointer to the memory region and optionally + takes an additional argument -- the "void* arg" passed around for + remote unwinds, in case the driver program allocated this e.g. with + mach_vm_read, and needs the token to vm_deallocate it. */ + + RemoteMemoryBlob (uint8_t *buf, free_callback_with_arg to_free, + uint64_t startaddr, uint64_t len, uint64_t mh, void *arg) : + fBuf(buf), fToFreeWithArg(to_free), fToFree(NULL), + fStartAddr(startaddr), fLen(len), fMachHeader(mh), + fArg(arg) { } + RemoteMemoryBlob (uint8_t *buf, free_callback to_free, uint64_t startaddr, + uint64_t len, uint64_t mh, void *arg) : + fBuf(buf), fToFree(to_free), fToFreeWithArg(NULL), + fStartAddr(startaddr), fLen(len), fMachHeader(mh), + fArg(NULL) { } + + // the following is to create a dummy RMB object for lower_bound's use in + // searching. + RemoteMemoryBlob (uint64_t startaddr) : fStartAddr(startaddr), fToFree(NULL), + fBuf(NULL), fToFreeWithArg(NULL), fArg(NULL), fMachHeader(-1), + fLen(0) { } + ~RemoteMemoryBlob () { + if (fToFreeWithArg) + fToFreeWithArg(fBuf, fArg); + else if (fToFree) + fToFree(fBuf); + } + bool contains_addr (uint64_t addr) { + if (fStartAddr <= addr && addr < fStartAddr + fLen) + return true; + else + return false; + } + uint8_t *get_blob_range (uint64_t remote_process_addr, int len) { + if (this->contains_addr (remote_process_addr) == false) + return NULL; + if (this->contains_addr (remote_process_addr + len) == false) + return NULL; + return fBuf + (remote_process_addr - fStartAddr); + } + uint64_t getMh () const { return fMachHeader; } + uint64_t getStartAddr() const { return fStartAddr; } + uint64_t getLength() const { return fLen; } +private: + uint8_t *fBuf; + free_callback fToFree; + free_callback_with_arg fToFreeWithArg; + uint64_t fStartAddr; + uint64_t fLen; + uint64_t fMachHeader; + void *fArg; +}; + +inline bool operator<(const RemoteMemoryBlob &b1, const RemoteMemoryBlob &b2) { + if (b1.getStartAddr() < b2.getStartAddr()) + return true; + else + return false; +} + +// One of these for each image in memory (executable, dylib, bundle, etc) + +struct RemoteImageEntry +{ + RemoteImageEntry () : mach_header(0), text_start(0), text_end(0), eh_frame_start(0), eh_frame_len(0), compact_unwind_info_start(0), compact_unwind_info_len(0) { } + ~RemoteImageEntry () { + std::map<uint64_t, RemoteUnwindProfile *>::iterator i; + for (i = profiles.begin(); i != profiles.end(); ++i) + delete i->second; + } + uint64_t mach_header; + uint64_t text_start; + uint64_t text_end; + uint64_t eh_frame_start; + uint64_t eh_frame_len; + uint64_t compact_unwind_info_start; + uint64_t compact_unwind_info_len; + + // unwind profiles created for thsi binary image so far, + // key is the start address of the profile. + std::map<uint64_t, RemoteUnwindProfile *> profiles; + + // a list of function address bounds for this binary image - + // end addresses should be accurate and not inferred from potentially + // incomplete start-address data (e.g. nlist records). + std::vector<FuncBounds> func_bounds; +}; + +class RemoteImages +{ +public: + RemoteImages (unw_targettype_t targarch) : fTargetArch(targarch) { } + ~RemoteImages (); + void removeAllImageProfiles(); + void removeOneImageProfiles(uint64_t mh); + RemoteImageEntry *remoteEntryForTextAddr (uint64_t pc); + bool addFuncBounds (uint64_t mh, std::vector<FuncBounds> &startAddrs); + bool haveFuncBounds (uint64_t mh); + bool findFuncBounds (uint32_t pc, uint32_t &startAddr, uint32_t &endAddr); + bool findFuncBounds (uint64_t pc, uint64_t &startAddr, uint64_t &endAddr); + void addImage (uint64_t mh, uint64_t text_start, uint64_t text_end, uint64_t eh_frame, uint64_t eh_frame_len, uint64_t compact_unwind_start, uint64_t compact_unwind_len); + bool addProfile (RemoteProcInfo* procinfo, unw_accessors_t *acc, unw_addr_space_t as, uint64_t start, uint64_t end, void *arg); + RemoteUnwindProfile* findProfileByTextAddr (uint64_t pc); + void addMemBlob (RemoteMemoryBlob *blob); + uint8_t *getMemBlobMemory (uint64_t addr, int len); +private: + RemoteImages(); + std::map<uint64_t, RemoteImageEntry> fImages; + std::vector<RemoteMemoryBlob *> fMemBlobs; + unw_targettype_t fTargetArch; +}; + +RemoteImages::~RemoteImages () { + std::map<uint64_t, std::vector<RemoteMemoryBlob *> >::iterator i; + std::vector<RemoteMemoryBlob *>::iterator j; + for (j = fMemBlobs.begin(); j != fMemBlobs.end(); ++j) { + delete *j; + } + fMemBlobs.erase(fMemBlobs.begin(), fMemBlobs.end()); +} + +void RemoteImages::removeAllImageProfiles() { + fImages.erase(fImages.begin(), fImages.end()); + std::vector<RemoteMemoryBlob *>::iterator j; + for (j = fMemBlobs.begin(); j != fMemBlobs.end(); ++j) + delete *j; + fMemBlobs.erase(fMemBlobs.begin(), fMemBlobs.end()); +} + +void RemoteImages::removeOneImageProfiles(uint64_t mh) { + std::map<uint64_t, RemoteImageEntry>::iterator i; + i = fImages.find(mh); + if (i != fImages.end()) + fImages.erase(i); + + std::vector<RemoteMemoryBlob *>::iterator j; + for (j = fMemBlobs.begin(); j != fMemBlobs.end(); ++j) { + if ((*j)->getMh() == mh) { + delete *j; + break; + } + } + if (j != fMemBlobs.end()) + fMemBlobs.erase(j); +} + +RemoteImageEntry *RemoteImages::remoteEntryForTextAddr (uint64_t pc) { + std::map<uint64_t, RemoteImageEntry>::iterator i = fImages.lower_bound (pc); + if (i == fImages.begin() && i == fImages.end()) + return NULL; + if (i == fImages.end()) { + --i; + } else { + if (i != fImages.begin() && i->first != pc) + --i; + } + if (i->second.text_start <= pc && i->second.text_end > pc) + { + return &(i->second); + } + else + { + return NULL; + } +} + +bool RemoteImages::addFuncBounds (uint64_t mh, std::vector<FuncBounds> &startAddrs) { + RemoteImageEntry *img = NULL; + std::map<uint64_t, RemoteImageEntry>::iterator i = fImages.find (mh); + if (i == fImages.end()) + return false; + img = &i->second; + img->func_bounds = startAddrs; + std::sort(img->func_bounds.begin(), img->func_bounds.end()); + return true; +} + +bool RemoteImages::haveFuncBounds (uint64_t mh) { + RemoteImageEntry *img = NULL; + std::map<uint64_t, RemoteImageEntry>::iterator i = fImages.find (mh); + if (i == fImages.end()) + return false; + img = &i->second; + if (img->func_bounds.size() > 0) + return true; + return false; +} + +bool RemoteImages::findFuncBounds (uint64_t pc, uint64_t &startAddr, uint64_t &endAddr) { + RemoteImageEntry *img = NULL; + startAddr = endAddr = 0; + std::map<uint64_t, RemoteImageEntry>::iterator i = fImages.lower_bound (pc); + if (i == fImages.begin() && i == fImages.end()) + return false; + if (i == fImages.end()) { + --i; + } else { + if (i != fImages.begin() && i->first != pc) + --i; + } + if (i->second.text_start <= pc && i->second.text_end > pc) + { + img = &i->second; + } + else + return false; + std::vector<FuncBounds>::iterator j; + j = std::lower_bound(img->func_bounds.begin(), img->func_bounds.end(), FuncBounds (pc, pc)); + if (j == img->func_bounds.begin() && j == img->func_bounds.end()) + return false; + if (j == img->func_bounds.end()) { + --j; + } else { + if (j != img->func_bounds.begin() && j->fStart != pc) + --j; + } + if (j->fStart <= pc && j->fEnd > pc) { + startAddr = j->fStart; + endAddr = j->fEnd; + return true; + } + return false; +} + +// Add 32-bit version of findFuncBounds so we can avoid templatizing all of these functions +// just to handle 64 and 32 bit unwinds. + +bool RemoteImages::findFuncBounds (uint32_t pc, uint32_t &startAddr, uint32_t &endAddr) { + uint64_t big_startAddr = startAddr; + uint64_t big_endAddr = endAddr; + bool ret; + ret = findFuncBounds (pc, big_startAddr, big_endAddr); + startAddr = (uint32_t) big_startAddr & 0xffffffff; + endAddr = (uint32_t) big_endAddr & 0xffffffff; + return ret; +} + +// Make sure we don't cache the same memory range more than once +// I'm not checking the length of the blobs to check for overlap - +// as this is used today, the only duplication will be with the same +// start address. + +void RemoteImages::addMemBlob (RemoteMemoryBlob *blob) { + std::vector<RemoteMemoryBlob *>::iterator i; + for (i = fMemBlobs.begin(); i != fMemBlobs.end(); ++i) { + if (blob->getStartAddr() == (*i)->getStartAddr()) + return; + } + fMemBlobs.push_back(blob); + std::sort(fMemBlobs.begin(), fMemBlobs.end()); +} + +uint8_t *RemoteImages::getMemBlobMemory (uint64_t addr, int len) { + uint8_t *res = NULL; + std::vector<RemoteMemoryBlob *>::iterator j; + RemoteMemoryBlob *searchobj = new RemoteMemoryBlob(addr); + j = std::lower_bound (fMemBlobs.begin(), fMemBlobs.end(), searchobj); + delete searchobj; + if (j == fMemBlobs.end() && j == fMemBlobs.begin()) + return NULL; + if (j == fMemBlobs.end()) { + --j; + } else { + if (j != fMemBlobs.begin() && (*j)->getStartAddr() != addr) + --j; + } + res = (*j)->get_blob_range (addr, len); + if (res != NULL) + return res; + for (j = fMemBlobs.begin(); j != fMemBlobs.end(); ++j) { + res = (*j)->get_blob_range (addr, len); + if (res != NULL) + break; + } + return res; +} + +void RemoteImages::addImage (uint64_t mh, uint64_t text_start, + uint64_t text_end, uint64_t eh_frame, + uint64_t eh_frame_len, + uint64_t compact_unwind_start, + uint64_t compact_unwind_len) { + struct RemoteImageEntry img; + img.mach_header = mh; + img.text_start = text_start; + img.text_end = text_end; + img.eh_frame_start = eh_frame; + img.eh_frame_len = eh_frame_len; + img.compact_unwind_info_start = compact_unwind_start; + img.compact_unwind_info_len = compact_unwind_len; + fImages[mh] = img; +} + +// The binary image for this start/end address must already be present +bool RemoteImages::addProfile (RemoteProcInfo* procinfo, unw_accessors_t *acc, unw_addr_space_t as, uint64_t start, uint64_t end, void *arg) { + RemoteImageEntry *img = NULL; + std::map<uint64_t, RemoteImageEntry>::iterator i = fImages.lower_bound (start); + if (i == fImages.begin() && i == fImages.end()) + return false; + if (i == fImages.end()) { + --i; + } else { + if (i != fImages.begin() && i->first != start) { + --i; + } + } + if (i->second.text_start <= start && i->second.text_end > start) + { + img = &i->second; + } + else + return false; + RemoteUnwindProfile* profile = new RemoteUnwindProfile; + if (AssemblyParse (procinfo, acc, as, start, end, *profile, arg)) { + img->profiles[start] = profile; + return true; + } + return false; +} + +RemoteUnwindProfile* RemoteImages::findProfileByTextAddr (uint64_t pc) { + RemoteImageEntry *img = NULL; + std::map<uint64_t, RemoteImageEntry>::iterator i = fImages.lower_bound (pc); + if (i == fImages.begin() && i == fImages.end()) + return NULL; + if (i == fImages.end()) { + --i; + } else { + if (i != fImages.begin() && i->first != pc) + --i; + } + if (i->second.text_start <= pc && i->second.text_end > pc) + { + img = &i->second; + } + else + return false; + std::map<uint64_t, RemoteUnwindProfile *>::iterator j; + j = img->profiles.lower_bound (pc); + if (j == img->profiles.begin() && j == img->profiles.end()) + return NULL; + if (j == img->profiles.end()) { + --j; + } else { + if (j != img->profiles.begin() && j->first != pc) + --j; + } + if (j->second->fStart <= pc && j->second->fEnd > pc) + { + return j->second; + } + return NULL; +} + +/// +/// RemoteProcInfo is used as a template parameter to UnwindCursor when +/// unwinding a thread that has a custom set of accessors. It calls the +/// custom accessors for all data. +/// +class RemoteProcInfo +{ +public: + +// libunwind documentation specifies that unw_create_addr_space defaults to +// UNW_CACHE_NONE but that's going to work very poorly for us so we're +// defaulting to UNW_CACHE_GLOBAL. + + RemoteProcInfo(unw_accessors_t* accessors, unw_targettype_t targarch) : + fAccessors(*accessors), fCachingPolicy(UNW_CACHE_GLOBAL), + fTargetArch(targarch), fImages(targarch), fLogging(NULL), + fLogLevel(UNW_LOG_LEVEL_NONE) + { + fWrapper.type = UNW_REMOTE; + fWrapper.ras = this; + fRemoteRegisterMap = new RemoteRegisterMap(accessors, targarch); + if (fTargetArch == UNW_TARGET_X86_64 || fTargetArch == UNW_TARGET_I386 + || fTargetArch == UNW_TARGET_ARM) + fLittleEndian = true; + else + fLittleEndian = false; + } + + ~RemoteProcInfo () { + delete fRemoteRegisterMap; + } + + bool haveProfile (uint64_t pc) { + if (fImages.findProfileByTextAddr (pc)) + return true; + else + return false; + } + + // returns NULL if profile does not yet exist. + RemoteUnwindProfile* findProfile (uint64_t pc) { + return fImages.findProfileByTextAddr (pc); + } + + // returns NULL if the binary image is not yet added. + bool addProfile (unw_accessors_t *acc, unw_addr_space_t as, uint64_t start, uint64_t end, void *arg) { + if (fImages.addProfile (this, acc, as, start, end, arg)) + return true; + else + return false; + } + + bool haveImageEntry (uint64_t pc, void *arg); + + bool getImageAddresses (uint64_t pc, uint64_t &mh, uint64_t &text_start, uint64_t &text_end, + uint64_t &eh_frame_start, uint64_t &eh_frame_len, uint64_t &compact_unwind_start, + void *arg); + bool getImageAddresses (uint64_t pc, uint32_t &mh, uint32_t &text_start, uint32_t &text_end, + uint32_t &eh_frame_start, uint32_t &eh_frame_len, uint32_t &compact_unwind_start, + void *arg); + + bool addFuncBounds (uint64_t mh, std::vector<FuncBounds> &startAddrs) { return fImages.addFuncBounds (mh, startAddrs); } + bool haveFuncBounds (uint64_t mh) { return fImages.haveFuncBounds (mh); } + bool findStartAddr (uint64_t pc, uint32_t &startAddr, uint32_t &endAddr) { return fImages.findFuncBounds (pc, startAddr, endAddr); } + bool findStartAddr (uint64_t pc, uint64_t &startAddr, uint64_t &endAddr) { return fImages.findFuncBounds (pc, startAddr, endAddr); } + uint8_t *getMemBlobMemory (uint64_t addr, int len) { return fImages.getMemBlobMemory (addr, len); } + + + // Functions to pull memory from the target into the debugger. + + int getBytes(uint64_t addr, uint64_t extent, uint8_t* buf, void* arg) + { + int err = readRaw(addr, extent, buf, arg); + + if(err) + return 0; + + return 1; + } + +#define DECLARE_INT_ACCESSOR(bits) \ + uint##bits##_t get##bits(uint64_t addr, void* arg) \ + { \ + uint##bits##_t ret; \ + int err = readRaw(addr, (unw_word_t)(bits / 8), (uint8_t*)&ret, arg); \ + \ + if(err) \ + ABORT("Invalid memory access in the target"); \ + \ + return ret; \ + } + DECLARE_INT_ACCESSOR(8) + DECLARE_INT_ACCESSOR(16) + DECLARE_INT_ACCESSOR(32) + DECLARE_INT_ACCESSOR(64) +#undef DECLARE_INT_ACCESSOR + +// 'err' is set to 0 if there were no errors reading this +// memory. Non-zero values indicate that the memory was not +// read successfully. This method should be preferred over the +// method above which asserts on failure. + +#define DECLARE_INT_ACCESSOR_ERR(bits) \ + uint##bits##_t get##bits(uint64_t addr, int &err, void* arg) \ + { \ + uint##bits##_t ret; \ + err = readRaw(addr, (unw_word_t)(bits / 8), (uint8_t*)&ret, arg); \ + \ + return ret; \ + } + DECLARE_INT_ACCESSOR_ERR(8) + DECLARE_INT_ACCESSOR_ERR(16) + DECLARE_INT_ACCESSOR_ERR(32) + DECLARE_INT_ACCESSOR_ERR(64) +#undef DECLARE_INT_ACCESSOR_ERR + + double getDouble(uint64_t addr, void* arg) + { + double ret; + int err = readRaw(addr, (unw_word_t)(sizeof(ret) / 8), (uint8_t*)&ret, arg); + if(err) + ABORT("Invalid memory access in the target"); + return ret; + } + + v128 getVector(uint64_t addr, void* arg) + { + v128 ret; + int err = readRaw(addr, (unw_word_t)(sizeof(ret) / 8), (uint8_t*)&ret, arg); + if(err) + ABORT("Invalid memory access in the target"); + return ret; + } + + // Pull an unsigned LEB128 from the target into the debugger as a uint64_t. + uint64_t getULEB128(uint64_t& addr, uint64_t end, void* arg) + { + uint64_t lAddr = addr; + uint64_t ret = 0; + uint8_t shift = 0; + uint64_t byte; + do { + if(lAddr == end) + ABORT("Truncated LEB128 number in the target"); + + byte = (uint64_t)get8(lAddr, arg); + lAddr++; + + if(((shift == 63) && (byte > 0x01)) || (shift > 63)) + ABORT("LEB128 number is larger than is locally representible"); + + ret |= ((byte & 0x7f) << shift); + shift += 7; + } while((byte & 0x80) == 0x80); + addr = lAddr; + return ret; + } + + // Pull an unsigned LEB128 from the target into the debugger as a uint64_t. + uint64_t getULEB128(uint32_t& addr, uint32_t end, void* arg) + { + uint32_t lAddr = addr; + uint64_t ret = 0; + uint8_t shift = 0; + uint64_t byte; + do { + if(lAddr == end) + ABORT("Truncated LEB128 number in the target"); + + byte = (uint64_t)get8(lAddr, arg); + lAddr++; + + if(((shift == 63) && (byte > 0x01)) || (shift > 63)) + ABORT("LEB128 number is larger than is locally representible"); + + ret |= ((byte & 0x7f) << shift); + shift += 7; + } while((byte & 0x80) == 0x80); + addr = lAddr; + return ret; + } + + + // Pull a signed LEB128 from the target into the debugger as a uint64_t. + int64_t getSLEB128(uint64_t& addr, uint64_t end, void* arg) + { + uint64_t lAddr = addr; + uint64_t ret = 0; + uint8_t shift = 0; + uint64_t byte; + do { + if(lAddr == end) + ABORT("Truncated LEB128 number in the target"); + byte = (uint64_t)get8(lAddr, arg); + lAddr++; + if(((shift == 63) && (byte > 0x01)) || (shift > 63)) + ABORT("LEB128 number is larger than is locally representible"); + ret |= ((byte & 0x7f) << shift); + shift += 7; + } while((byte & 0x80) == 0x80); + // Sign-extend + if((shift < (sizeof(int64_t) * 8)) && (byte & 0x40)) + ret |= -(1 << shift); + addr = lAddr; + return ret; + } + + // Pull a signed LEB128 from the target into the debugger as a uint64_t. + int64_t getSLEB128(uint32_t& addr, uint32_t end, void* arg) + { + uint32_t lAddr = addr; + uint64_t ret = 0; + uint8_t shift = 0; + uint64_t byte; + do { + if(lAddr == end) + ABORT("Truncated LEB128 number in the target"); + byte = (uint64_t)get8(lAddr, arg); + lAddr++; + if(((shift == 63) && (byte > 0x01)) || (shift > 63)) + ABORT("LEB128 number is larger than is locally representible"); + ret |= ((byte & 0x7f) << shift); + shift += 7; + } while((byte & 0x80) == 0x80); + // Sign-extend + if((shift < (sizeof(int64_t) * 8)) && (byte & 0x40)) + ret |= -(1 << shift); + addr = lAddr; + return ret; + } + + + uint64_t getP (uint64_t addr, void *arg) { + switch (fTargetArch) { + case UNW_TARGET_X86_64: + return get64(addr, arg); + break; + case UNW_TARGET_I386: + return get32(addr, arg); + break; + } + ABORT("Unknown target architecture."); + return 0; + } + + uint64_t getP (uint64_t addr, int& err, void *arg) { + switch (fTargetArch) { + case UNW_TARGET_X86_64: + return get64(addr, err, arg); + break; + case UNW_TARGET_I386: + return get32(addr, err, arg); + break; + } + ABORT("Unknown target architecture."); + return 0; + } + + bool findFunctionName(uint64_t addr, char *buf, size_t bufLen, unw_word_t *offset, void* arg); + bool findFunctionBounds(uint64_t addr, uint64_t& low, uint64_t& high, void* arg); + int setCachingPolicy(unw_caching_policy_t policy); + + void setLoggingLevel(FILE *f, unw_log_level_t level); + void logInfo(const char *fmt, ...); + void logAPI(const char *fmt, ...); + void logVerbose(const char *fmt, ...); + void logDebug(const char *fmt, ...); + struct timeval *timestamp_start (); + void timestamp_stop (struct timeval *tstart, const char *fmt, ...); + + void flushAllCaches() { fImages.removeAllImageProfiles(); } + void flushCacheByMachHeader(uint64_t mh) { fImages.removeOneImageProfiles(mh); } + unw_targettype_t getTargetArch() { return fTargetArch; } + unw_accessors_t* getAccessors () { return &fAccessors; } + RemoteRegisterMap* getRegisterMap() { return fRemoteRegisterMap; } + unw_addr_space_t wrap () { return (unw_addr_space_t) &fWrapper; } + bool remoteIsLittleEndian () { return fLittleEndian; } + unw_log_level_t getDebugLoggingLevel() { return fLogLevel; } + void addMemBlob (RemoteMemoryBlob *blob) { fImages.addMemBlob(blob); } + unw_caching_policy_t getCachingPolicy() { return fCachingPolicy; } + +private: + int readRaw(uint64_t addr, uint64_t extent, uint8_t *valp, void* arg) + { + uint8_t *t = this->getMemBlobMemory (addr, extent); + if (t) { + memcpy (valp, t, extent); + return 0; + } + return fAccessors.access_raw((unw_addr_space_t)this, addr, extent, valp, 0, arg); + } + + struct unw_addr_space_remote fWrapper; + unw_accessors_t fAccessors; + unw_caching_policy_t fCachingPolicy; + unw_targettype_t fTargetArch; + unw_addr_space_t fAddrSpace; + RemoteImages fImages; + RemoteRegisterMap *fRemoteRegisterMap; + FILE *fLogging; + unw_log_level_t fLogLevel; + bool fLittleEndian; +}; + +// Find an image containing the given pc, returns false if absent and +// we can't add it via the accessors. +bool RemoteProcInfo::haveImageEntry (uint64_t pc, void *arg) { + if (fImages.remoteEntryForTextAddr (pc) == NULL) { + unw_word_t mh, text_start, text_end, eh_frame, eh_frame_len, compact_unwind, compact_unwind_len; + if (fAccessors.find_image_info (wrap(), pc, &mh, &text_start, + &text_end, &eh_frame, &eh_frame_len, &compact_unwind, &compact_unwind_len, arg) == UNW_ESUCCESS) { + fImages.addImage (mh, text_start, text_end, eh_frame, eh_frame_len, compact_unwind, compact_unwind_len); + if (fCachingPolicy != UNW_CACHE_NONE) { + if (compact_unwind_len != 0) { + logVerbose ("Creating RemoteMemoryBlob of compact unwind info image at mh 0x%llx, %lld bytes", mh, (uint64_t) compact_unwind_len); + uint8_t *buf = (uint8_t*) malloc (compact_unwind_len); + if (this->getBytes (compact_unwind, compact_unwind_len, buf, arg)) { + RemoteMemoryBlob *b = new RemoteMemoryBlob(buf, free, compact_unwind, compact_unwind_len, mh, NULL); + fImages.addMemBlob (b); + } + } else if (eh_frame_len != 0) { + logVerbose ("Creating RemoteMemoryBlob of eh_frame for image at mh 0x%llx, %lld bytes", mh, (uint64_t) compact_unwind_len); + uint8_t *buf = (uint8_t*) malloc (eh_frame_len); + if (this->getBytes (eh_frame, eh_frame_len, buf, arg)) { + RemoteMemoryBlob *b = new RemoteMemoryBlob(buf, free, eh_frame, eh_frame_len, mh, NULL); + fImages.addMemBlob (b); + } + } + } + } else { + return false; /// find_image_info failed + } + } else { + return true; + } + return true; +} + +bool RemoteProcInfo::getImageAddresses (uint64_t pc, uint64_t &mh, uint64_t &text_start, uint64_t &text_end, + uint64_t &eh_frame_start, uint64_t &eh_frame_len, uint64_t &compact_unwind_start, + void *arg) { + // Make sure we have this RemoteImageEntry already - fetch it now if needed. + if (haveImageEntry (pc, arg) == false) { + return false; + } + RemoteImageEntry *r = fImages.remoteEntryForTextAddr (pc); + if (r) { + mh = r->mach_header; + text_start = r->text_start; + text_end = r->text_end; + eh_frame_start = r->eh_frame_start; + eh_frame_len = r->eh_frame_len; + compact_unwind_start = r->compact_unwind_info_start; + return true; + } + return false; +} + + +bool RemoteProcInfo::findFunctionName(uint64_t addr, char *buf, size_t bufLen, unw_word_t *offset, void* arg) +{ + if(fAccessors.get_proc_name(wrap(), addr, buf, bufLen, offset, arg) == UNW_ESUCCESS) + return true; + else + return false; +} + +bool RemoteProcInfo::findFunctionBounds(uint64_t addr, uint64_t& low, uint64_t& high, void* arg) +{ + if (fAccessors.get_proc_bounds(wrap(), addr, &low, &high, arg) == UNW_ESUCCESS + && high != 0) + return true; + else + return false; +} + +int RemoteProcInfo::setCachingPolicy(unw_caching_policy_t policy) +{ + if(policy == UNW_CACHE_NONE && fCachingPolicy != UNW_CACHE_NONE) + { + flushAllCaches(); + } + + if(!(policy == UNW_CACHE_NONE || policy == UNW_CACHE_GLOBAL || policy == UNW_CACHE_PER_THREAD)) + return UNW_EINVAL; + + fCachingPolicy = policy; + + return UNW_ESUCCESS; +} + +void RemoteProcInfo::setLoggingLevel(FILE *f, unw_log_level_t level) +{ + fLogLevel = level; + fLogging = f; +} + +void RemoteProcInfo::logInfo(const char *fmt, ...) +{ + if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE) + return; + if (fLogLevel & UNW_LOG_LEVEL_INFO) { + va_list ap; + va_start (ap, fmt); + vfprintf (fLogging, fmt, ap); + fputs ("\n", fLogging); + va_end (ap); + } +} + +void RemoteProcInfo::logAPI(const char *fmt, ...) +{ + if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE) + return; + if (fLogLevel & UNW_LOG_LEVEL_API) { + va_list ap; + va_start (ap, fmt); + vfprintf (fLogging, fmt, ap); + fputs ("\n", fLogging); + va_end (ap); + } +} + +void RemoteProcInfo::logVerbose(const char *fmt, ...) +{ + if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE) + return; + if (fLogLevel & UNW_LOG_LEVEL_VERBOSE) { + va_list ap; + va_start (ap, fmt); + vfprintf (fLogging, fmt, ap); + fputs ("\n", fLogging); + va_end (ap); + } +} + +void RemoteProcInfo::logDebug(const char *fmt, ...) +{ + if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE) + return; + if (fLogLevel & UNW_LOG_LEVEL_DEBUG) { + va_list ap; + va_start (ap, fmt); + vfprintf (fLogging, fmt, ap); + fputs ("\n", fLogging); + va_end (ap); + } +} + +struct timeval *RemoteProcInfo::timestamp_start () +{ + if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE) + return NULL; + if (fLogLevel & UNW_LOG_LEVEL_TIMINGS) { + struct timeval *t = (struct timeval *) malloc (sizeof (struct timeval)); + if (gettimeofday (t, NULL) != 0) { + free (t); + return NULL; + } + return t; + } + return NULL; +} + +void RemoteProcInfo::timestamp_stop (struct timeval *tstart, const char *fmt, ...) +{ + if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE || tstart == NULL) + return; + if (fLogLevel & UNW_LOG_LEVEL_TIMINGS) { + struct timeval tend; + if (gettimeofday (&tend, NULL) != 0) { + free (tstart); + return; + } + struct timeval result; + timersub (&tend, tstart, &result); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + printf (" duration %0.5fs\n", (double) ((result.tv_sec * 1000000) + result.tv_usec) / 1000000.0); + va_end (ap); + free (tstart); + } +} + + +// Initialize the register context at the start of a remote unwind. + +void getRemoteContext (RemoteProcInfo* procinfo, Registers_x86_64& r, void *arg) { + unw_accessors_t* accessors = procinfo->getAccessors(); + unw_addr_space_t addrSpace = procinfo->wrap(); + RemoteRegisterMap* regmap = procinfo->getRegisterMap(); + uint64_t rv; + + // now that we have a selected process/thread, ask about the valid registers. + regmap->scan_caller_regs (addrSpace, arg); + +#define FILLREG(reg) {int caller_reg; regmap->unwind_regno_to_caller_regno ((reg), caller_reg); accessors->access_reg (addrSpace, caller_reg, &rv, 0, arg); r.setRegister ((reg), rv);} + FILLREG (UNW_X86_64_RAX); + FILLREG (UNW_X86_64_RDX); + FILLREG (UNW_X86_64_RCX); + FILLREG (UNW_X86_64_RBX); + FILLREG (UNW_X86_64_RSI); + FILLREG (UNW_X86_64_RDI); + FILLREG (UNW_X86_64_RBP); + FILLREG (UNW_X86_64_RSP); + FILLREG (UNW_X86_64_R8); + FILLREG (UNW_X86_64_R9); + FILLREG (UNW_X86_64_R10); + FILLREG (UNW_X86_64_R11); + FILLREG (UNW_X86_64_R12); + FILLREG (UNW_X86_64_R13); + FILLREG (UNW_X86_64_R14); + FILLREG (UNW_X86_64_R15); + FILLREG (UNW_REG_IP); +#undef FILLREG +} + +void getRemoteContext (RemoteProcInfo* procinfo, Registers_x86& r, void *arg) { + unw_accessors_t* accessors = procinfo->getAccessors(); + unw_addr_space_t addrSpace = procinfo->wrap(); + RemoteRegisterMap* regmap = procinfo->getRegisterMap(); + uint64_t rv; + + // now that we have a selected process/thread, ask about the valid registers. + regmap->scan_caller_regs (addrSpace, arg); + +#define FILLREG(reg) {int caller_reg; regmap->unwind_regno_to_caller_regno ((reg), caller_reg); accessors->access_reg (addrSpace, caller_reg, &rv, 0, arg); r.setRegister ((reg), rv);} + FILLREG (UNW_X86_EAX); + FILLREG (UNW_X86_ECX); + FILLREG (UNW_X86_EDX); + FILLREG (UNW_X86_EBX); + FILLREG (UNW_X86_EBP); + FILLREG (UNW_X86_ESP); + FILLREG (UNW_X86_ESI); + FILLREG (UNW_X86_EDI); + FILLREG (UNW_REG_IP); +#undef FILLREG +} + + +void getRemoteContext (RemoteProcInfo* procinfo, Registers_ppc& r, void *arg) { + ABORT("ppc get remote context not implemented."); +} + +}; // namespace lldb_private + + + +#endif // SUPPORT_REMOTE_UNWINDING +#endif // __REMOTE_PROC_INFO_HPP__ diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteRegisterMap.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteRegisterMap.hpp new file mode 100644 index 00000000000..19caae9a3f4 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteRegisterMap.hpp @@ -0,0 +1,405 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- RemoteRegisterMap.hpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Provide conversions between reigster names, the libunwind internal enums, +// and the register numbers the program calling libunwind are using. + +#ifndef __REMOTE_REGISTER_MAP_HPP__ +#define __REMOTE_REGISTER_MAP_HPP__ + +#if defined (SUPPORT_REMOTE_UNWINDING) + +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif + +#include "libunwind.h" +#include <vector> + +namespace lldb_private +{ +class RemoteRegisterMap { +public: + RemoteRegisterMap (unw_accessors_t *accessors, unw_targettype_t target); + ~RemoteRegisterMap (); + void initialize_x86_64 (); + void initialize_i386 (); + bool name_to_caller_regno (const char *name, int& callerr); + bool name_to_unwind_regno (const char *name, int& unwindr); + bool unwind_regno_to_caller_regno (int unwindr, int& callerr); + bool nonvolatile_reg_p (int unwind_regno); + bool argument_regnum_p (int unwind_regno); + const char *ip_register_name(); + const char *sp_register_name(); + int caller_regno_for_ip (); + int caller_regno_for_sp (); + int unwind_regno_for_frame_pointer (); + int unwind_regno_for_stack_pointer (); + int wordsize () { return fWordSize; } + void scan_caller_regs (unw_addr_space_t as, void *arg); + + bool unwind_regno_to_machine_regno (int unwindr, int& machiner); + bool machine_regno_to_unwind_regno (int machr, int& unwindr); + bool caller_regno_to_unwind_regno (int callerr, int& unwindr); + const char* unwind_regno_to_name (int unwindr); + int byte_size_for_regtype (unw_regtype_t type); + +private: + + // A structure that collects everything we need to know about a + // given register in one place. + struct reg { + int unwind_regno; // What libunwind-remote uses internally + int caller_regno; // What the libunwind-remote driver program uses + int eh_frame_regno; // What the eh_frame section uses + int machine_regno; // What the actual bits/bytes are in instructions + char *name; + unw_regtype_t type; + reg () : unwind_regno(-1), machine_regno(-1), caller_regno(-1), + eh_frame_regno(-1), name(NULL), type(UNW_NOT_A_REG) { } + }; + + unw_accessors_t fAccessors; + unw_targettype_t fTarget; + std::vector<RemoteRegisterMap::reg> fRegMap; + int fWordSize; +}; + +void RemoteRegisterMap::initialize_x86_64 () { +#define DEFREG(ureg, ehno, machno, regn) {RemoteRegisterMap::reg r; r.unwind_regno = ureg; r.name = regn; r.eh_frame_regno = ehno; r.machine_regno = machno; r.type = UNW_INTEGER_REG; fRegMap.push_back(r); } + DEFREG (UNW_X86_64_RAX, 0, 0, strdup ("rax")); + DEFREG (UNW_X86_64_RDX, 1, 2, strdup ("rdx")); + DEFREG (UNW_X86_64_RCX, 2, 1, strdup ("rcx")); + DEFREG (UNW_X86_64_RBX, 3, 3, strdup ("rbx")); + DEFREG (UNW_X86_64_RSI, 4, 6, strdup ("rsi")); + DEFREG (UNW_X86_64_RDI, 5, 7, strdup ("rdi")); + DEFREG (UNW_X86_64_RBP, 6, 5, strdup ("rbp")); + DEFREG (UNW_X86_64_RSP, 7, 4, strdup ("rsp")); + DEFREG (UNW_X86_64_R8, 8, 8, strdup ("r8")); + DEFREG (UNW_X86_64_R9, 9, 9, strdup ("r9")); + DEFREG (UNW_X86_64_R10, 10, 10, strdup ("r10")); + DEFREG (UNW_X86_64_R11, 11, 11, strdup ("r11")); + DEFREG (UNW_X86_64_R12, 12, 12, strdup ("r12")); + DEFREG (UNW_X86_64_R13, 13, 13, strdup ("r13")); + DEFREG (UNW_X86_64_R14, 14, 14, strdup ("r14")); + DEFREG (UNW_X86_64_R15, 15, 15, strdup ("r15")); +#undef DEFREG + RemoteRegisterMap::reg r; + r.name = strdup ("rip"); + r.type = UNW_INTEGER_REG; + r.eh_frame_regno = 16; + fRegMap.push_back(r); +} + +void RemoteRegisterMap::initialize_i386 () { +#define DEFREG(ureg, ehno, machno, regn) {RemoteRegisterMap::reg r; r.unwind_regno = ureg; r.name = regn; r.eh_frame_regno = ehno; r.machine_regno = machno; r.type = UNW_INTEGER_REG; fRegMap.push_back(r); } + DEFREG (UNW_X86_EAX, 0, 0, strdup ("eax")); + DEFREG (UNW_X86_ECX, 1, 1, strdup ("ecx")); + DEFREG (UNW_X86_EDX, 2, 2, strdup ("edx")); + DEFREG (UNW_X86_EBX, 3, 3, strdup ("ebx")); + // i386 EH frame info has the next two swapped, + // v. gcc/config/i386/darwin.h:DWARF2_FRAME_REG_OUT. + DEFREG (UNW_X86_EBP, 4, 5, strdup ("ebp")); + DEFREG (UNW_X86_ESP, 5, 4, strdup ("esp")); + DEFREG (UNW_X86_ESI, 6, 6, strdup ("esi")); + DEFREG (UNW_X86_EDI, 7, 7, strdup ("edi")); +#undef DEFREG + RemoteRegisterMap::reg r; + r.name = strdup ("eip"); + r.type = UNW_INTEGER_REG; + r.eh_frame_regno = 8; + fRegMap.push_back(r); +} + + +RemoteRegisterMap::RemoteRegisterMap (unw_accessors_t *accessors, unw_targettype_t target) { + fAccessors = *accessors; + fTarget = target; + switch (target) { + case UNW_TARGET_X86_64: + this->initialize_x86_64(); + fWordSize = 8; + break; + case UNW_TARGET_I386: + this->initialize_i386(); + fWordSize = 4; + break; + default: + ABORT("RemoteRegisterMap called with unknown target"); + } +} + +RemoteRegisterMap::~RemoteRegisterMap () { + std::vector<RemoteRegisterMap::reg>::iterator j; + for (j = fRegMap.begin(); j != fRegMap.end(); ++j) + free (j->name); +} + +bool RemoteRegisterMap::name_to_caller_regno (const char *name, int& callerr) { + if (name == NULL) + return false; + for (std::vector<RemoteRegisterMap::reg>::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j) + if (strcasecmp (j->name, name) == 0) { + callerr = j->caller_regno; + return true; + } + return false; +} + +bool RemoteRegisterMap::unwind_regno_to_caller_regno (int unwindr, int& callerr) { + if (unwindr == UNW_REG_IP) { + callerr = this->caller_regno_for_ip (); + return true; + } + if (unwindr == UNW_REG_SP) { + callerr = this->caller_regno_for_sp (); + return true; + } + for (std::vector<RemoteRegisterMap::reg>::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j) + if (j->unwind_regno == unwindr && j->caller_regno != -1) { + callerr = j->caller_regno; + return true; + } + return false; +} + +bool RemoteRegisterMap::nonvolatile_reg_p (int unwind_regno) { + if (fTarget == UNW_TARGET_X86_64) { + switch (unwind_regno) { + case UNW_X86_64_RBX: + case UNW_X86_64_RSP: + case UNW_X86_64_RBP: // not actually a nonvolatile but often treated as such by convention + case UNW_X86_64_R12: + case UNW_X86_64_R13: + case UNW_X86_64_R14: + case UNW_X86_64_R15: + case UNW_REG_IP: + case UNW_REG_SP: + return true; + break; + default: + return false; + } + } + if (fTarget == UNW_TARGET_I386) { + switch (unwind_regno) { + case UNW_X86_EBX: + case UNW_X86_EBP: // not actually a nonvolatile but often treated as such by convention + case UNW_X86_ESI: + case UNW_X86_EDI: + case UNW_X86_ESP: + case UNW_REG_IP: + case UNW_REG_SP: + return true; + break; + default: + return false; + } + } + return false; +} + + +bool RemoteRegisterMap::argument_regnum_p (int unwind_regno) { + if (fTarget == UNW_TARGET_X86_64) { + switch (unwind_regno) { + case UNW_X86_64_RDI: /* arg 1 */ + case UNW_X86_64_RSI: /* arg 2 */ + case UNW_X86_64_RDX: /* arg 3 */ + case UNW_X86_64_RCX: /* arg 4 */ + case UNW_X86_64_R8: /* arg 5 */ + case UNW_X86_64_R9: /* arg 6 */ + return true; + break; + default: + return false; + } + } + return false; +} + +const char *RemoteRegisterMap::ip_register_name () { + switch (fTarget) { + case UNW_TARGET_X86_64: + return "rip"; + case UNW_TARGET_I386: + return "eip"; + default: + ABORT("unsupported architecture"); + } + return NULL; +} + +const char *RemoteRegisterMap::sp_register_name () { + switch (fTarget) { + case UNW_TARGET_X86_64: + return "rsp"; + case UNW_TARGET_I386: + return "esp"; + default: + ABORT("unsupported architecture"); + } + return NULL; +} + +int RemoteRegisterMap::caller_regno_for_ip () { + int callerr; + if (this->name_to_caller_regno (this->ip_register_name(), callerr)) + return callerr; + return -1; +} + +int RemoteRegisterMap::caller_regno_for_sp () { + int callerr; + if (this->name_to_caller_regno (this->sp_register_name(), callerr)) + return callerr; + return -1; +} + +int RemoteRegisterMap::unwind_regno_for_frame_pointer () { + switch (fTarget) { + case UNW_TARGET_X86_64: + return UNW_X86_64_RBP; + case UNW_TARGET_I386: + return UNW_X86_EBP; + default: + ABORT("cannot be reached"); + } + return -1; +} + +int RemoteRegisterMap::unwind_regno_for_stack_pointer () { + switch (fTarget) { + case UNW_TARGET_X86_64: + return UNW_X86_64_RSP; + case UNW_TARGET_I386: + return UNW_X86_ESP; + default: + ABORT("cannot be reached"); + } + return -1; +} + +// This call requires a "arg" which specifies a given process/thread to +// complete unlike the rest of the RegisterMap functions. Ideally this +// would be in the ctor but the register map is created when an +// AddressSpace is created and we don't have a process/thread yet. + +void RemoteRegisterMap::scan_caller_regs (unw_addr_space_t as, void *arg) { + for (int i = 0; i < 256; i++) { + unw_regtype_t type; + char namebuf[16]; + if (fAccessors.reg_info (as, i, &type, namebuf, sizeof (namebuf), arg) == UNW_ESUCCESS + && type != UNW_NOT_A_REG) { + std::vector<RemoteRegisterMap::reg>::iterator j; + for (j = fRegMap.begin(); j != fRegMap.end(); ++j) { + if (strcasecmp (j->name, namebuf) == 0) { + j->caller_regno = i; + // if we haven't picked up a reg type yet it will be UNW_NOT_A_REG via the ctor + if (j->type == UNW_NOT_A_REG) + j->type = type; + if (j->type != type) { + ABORT("Caller and libunwind disagree about type of register"); + break; + } + } + } + // caller knows about a register we don't have a libunwind entry for + if (j == fRegMap.end()) { + RemoteRegisterMap::reg r; + r.name = strdup (namebuf); + r.caller_regno = i; + r.type = type; + fRegMap.push_back(r); + } + } + } +} + + +bool RemoteRegisterMap::name_to_unwind_regno (const char *name, int& unwindr) { + if (name == NULL) + return false; + for (std::vector<RemoteRegisterMap::reg>::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j) + if (strcasecmp (j->name, name) == 0) { + unwindr = j->unwind_regno; + return true; + } + return false; +} + +bool RemoteRegisterMap::unwind_regno_to_machine_regno (int unwindr, int& machiner) { + if (unwindr == UNW_REG_IP) + unwindr = this->caller_regno_for_ip (); + if (unwindr == UNW_REG_SP) + unwindr = this->caller_regno_for_sp (); + for (std::vector<RemoteRegisterMap::reg>::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j) + if (j->unwind_regno == unwindr && j->machine_regno != -1) { + machiner = j->machine_regno; + return true; + } + return false; +} +bool RemoteRegisterMap::machine_regno_to_unwind_regno (int machr, int& unwindr) { + for (std::vector<RemoteRegisterMap::reg>::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j) + if (j->machine_regno == machr && j->unwind_regno != -1) { + unwindr = j->unwind_regno; + return true; + } + return false; +} +bool RemoteRegisterMap::caller_regno_to_unwind_regno (int callerr, int& unwindr) { + if (this->caller_regno_for_ip() == callerr) { + unwindr = UNW_REG_IP; + return true; + } + if (this->caller_regno_for_sp() == callerr) { + unwindr = UNW_REG_SP; + return true; + } + for (std::vector<RemoteRegisterMap::reg>::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j) + if (j->caller_regno == callerr && j->unwind_regno != -1) { + unwindr = j->unwind_regno; + return true; + } + return false; +} + +const char* RemoteRegisterMap::unwind_regno_to_name (int unwindr) { + for (std::vector<RemoteRegisterMap::reg>::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j) + if (j->unwind_regno == unwindr && j->name != NULL) { + return j->name; + } + return NULL; +} + +int RemoteRegisterMap::byte_size_for_regtype (unw_regtype_t type) { + switch (type) { + case UNW_TARGET_X86_64: + case UNW_TARGET_I386: + if (type == UNW_INTEGER_REG) return fWordSize; + if (type == UNW_FLOATING_POINT_REG) return 8; + if (type == UNW_VECTOR_REG) return 16; + default: + ABORT("cannot be reached"); + } + return -1; +} + + +}; // namespace lldb_private + +#endif // SUPPORT_REMOTE_UNWINDING + +#endif // __REMOTE_REGISTER_MAP_HPP__ + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteUnwindProfile.h b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteUnwindProfile.h new file mode 100644 index 00000000000..b03551cd21c --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteUnwindProfile.h @@ -0,0 +1,85 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- RemoteUnwindProfile.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __UNWIND_PROFILE_H__ +#define __UNWIND_PROFILE_H__ +#if defined (SUPPORT_REMOTE_UNWINDING) + +#include <vector> + +// The architecture-independent profile of a function's prologue + +namespace lldb_private +{ + +class RemoteUnwindProfile { +public: + RemoteUnwindProfile () : fRegistersSaved(32, 0), fRegSizes(10, 0) { } + struct CFALocation { + int regno; + int offset; + }; + enum RegisterSavedWhere { kRegisterOffsetFromCFA, kRegisterIsCFA }; + enum RegisterType { kGeneralPurposeRegister = 0, kFloatingPointRegister, kVectorRegister }; + struct SavedReg { + int regno; + RegisterSavedWhere location; + int64_t value; + int adj; // Used in kRegisterInRegister e.g. when we recover the caller's rsp by + // taking the contents of rbp and subtracting 16. + RegisterType type; + }; + // In the following maps the key is the address after which this change has effect. + // + // 0 push %rbp + // 1 mov %rsp, %rbp + // 2 sub $16, %rsp + // + // At saved_registers<2> we'll find the record stating that rsp is now stored in rbp. + + std::map<uint64_t, CFALocation> cfa; + std::map<uint64_t, std::vector<SavedReg> > saved_registers; + + struct CFALocation initial_cfa; // At entry to the function + + std::vector<uint8_t> fRegistersSaved; + std::vector<uint8_t> fRegSizes; + SavedReg returnAddress; + uint64_t fStart, fEnd; // low and high pc values for this function. + // END is the addr of the first insn outside the function. + uint64_t fFirstInsnPastPrologue; +}; + +class RemoteProcInfo; + +bool AssemblyParse (RemoteProcInfo *procinfo, unw_accessors_t *as, unw_addr_space_t as, uint64_t start, uint64_t end, RemoteUnwindProfile &profile, void *arg); + + +class FuncBounds { + public: + FuncBounds (uint64_t low, uint64_t high) : fStart(low), fEnd(high) { } + uint64_t fStart; + uint64_t fEnd; +}; + +inline bool operator<(const FuncBounds &ap1, const FuncBounds &ap2) { + if (ap1.fStart < ap2.fStart) + return true; + if (ap1.fStart == ap2.fStart && ap1.fEnd < ap2.fEnd) + return true; + return false; +} + + +}; +#endif + + +#endif // __UNWIND_PROFILE_H__ diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/Unwind-sjlj.c b/lldb/source/Plugins/Process/Utility/libunwind/src/Unwind-sjlj.c new file mode 100644 index 00000000000..584528353a4 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/Unwind-sjlj.c @@ -0,0 +1,466 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- Unwind-sjlj.c -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/* + * + * Implements setjump-longjump based C++ exceptions + * + */ + +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <pthread.h> +#include <setjmp.h> + +#include "unwind.h" +#include "InternalMacros.h" + +// +// ARM uses setjump/longjump based C++ exceptions. +// Other architectures use "zero cost" exceptions. +// +// With SJLJ based exceptions any function that has a catch clause or needs to do any clean up when +// an exception propagates through it, needs to call _Unwind_SjLj_Register() at the start of the +// function and _Unwind_SjLj_Unregister() at the end. The register function is called with the +// address of a block of memory in the function's stack frame. The runtime keeps a linked list +// (stack) of these blocks - one per thread. The calling function also sets the personality +// and lsda fields of the block. +// +// +#if __arm__ + +struct _Unwind_FunctionContext +{ + // next function in stack of handlers + struct _Unwind_FunctionContext* prev; + + // set by calling function before registering to be the landing pad + uintptr_t resumeLocation; + + // set by personality handler to be parameters passed to landing pad function + uintptr_t resumeParameters[4]; + + // set by calling function before registering + __personality_routine personality; // arm offset=24 + uintptr_t lsda; // arm offset=28 + + // variable length array, contains registers to restore + // 0 = r7, 1 = pc, 2 = sp + void* jbuf[]; +}; + + +#if FOR_DYLD + // implemented in dyld + extern struct _Unwind_FunctionContext* __Unwind_SjLj_GetTopOfFunctionStack(); + extern void __Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext* fc); +#else + static pthread_key_t sPerThreadTopOfFunctionStack = 0; + static pthread_once_t sOnceFlag = PTHREAD_ONCE_INIT; + + static void __Unwind_SjLj_MakeTopOfFunctionStackKey() + { + pthread_key_create(&sPerThreadTopOfFunctionStack, NULL); + } + + static struct _Unwind_FunctionContext* __Unwind_SjLj_GetTopOfFunctionStack() + { + pthread_once(&sOnceFlag, __Unwind_SjLj_MakeTopOfFunctionStackKey); + return (struct _Unwind_FunctionContext*)pthread_getspecific(sPerThreadTopOfFunctionStack); + } + + static void __Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext* fc) + { + pthread_once(&sOnceFlag, __Unwind_SjLj_MakeTopOfFunctionStackKey); + pthread_setspecific(sPerThreadTopOfFunctionStack, fc); + } +#endif + + +// +// Called at start of each function that catches exceptions +// +EXPORT void _Unwind_SjLj_Register(struct _Unwind_FunctionContext* fc) +{ + fc->prev = __Unwind_SjLj_GetTopOfFunctionStack(); + __Unwind_SjLj_SetTopOfFunctionStack(fc); +} + + +// +// Called at end of each function that catches exceptions +// +EXPORT void _Unwind_SjLj_Unregister(struct _Unwind_FunctionContext* fc) +{ + __Unwind_SjLj_SetTopOfFunctionStack(fc->prev); +} + + +static _Unwind_Reason_Code unwind_phase1(struct _Unwind_Exception* exception_object) +{ + _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack(); + DEBUG_PRINT_UNWINDING("unwind_phase1: initial function-context=%p\n", c); + + // walk each frame looking for a place to stop + for (bool handlerNotFound = true; handlerNotFound; c = c->prev) { + + // check for no more frames + if ( c == NULL ) { + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): reached bottom => _URC_END_OF_STACK\n", exception_object); + return _URC_END_OF_STACK; + } + + DEBUG_PRINT_UNWINDING("unwind_phase1: function-context=%p\n", c); + // if there is a personality routine, ask it if it will want to stop at this frame + if ( c->personality != NULL ) { + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): calling personality function %p\n", exception_object, c->personality); + _Unwind_Reason_Code personalityResult = (*c->personality)(1, _UA_SEARCH_PHASE, + exception_object->exception_class, exception_object, + (struct _Unwind_Context*)c); + switch ( personalityResult ) { + case _URC_HANDLER_FOUND: + // found a catch clause or locals that need destructing in this frame + // stop search and remember function context + handlerNotFound = false; + exception_object->private_2 = (uintptr_t)c; + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_HANDLER_FOUND\n", exception_object); + return _URC_NO_REASON; + + case _URC_CONTINUE_UNWIND: + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object); + // continue unwinding + break; + + default: + // something went wrong + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR\n", exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + } + } + return _URC_NO_REASON; +} + + +static _Unwind_Reason_Code unwind_phase2(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p)\n", exception_object); + + // walk each frame until we reach where search phase said to stop + _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack(); + while ( true ) { + DEBUG_PRINT_UNWINDING("unwind_phase2s(ex_ojb=%p): function-context=%p\n", exception_object, c); + + // check for no more frames + if ( c == NULL ) { + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached bottom => _URC_END_OF_STACK\n", exception_object); + return _URC_END_OF_STACK; + } + + // if there is a personality routine, tell it we are unwinding + if ( c->personality != NULL ) { + _Unwind_Action action = _UA_CLEANUP_PHASE; + if ( (uintptr_t)c == exception_object->private_2 ) + action = (_Unwind_Action)(_UA_CLEANUP_PHASE|_UA_HANDLER_FRAME); // tell personality this was the frame it marked in phase 1 + _Unwind_Reason_Code personalityResult = (*c->personality)(1, action, + exception_object->exception_class, exception_object, + (struct _Unwind_Context*)c); + switch ( personalityResult ) { + case _URC_CONTINUE_UNWIND: + // continue unwinding + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object); + if ( (uintptr_t)c == exception_object->private_2 ) { + // phase 1 said we would stop at this frame, but we did not... + ABORT("during phase1 personality function said it would stop here, but now if phase2 it did not stop here"); + } + break; + case _URC_INSTALL_CONTEXT: + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT, will resume at landing pad %p\n", exception_object, c->jbuf[1]); + // personality routine says to transfer control to landing pad + // we may get control back if landing pad calls _Unwind_Resume() + __Unwind_SjLj_SetTopOfFunctionStack(c); + __builtin_longjmp(c->jbuf, 1); + // unw_resume() only returns if there was an error + return _URC_FATAL_PHASE2_ERROR; + default: + // something went wrong + DEBUG_MESSAGE("personality function returned unknown result %d", personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + c = c->prev; + } + + // clean up phase did not resume at the frame that the search phase said it would + return _URC_FATAL_PHASE2_ERROR; +} + + +static _Unwind_Reason_Code unwind_phase2_forced(struct _Unwind_Exception* exception_object, + _Unwind_Stop_Fn stop, void* stop_parameter) +{ + // walk each frame until we reach where search phase said to stop + _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack(); + while ( true ) { + + // get next frame (skip over first which is _Unwind_RaiseException) + if ( c == NULL ) { + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached bottom => _URC_END_OF_STACK\n", exception_object); + return _URC_END_OF_STACK; + } + + // call stop function at each frame + _Unwind_Action action = (_Unwind_Action)(_UA_FORCE_UNWIND|_UA_CLEANUP_PHASE); + _Unwind_Reason_Code stopResult = (*stop)(1, action, + exception_object->exception_class, exception_object, + (struct _Unwind_Context*)c, stop_parameter); + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): stop function returned %d\n", exception_object, stopResult); + if ( stopResult != _URC_NO_REASON ) { + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): stopped by stop function\n", exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // if there is a personality routine, tell it we are unwinding + if ( c->personality != NULL ) { + __personality_routine p = (__personality_routine)c->personality; + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling personality function %p\n", exception_object, p); + _Unwind_Reason_Code personalityResult = (*p)(1, action, + exception_object->exception_class, exception_object, + (struct _Unwind_Context*)c); + switch ( personalityResult ) { + case _URC_CONTINUE_UNWIND: + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned _URC_CONTINUE_UNWIND\n", exception_object); + // destructors called, continue unwinding + break; + case _URC_INSTALL_CONTEXT: + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned _URC_INSTALL_CONTEXT\n", exception_object); + // we may get control back if landing pad calls _Unwind_Resume() + __Unwind_SjLj_SetTopOfFunctionStack(c); + __builtin_longjmp(c->jbuf, 1); + break; + default: + // something went wrong + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned %d, _URC_FATAL_PHASE2_ERROR\n", + exception_object, personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + c = c->prev; + } + + // call stop function one last time and tell it we've reached the end of the stack + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop function with _UA_END_OF_STACK\n", exception_object); + _Unwind_Action lastAction = (_Unwind_Action)(_UA_FORCE_UNWIND|_UA_CLEANUP_PHASE|_UA_END_OF_STACK); + (*stop)(1, lastAction, exception_object->exception_class, exception_object, (struct _Unwind_Context*)c, stop_parameter); + + // clean up phase did not resume at the frame that the search phase said it would + return _URC_FATAL_PHASE2_ERROR; +} + + +// +// Called by __cxa_throw. Only returns if there is a fatal error +// +EXPORT _Unwind_Reason_Code _Unwind_SjLj_RaiseException(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_API("_Unwind_SjLj_RaiseException(ex_obj=%p)\n", exception_object); + + // mark that this is a non-forced unwind, so _Unwind_Resume() can do the right thing + exception_object->private_1 = 0; + exception_object->private_2 = 0; + + // phase 1: the search phase + _Unwind_Reason_Code phase1 = unwind_phase1(exception_object); + if ( phase1 != _URC_NO_REASON ) + return phase1; + + // phase 2: the clean up phase + return unwind_phase2(exception_object); +} + + +// +// When _Unwind_RaiseException() is in phase2, it hands control +// to the personality function at each frame. The personality +// may force a jump to a landing pad in that function, the landing +// pad code may then call _Unwind_Resume() to continue with the +// unwinding. Note: the call to _Unwind_Resume() is from compiler +// geneated user code. All other _Unwind_* routines are called +// by the C++ runtime __cxa_* routines. +// +// Re-throwing an exception is implemented by having the code call +// __cxa_rethrow() which in turn calls _Unwind_Resume_or_Rethrow() +// +EXPORT void _Unwind_SjLj_Resume(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_API("_Unwind_SjLj_Resume(ex_obj=%p)\n", exception_object); + + if ( exception_object->private_1 != 0 ) + unwind_phase2_forced(exception_object, (_Unwind_Stop_Fn)exception_object->private_1, (void*)exception_object->private_2); + else + unwind_phase2(exception_object); + + // clients assume _Unwind_Resume() does not return, so all we can do is abort. + ABORT("_Unwind_SjLj_Resume() can't return"); +} + + +// +// Called by __cxa_rethrow() +// +EXPORT _Unwind_Reason_Code _Unwind_SjLj_Resume_or_Rethrow(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_API("__Unwind_SjLj_Resume_or_Rethrow(ex_obj=%p), private_1=%ld\n", exception_object, exception_object->private_1); + // if this is non-forced and a stopping place was found, then this is a re-throw + // call _Unwind_RaiseException() as if this was a new exception + if ( exception_object->private_1 == 0 ) + _Unwind_SjLj_RaiseException(exception_object); + + // call through to _Unwind_Resume() which distiguishes between forced and regular exceptions + _Unwind_SjLj_Resume(exception_object); + ABORT("__Unwind_SjLj_Resume_or_Rethrow() called _Unwind_SjLj_Resume() which unexpectedly returned"); +} + + +// +// Called by personality handler during phase 2 to get LSDA for current frame +// +EXPORT uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context* context) +{ + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context; + DEBUG_PRINT_API("_Unwind_GetLanguageSpecificData(context=%p) => 0x%0lX\n", context, ufc->lsda); + return ufc->lsda; +} + + +// +// Called by personality handler during phase 2 to get register values +// +EXPORT uintptr_t _Unwind_GetGR(struct _Unwind_Context* context, int index) +{ + DEBUG_PRINT_API("_Unwind_GetGR(context=%p, reg=%d)\n", context, index); + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context; + return ufc->resumeParameters[index]; +} + + + +// +// Called by personality handler during phase 2 to alter register values +// +EXPORT void _Unwind_SetGR(struct _Unwind_Context* context, int index, uintptr_t new_value) +{ + DEBUG_PRINT_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%0lX)\n", context, index, new_value); + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context; + ufc->resumeParameters[index] = new_value; +} + + +// +// Called by personality handler during phase 2 to get instruction pointer +// +EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context* context) +{ + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context; + DEBUG_PRINT_API("_Unwind_GetIP(context=%p) => 0x%lX\n", context, ufc->resumeLocation+1); + return ufc->resumeLocation+1; +} + +// +// Called by personality handler during phase 2 to get instruction pointer +// ipBefore is a boolean that says if IP is already adjusted to be the call +// site address. Normally IP is the return address. +// +EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context* context, int* ipBefore) +{ + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context; + *ipBefore = 0; + DEBUG_PRINT_API("_Unwind_GetIPInfo(context=%p, %p) => 0x%lX\n", context, ipBefore, ufc->resumeLocation+1); + return ufc->resumeLocation+1; +} + + +// +// Called by personality handler during phase 2 to alter instruction pointer +// +EXPORT void _Unwind_SetIP(struct _Unwind_Context* context, uintptr_t new_value) +{ + DEBUG_PRINT_API("_Unwind_SetIP(context=%p, value=0x%0lX)\n", context, new_value); + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context; + ufc->resumeLocation = new_value-1; +} + +// +// Called by personality handler during phase 2 to find the start of the function +// +EXPORT uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context* context) +{ + // Not supported or needed for sjlj based unwinding + DEBUG_PRINT_API("_Unwind_GetRegionStart(context=%p)\n", context); + return 0; +} + +// +// Called by personality handler during phase 2 if a foreign exception is caught +// +EXPORT void _Unwind_DeleteException(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_API("_Unwind_DeleteException(ex_obj=%p)\n", exception_object); + if ( exception_object->exception_cleanup != NULL ) + (*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, exception_object); +} + + +// +// Called by personality handler during phase 2 to get base address for data relative encodings +// +EXPORT uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context* context) +{ + // Not supported or needed for sjlj based unwinding + DEBUG_PRINT_API("_Unwind_GetDataRelBase(context=%p)\n", context); + ABORT("_Unwind_GetDataRelBase() not implemented"); +} + + +// +// Called by personality handler during phase 2 to get base address for text relative encodings +// +EXPORT uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context* context) +{ + // Not supported or needed for sjlj based unwinding + DEBUG_PRINT_API("_Unwind_GetTextRelBase(context=%p)\n", context); + ABORT("_Unwind_GetTextRelBase() not implemented"); +} + + + +// +// Called by personality handler to get Call Frame Area for current frame +// +EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context* context) +{ + DEBUG_PRINT_API("_Unwind_GetCFA(context=%p)\n", context); + if ( context != NULL ) { + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context; + // setjmp/longjmp based exceptions don't have a true CFA + // the SP in the jmpbuf is the closest approximation + return (uintptr_t)ufc->jbuf[2]; + } + return 0; +} + + + + + +#endif // __arm__ diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindCursor.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindCursor.hpp new file mode 100644 index 00000000000..e940b8b8bf1 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindCursor.hpp @@ -0,0 +1,1307 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- UnwindCursor.hpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// +// C++ interface to lower levels of libuwind +// + +#ifndef __UNWINDCURSOR_HPP__ +#define __UNWINDCURSOR_HPP__ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <stdarg.h> + +#include "libunwind.h" + +#include "AddressSpace.hpp" +#include "Registers.hpp" +#include "DwarfInstructions.hpp" + +#include "AssemblyParser.hpp" +#include "AssemblyInstructions.hpp" +#include "RemoteProcInfo.hpp" +#include "ArchDefaultUnwinder.hpp" +#include "RemoteDebuggerDummyUnwinder.hpp" + +#include "CompactUnwinder.hpp" +#include "InternalMacros.h" + +// private keymgr stuff +#define KEYMGR_GCC3_DW2_OBJ_LIST 302 +extern "C" { + extern void _keymgr_set_and_unlock_processwide_ptr(int key, void* ptr); + extern void* _keymgr_get_and_lock_processwide_ptr(int key); +}; + +// undocumented libgcc "struct object" +struct libgcc_object +{ + void* start; + void* unused1; + void* unused2; + void* fde; + unsigned long encoding; + void* fde_end; + libgcc_object* next; +}; + +// undocumented libgcc "struct km_object_info" referenced by KEYMGR_GCC3_DW2_OBJ_LIST +struct libgcc_object_info { + struct libgcc_object* seen_objects; + struct libgcc_object* unseen_objects; + unsigned spare[2]; +}; + + + + +namespace lldb_private { + +#if !FOR_DYLD +template <typename A> +class DwarfFDECache +{ +public: + typedef typename A::pint_t pint_t; + static pint_t findFDE(pint_t mh, pint_t pc); + static void add(pint_t mh, pint_t ip_start, pint_t ip_end, pint_t fde); + static void removeAllIn(pint_t mh); + static void iterateCacheEntries(void (*func)(unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)); +private: + static void dyldUnloadHook(const struct mach_header* mh, intptr_t vmaddr_slide); + + struct entry { pint_t mh; pint_t ip_start; pint_t ip_end; pint_t fde; }; + + // these fields are all static to avoid needing an initializer + // there is only one instance of this class per process + static pthread_rwlock_t fgLock; + static bool fgRegisteredForDyldUnloads; + // can't use std::vector<> here because this code must live in libSystem.dylib (which is below libstdc++.dylib) + static entry* fgBuffer; + static entry* fgBufferUsed; + static entry* fgBufferEnd; + static entry fgInitialBuffer[64]; +}; + +template <typename A> typename DwarfFDECache<A>::entry* DwarfFDECache<A>::fgBuffer = fgInitialBuffer; +template <typename A> typename DwarfFDECache<A>::entry* DwarfFDECache<A>::fgBufferUsed = fgInitialBuffer; +template <typename A> typename DwarfFDECache<A>::entry* DwarfFDECache<A>::fgBufferEnd = &fgInitialBuffer[64]; +template <typename A> typename DwarfFDECache<A>::entry DwarfFDECache<A>::fgInitialBuffer[64]; + +template <typename A> +pthread_rwlock_t DwarfFDECache<A>::fgLock = PTHREAD_RWLOCK_INITIALIZER; + +template <typename A> +bool DwarfFDECache<A>::fgRegisteredForDyldUnloads = false; + + +template <typename A> +typename A::pint_t DwarfFDECache<A>::findFDE(pint_t mh, pint_t pc) +{ + pint_t result = NULL; + DEBUG_LOG_NON_ZERO(::pthread_rwlock_rdlock(&fgLock)); + for(entry* p=fgBuffer; p < fgBufferUsed; ++p) { + if ( (mh == p->mh) || (mh == 0) ) { + if ( (p->ip_start <= pc) && (pc < p->ip_end) ) { + result = p->fde; + break; + } + } + } + DEBUG_LOG_NON_ZERO(::pthread_rwlock_unlock(&fgLock)); + //fprintf(stderr, "DwarfFDECache::findFDE(mh=0x%llX, pc=0x%llX) => 0x%llX\n", (uint64_t)mh, (uint64_t)pc, (uint64_t)result); + return result; +} + +template <typename A> +void DwarfFDECache<A>::add(pint_t mh, pint_t ip_start, pint_t ip_end, pint_t fde) +{ + //fprintf(stderr, "DwarfFDECache::add(mh=0x%llX, ip_start=0x%llX, ip_end=0x%llX, fde=0x%llX) pthread=%p\n", + // (uint64_t)mh, (uint64_t)ip_start, (uint64_t)ip_end, (uint64_t)fde, pthread_self()); + DEBUG_LOG_NON_ZERO(::pthread_rwlock_wrlock(&fgLock)); + if ( fgBufferUsed >= fgBufferEnd ) { + int oldSize = fgBufferEnd - fgBuffer; + int newSize = oldSize*4; + entry* newBuffer = (entry*)malloc(newSize*sizeof(entry)); // can't use operator new in libSystem.dylib + memcpy(newBuffer, fgBuffer, oldSize*sizeof(entry)); + //fprintf(stderr, "DwarfFDECache::add() growing buffer to %d\n", newSize); + if ( fgBuffer != fgInitialBuffer ) + free(fgBuffer); + fgBuffer = newBuffer; + fgBufferUsed = &newBuffer[oldSize]; + fgBufferEnd = &newBuffer[newSize]; + } + fgBufferUsed->mh = mh; + fgBufferUsed->ip_start = ip_start; + fgBufferUsed->ip_end = ip_end; + fgBufferUsed->fde = fde; + ++fgBufferUsed; +#if !defined (SUPPORT_REMOTE_UNWINDING) + if ( !fgRegisteredForDyldUnloads ) { + _dyld_register_func_for_remove_image(&dyldUnloadHook); + fgRegisteredForDyldUnloads = true; + } +#endif + DEBUG_LOG_NON_ZERO(::pthread_rwlock_unlock(&fgLock)); +} + + + +template <typename A> +void DwarfFDECache<A>::removeAllIn(pint_t mh) +{ + DEBUG_LOG_NON_ZERO(::pthread_rwlock_wrlock(&fgLock)); + entry* d=fgBuffer; + for(const entry* s=fgBuffer; s < fgBufferUsed; ++s) { + if ( s->mh != mh ) { + if ( d != s ) + *d = *s; + ++d; + } + } + fgBufferUsed = d; + DEBUG_LOG_NON_ZERO(::pthread_rwlock_unlock(&fgLock)); +} + + +template <typename A> +void DwarfFDECache<A>::dyldUnloadHook(const struct mach_header* mh, intptr_t vmaddr_slide) +{ +#if !defined (SUPPORT_REMOTE_UNWINDING) + removeAllIn((pint_t)mh); +#endif +} + +template <typename A> +void DwarfFDECache<A>::iterateCacheEntries(void (*func)(unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) +{ + DEBUG_LOG_NON_ZERO(::pthread_rwlock_wrlock(&fgLock)); + for(entry* p=fgBuffer; p < fgBufferUsed; ++p) { + (*func)(p->ip_start, p->ip_end, p->fde, p->mh); + } + DEBUG_LOG_NON_ZERO(::pthread_rwlock_unlock(&fgLock)); +} +#endif // !FOR_DYLD + + + + +#define arrayoffsetof(type, index, field) ((size_t)(&((type *)0)[index].field)) + +template <typename A> +class UnwindSectionHeader { +public: + UnwindSectionHeader(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {} + + uint32_t version() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, version)); } + uint32_t commonEncodingsArraySectionOffset() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, commonEncodingsArraySectionOffset)); } + uint32_t commonEncodingsArrayCount() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, commonEncodingsArrayCount)); } + uint32_t personalityArraySectionOffset() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, personalityArraySectionOffset)); } + uint32_t personalityArrayCount() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, personalityArrayCount)); } + uint32_t indexSectionOffset() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, indexSectionOffset)); } + uint32_t indexCount() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, indexCount)); } +private: + A& fAddressSpace; + typename A::pint_t fAddr; +}; + +template <typename A> +class UnwindSectionIndexArray { +public: + UnwindSectionIndexArray(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {} + + uint32_t functionOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_section_header_index_entry, index, functionOffset)); } + uint32_t secondLevelPagesSectionOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_section_header_index_entry, index, secondLevelPagesSectionOffset)); } + uint32_t lsdaIndexArraySectionOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_section_header_index_entry, index, lsdaIndexArraySectionOffset)); } +private: + A& fAddressSpace; + typename A::pint_t fAddr; +}; + + +template <typename A> +class UnwindSectionRegularPageHeader { +public: + UnwindSectionRegularPageHeader(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {} + + uint32_t kind() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_regular_second_level_page_header, kind)); } + uint16_t entryPageOffset() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_regular_second_level_page_header, entryPageOffset)); } + uint16_t entryCount() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_regular_second_level_page_header, entryCount)); } +private: + A& fAddressSpace; + typename A::pint_t fAddr; +}; + + +template <typename A> +class UnwindSectionRegularArray { +public: + UnwindSectionRegularArray(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {} + + uint32_t functionOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_regular_second_level_entry, index, functionOffset)); } + uint32_t encoding(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_regular_second_level_entry, index, encoding)); } +private: + A& fAddressSpace; + typename A::pint_t fAddr; +}; + + +template <typename A> +class UnwindSectionCompressedPageHeader { +public: + UnwindSectionCompressedPageHeader(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {} + + uint32_t kind() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_compressed_second_level_page_header, kind)); } + uint16_t entryPageOffset() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_compressed_second_level_page_header, entryPageOffset)); } + uint16_t entryCount() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_compressed_second_level_page_header, entryCount)); } + uint16_t encodingsPageOffset() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_compressed_second_level_page_header, encodingsPageOffset)); } + uint16_t encodingsCount() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_compressed_second_level_page_header, encodingsCount)); } +private: + A& fAddressSpace; + typename A::pint_t fAddr; +}; + + +template <typename A> +class UnwindSectionCompressedArray { +public: + UnwindSectionCompressedArray(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {} + + uint32_t functionOffset(int index) const INLINE { return UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET( fAddressSpace.get32(fAddr + index*sizeof(uint32_t)) ); } + uint16_t encodingIndex(int index) const INLINE { return UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX( fAddressSpace.get32(fAddr + index*sizeof(uint32_t)) ); } +private: + A& fAddressSpace; + typename A::pint_t fAddr; +}; + + +template <typename A> +class UnwindSectionLsdaArray { +public: + UnwindSectionLsdaArray(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {} + + uint32_t functionOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_section_header_lsda_index_entry, index, functionOffset)); } + int32_t lsdaOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_section_header_lsda_index_entry, index, lsdaOffset)); } +private: + A& fAddressSpace; + typename A::pint_t fAddr; +}; + + +template <typename A, typename R> +class UnwindCursor +{ +public: + UnwindCursor(unw_context_t* context, A& as); + virtual ~UnwindCursor() {} + virtual bool validReg(int); + virtual uint64_t getReg(int); + virtual int getReg(int, uint64_t*); + virtual int setReg(int, uint64_t); + virtual bool validFloatReg(int); + virtual double getFloatReg(int); + virtual int getFloatReg(int, double*); + virtual int setFloatReg(int, double); + virtual int step(); + virtual void getInfo(unw_proc_info_t*); + virtual void jumpto(); + virtual const char* getRegisterName(int num); + virtual bool isSignalFrame(); + virtual bool getFunctionName(char* buf, size_t bufLen, unw_word_t* offset); + virtual void setInfoBasedOnIPRegister(bool isReturnAddress=false); + + void operator delete(void* p, size_t size) {} + +protected: + typedef typename A::pint_t pint_t; + typedef uint32_t EncodedUnwindInfo; + + virtual bool getInfoFromCompactEncodingSection(pint_t pc, pint_t mh, pint_t unwindSectionStart); + virtual bool getInfoFromDwarfSection(pint_t pc, pint_t mh, pint_t ehSectionStart, uint32_t sectionLength, uint32_t sectionOffsetOfFDE); + + virtual int stepWithDwarfFDE() + { return DwarfInstructions<A,R>::stepWithDwarf(fAddressSpace, this->getReg(UNW_REG_IP), fInfo.unwind_info, fRegisters); } + + virtual int stepWithCompactEncoding() { R dummy; return stepWithCompactEncoding(dummy); } + int stepWithCompactEncoding(Registers_x86_64&) + { return CompactUnwinder_x86_64<A>::stepWithCompactEncoding(fInfo.format, fInfo.start_ip, fAddressSpace, fRegisters); } + int stepWithCompactEncoding(Registers_x86&) + { return CompactUnwinder_x86<A>::stepWithCompactEncoding(fInfo.format, fInfo.start_ip, fAddressSpace, fRegisters); } + int stepWithCompactEncoding(Registers_ppc&) + { return UNW_EINVAL; } + +#if FOR_DYLD + #if __ppc__ + virtual bool mustUseDwarf() const { return true; } + #else + virtual bool mustUseDwarf() const { return false; } + #endif +#else + virtual bool mustUseDwarf() const { R dummy; uint32_t offset; return dwarfWithOffset(dummy, offset); } +#endif + + virtual bool dwarfWithOffset(uint32_t& offset) const { R dummy; return dwarfWithOffset(dummy, offset); } + virtual bool dwarfWithOffset(Registers_x86_64&, uint32_t& offset) const { + if ( (fInfo.format & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF ) { + offset = (fInfo.format & UNWIND_X86_64_DWARF_SECTION_OFFSET); + return true; + } +#if SUPPORT_OLD_BINARIES + if ( (fInfo.format & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_COMPATIBILITY ) { + if ( (fInfo.format & UNWIND_X86_64_CASE_MASK) == UNWIND_X86_64_UNWIND_REQUIRES_DWARF ) { + offset = 0; + return true; + } + } +#endif + return false; + } + virtual bool dwarfWithOffset(Registers_x86&, uint32_t& offset) const { + if ( (fInfo.format & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF ) { + offset = (fInfo.format & UNWIND_X86_DWARF_SECTION_OFFSET); + return true; + } +#if SUPPORT_OLD_BINARIES + if ( (fInfo.format & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_COMPATIBILITY ) { + if ( (fInfo.format & UNWIND_X86_CASE_MASK) == UNWIND_X86_UNWIND_REQUIRES_DWARF ) { + offset = 0; + return true; + } + } +#endif + return false; + } + virtual bool dwarfWithOffset(Registers_ppc&, uint32_t& offset) const { return true; } + + + virtual compact_unwind_encoding_t dwarfEncoding() const { R dummy; return dwarfEncoding(dummy); } + virtual compact_unwind_encoding_t dwarfEncoding(Registers_x86_64&) const { return UNWIND_X86_64_MODE_DWARF; } + virtual compact_unwind_encoding_t dwarfEncoding(Registers_x86&) const { return UNWIND_X86_MODE_DWARF; } + virtual compact_unwind_encoding_t dwarfEncoding(Registers_ppc&) const { return 0; } + + unw_proc_info_t fInfo; + R fRegisters; + A& fAddressSpace; + bool fUnwindInfoMissing; + bool fIsSignalFrame; +}; + +typedef UnwindCursor<LocalAddressSpace,Registers_x86> AbstractUnwindCursor; + +template <typename A, typename R> +UnwindCursor<A,R>::UnwindCursor(unw_context_t* context, A& as) + : fRegisters(context), fAddressSpace(as), fUnwindInfoMissing(false), fIsSignalFrame(false) +{ + COMPILE_TIME_ASSERT( sizeof(UnwindCursor<A,R>) < sizeof(unw_cursor_t) ); + + bzero(&fInfo, sizeof(fInfo)); +} + +template <typename A, typename R> +bool UnwindCursor<A,R>::validReg(int regNum) +{ + return fRegisters.validRegister(regNum); +} + +template <typename A, typename R> +uint64_t UnwindCursor<A,R>::getReg(int regNum) +{ + return fRegisters.getRegister(regNum); +} + +template <typename A, typename R> +int UnwindCursor<A,R>::getReg(int regNum, uint64_t *valp) +{ + *valp = fRegisters.getRegister(regNum); + return UNW_ESUCCESS; +} + +template <typename A, typename R> +int UnwindCursor<A,R>::setReg(int regNum, uint64_t value) +{ + fRegisters.setRegister(regNum, value); + return UNW_ESUCCESS; +} + +template <typename A, typename R> +bool UnwindCursor<A,R>::validFloatReg(int regNum) +{ + return fRegisters.validFloatRegister(regNum); +} + +template <typename A, typename R> +double UnwindCursor<A,R>::getFloatReg(int regNum) +{ + return fRegisters.getFloatRegister(regNum); +} + +template <typename A, typename R> +int UnwindCursor<A,R>::getFloatReg(int regNum, double *valp) +{ + *valp = fRegisters.getFloatRegister(regNum); + return UNW_ESUCCESS; +} + +template <typename A, typename R> +int UnwindCursor<A,R>::setFloatReg(int regNum, double value) +{ + fRegisters.setFloatRegister(regNum, value); + return UNW_ESUCCESS; +} + +template <typename A, typename R> +void UnwindCursor<A,R>::jumpto() +{ +#if !defined (SUPPORT_REMOTE_UNWINDING) + fRegisters.jumpto(); +#endif +} + +template <typename A, typename R> +const char* UnwindCursor<A,R>::getRegisterName(int regNum) +{ + return fRegisters.getRegisterName(regNum); +} + +template <typename A, typename R> +bool UnwindCursor<A,R>::isSignalFrame() +{ + return fIsSignalFrame; +} + + +template <typename A, typename R> +bool UnwindCursor<A,R>::getInfoFromDwarfSection(pint_t pc, pint_t mh, pint_t ehSectionStart, uint32_t sectionLength, uint32_t sectionOffsetOfFDE) +{ + typename CFI_Parser<A>::FDE_Info fdeInfo; + typename CFI_Parser<A>::CIE_Info cieInfo; + bool foundFDE = false; + bool foundInCache = false; + // if compact encoding table gave offset into dwarf section, go directly there + if ( sectionOffsetOfFDE != 0 ) { + foundFDE = CFI_Parser<A>::findFDE(fAddressSpace, pc, ehSectionStart, sectionLength, ehSectionStart+sectionOffsetOfFDE, &fdeInfo, &cieInfo); + } +#if !FOR_DYLD + if ( !foundFDE ) { + // otherwise, search cache of previously found FDEs + pint_t cachedFDE = DwarfFDECache<A>::findFDE(mh, pc); + //fprintf(stderr, "getInfoFromDwarfSection(pc=0x%llX) cachedFDE=0x%llX\n", (uint64_t)pc, (uint64_t)cachedFDE); + if ( cachedFDE != 0 ) { + foundFDE = CFI_Parser<A>::findFDE(fAddressSpace, pc, ehSectionStart, sectionLength, cachedFDE, &fdeInfo, &cieInfo); + foundInCache = foundFDE; + //fprintf(stderr, "cachedFDE=0x%llX, foundInCache=%d\n", (uint64_t)cachedFDE, foundInCache); + } + } +#endif + if ( !foundFDE ) { + // still not found, do full scan of __eh_frame section + foundFDE = CFI_Parser<A>::findFDE(fAddressSpace, pc, ehSectionStart, sectionLength, 0, &fdeInfo, &cieInfo); + } + if ( foundFDE ) { + typename CFI_Parser<A>::PrologInfo prolog; + if ( CFI_Parser<A>::parseFDEInstructions(fAddressSpace, fdeInfo, cieInfo, pc, &prolog) ) { + // save off parsed FDE info + fInfo.start_ip = fdeInfo.pcStart; + fInfo.end_ip = fdeInfo.pcEnd; + fInfo.lsda = fdeInfo.lsda; + fInfo.handler = cieInfo.personality; + fInfo.gp = prolog.spExtraArgSize; // some frameless functions need SP altered when resuming in function + fInfo.flags = 0; + fInfo.format = dwarfEncoding(); + fInfo.unwind_info = fdeInfo.fdeStart; + fInfo.unwind_info_size = fdeInfo.fdeLength; + fInfo.extra = (unw_word_t)mh; + if ( !foundInCache && (sectionOffsetOfFDE == 0) ) { + // don't add to cache entries the compact encoding table can find quickly + //fprintf(stderr, "getInfoFromDwarfSection(pc=0x%0llX), mh=0x%llX, start_ip=0x%0llX, fde=0x%0llX, personality=0x%0llX\n", + // (uint64_t)pc, (uint64_t)mh, fInfo.start_ip, fInfo.unwind_info, fInfo.handler); +#if !FOR_DYLD + DwarfFDECache<A>::add(mh, fdeInfo.pcStart, fdeInfo.pcEnd, fdeInfo.fdeStart); +#endif + } + return true; + } + } + //DEBUG_MESSAGE("can't find/use FDE for pc=0x%llX\n", (uint64_t)pc); + return false; +} + +template <typename A, typename R> +bool UnwindCursor<A,R>::getInfoFromCompactEncodingSection(pint_t pc, pint_t mh, pint_t unwindSectionStart) +{ + const bool log = false; + if ( log ) fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX, mh=0x%llX)\n", (uint64_t)pc, (uint64_t)mh); + + const UnwindSectionHeader<A> sectionHeader(fAddressSpace, unwindSectionStart); + if ( sectionHeader.version() != UNWIND_SECTION_VERSION ) + return false; + + // do a binary search of top level index to find page with unwind info + uint32_t targetFunctionOffset = pc - mh; + const UnwindSectionIndexArray<A> topIndex(fAddressSpace, unwindSectionStart + sectionHeader.indexSectionOffset()); + uint32_t low = 0; + uint32_t high = sectionHeader.indexCount(); + const uint32_t last = high - 1; + while ( low < high ) { + uint32_t mid = (low + high)/2; + //if ( log ) fprintf(stderr, "\tmid=%d, low=%d, high=%d, *mid=0x%08X\n", mid, low, high, topIndex.functionOffset(mid)); + if ( topIndex.functionOffset(mid) <= targetFunctionOffset ) { + if ( (mid == last) || (topIndex.functionOffset(mid+1) > targetFunctionOffset) ) { + low = mid; + break; + } + else { + low = mid+1; + } + } + else { + high = mid; + } + } + const uint32_t firstLevelFunctionOffset = topIndex.functionOffset(low); + const uint32_t firstLevelNextPageFunctionOffset = topIndex.functionOffset(low+1); + const pint_t secondLevelAddr = unwindSectionStart+topIndex.secondLevelPagesSectionOffset(low); + const pint_t lsdaArrayStartAddr = unwindSectionStart+topIndex.lsdaIndexArraySectionOffset(low); + const pint_t lsdaArrayEndAddr = unwindSectionStart+topIndex.lsdaIndexArraySectionOffset(low+1); + if ( log ) fprintf(stderr, "\tfirst level search for result index=%d to secondLevelAddr=0x%llX\n", + low, (uint64_t)secondLevelAddr); + // do a binary search of second level page index + uint32_t encoding = 0; + pint_t funcStart = 0; + pint_t funcEnd = 0; + pint_t lsda = 0; + pint_t personality = 0; + uint32_t pageKind = fAddressSpace.get32(secondLevelAddr); + if ( pageKind == UNWIND_SECOND_LEVEL_REGULAR ) { + // regular page + UnwindSectionRegularPageHeader<A> pageHeader(fAddressSpace, secondLevelAddr); + UnwindSectionRegularArray<A> pageIndex(fAddressSpace, secondLevelAddr + pageHeader.entryPageOffset()); + // binary search looks for entry with e where index[e].offset <= pc < index[e+1].offset + if ( log ) fprintf(stderr, "\tbinary search for targetFunctionOffset=0x%08llX in regular page starting at secondLevelAddr=0x%llX\n", + (uint64_t)targetFunctionOffset, (uint64_t)secondLevelAddr); + uint32_t low = 0; + uint32_t high = pageHeader.entryCount(); + while ( low < high ) { + uint32_t mid = (low + high)/2; + if ( pageIndex.functionOffset(mid) <= targetFunctionOffset ) { + if ( mid == (uint32_t)(pageHeader.entryCount()-1) ) { + // at end of table + low = mid; + funcEnd = firstLevelNextPageFunctionOffset + mh; + break; + } + else if ( pageIndex.functionOffset(mid+1) > targetFunctionOffset ) { + // next is too big, so we found it + low = mid; + funcEnd = pageIndex.functionOffset(low+1) + mh; + break; + } + else { + low = mid+1; + } + } + else { + high = mid; + } + } + encoding = pageIndex.encoding(low); + funcStart = pageIndex.functionOffset(low) + mh; + if ( pc < funcStart ) { + if ( log ) fprintf(stderr, "\tpc not in table, pc=0x%llX, funcStart=0x%llX, funcEnd=0x%llX\n", (uint64_t)pc, (uint64_t)funcStart, (uint64_t)funcEnd); + return false; + } + if ( pc > funcEnd ) { + if ( log ) fprintf(stderr, "\tpc not in table, pc=0x%llX, funcStart=0x%llX, funcEnd=0x%llX\n", (uint64_t)pc, (uint64_t)funcStart, (uint64_t)funcEnd); + return false; + } + } + else if ( pageKind == UNWIND_SECOND_LEVEL_COMPRESSED ) { + // compressed page + UnwindSectionCompressedPageHeader<A> pageHeader(fAddressSpace, secondLevelAddr); + UnwindSectionCompressedArray<A> pageIndex(fAddressSpace, secondLevelAddr + pageHeader.entryPageOffset()); + const uint32_t targetFunctionPageOffset = targetFunctionOffset - firstLevelFunctionOffset; + // binary search looks for entry with e where index[e].offset <= pc < index[e+1].offset + if ( log ) fprintf(stderr, "\tbinary search of compressed page starting at secondLevelAddr=0x%llX\n", (uint64_t)secondLevelAddr); + uint32_t low = 0; + const uint32_t last = pageHeader.entryCount() - 1; + uint32_t high = pageHeader.entryCount(); + while ( low < high ) { + uint32_t mid = (low + high)/2; + if ( pageIndex.functionOffset(mid) <= targetFunctionPageOffset ) { + if ( (mid == last) || (pageIndex.functionOffset(mid+1) > targetFunctionPageOffset) ) { + low = mid; + break; + } + else { + low = mid+1; + } + } + else { + high = mid; + } + } + funcStart = pageIndex.functionOffset(low) + firstLevelFunctionOffset + mh; + if ( low < last ) + funcEnd = pageIndex.functionOffset(low+1) + firstLevelFunctionOffset + mh; + else + funcEnd = firstLevelNextPageFunctionOffset + mh; + if ( pc < funcStart ) { + DEBUG_MESSAGE("malformed __unwind_info, pc=0x%llX not in second level compressed unwind table. funcStart=0x%llX\n", (uint64_t)pc, (uint64_t)funcStart); + return false; + } + if ( pc > funcEnd ) { + DEBUG_MESSAGE("malformed __unwind_info, pc=0x%llX not in second level compressed unwind table. funcEnd=0x%llX\n", (uint64_t)pc, (uint64_t)funcEnd); + return false; + } + uint16_t encodingIndex = pageIndex.encodingIndex(low); + if ( encodingIndex < sectionHeader.commonEncodingsArrayCount() ) { + // encoding is in common table in section header + encoding = fAddressSpace.get32(unwindSectionStart+sectionHeader.commonEncodingsArraySectionOffset()+encodingIndex*sizeof(uint32_t)); + } + else { + // encoding is in page specific table + uint16_t pageEncodingIndex = encodingIndex-sectionHeader.commonEncodingsArrayCount(); + encoding = fAddressSpace.get32(secondLevelAddr+pageHeader.encodingsPageOffset()+pageEncodingIndex*sizeof(uint32_t)); + } + } + else { + DEBUG_MESSAGE("malformed __unwind_info at 0x%0llX bad second level page\n", (uint64_t)unwindSectionStart); + return false; + } + + // look up LSDA, if encoding says function has one + if ( encoding & UNWIND_HAS_LSDA ) { + UnwindSectionLsdaArray<A> lsdaIndex(fAddressSpace, lsdaArrayStartAddr); + uint32_t funcStartOffset = funcStart - mh; + uint32_t low = 0; + uint32_t high = (lsdaArrayEndAddr-lsdaArrayStartAddr)/sizeof(unwind_info_section_header_lsda_index_entry); + // binary search looks for entry with exact match for functionOffset + if ( log ) fprintf(stderr, "\tbinary search of lsda table for targetFunctionOffset=0x%08X\n", funcStartOffset); + while ( low < high ) { + uint32_t mid = (low + high)/2; + if ( lsdaIndex.functionOffset(mid) == funcStartOffset ) { + lsda = lsdaIndex.lsdaOffset(mid) + mh; + break; + } + else if ( lsdaIndex.functionOffset(mid) < funcStartOffset ) { + low = mid+1; + } + else { + high = mid; + } + } + if ( lsda == 0 ) { + DEBUG_MESSAGE("found encoding 0x%08X with HAS_LSDA bit set for pc=0x%0llX, but lsda table has no entry\n", encoding, (uint64_t)pc); + return false; + } + } + + // extact personality routine, if encoding says function has one + uint32_t personalityIndex = (encoding & UNWIND_PERSONALITY_MASK) >> (__builtin_ctz(UNWIND_PERSONALITY_MASK)); + if ( personalityIndex != 0 ) { + --personalityIndex; // change 1-based to zero-based index + if ( personalityIndex > sectionHeader.personalityArrayCount() ) { + DEBUG_MESSAGE("found encoding 0x%08X with personality index %d, but personality table has only %d entires\n", + encoding, personalityIndex, sectionHeader.personalityArrayCount()); + return false; + } + int32_t personalityDelta = fAddressSpace.get32(unwindSectionStart+sectionHeader.personalityArraySectionOffset()+personalityIndex*sizeof(uint32_t)); + pint_t personalityPointer = personalityDelta + mh; + personality = fAddressSpace.getP(personalityPointer); + if (log ) fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), personalityDelta=0x%08X, personality=0x%08llX\n", + (uint64_t)pc, personalityDelta, (uint64_t)personality); + } + + if (log ) fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), encoding=0x%08X, lsda=0x%08llX for funcStart=0x%llX\n", + (uint64_t)pc, encoding, (uint64_t)lsda, (uint64_t)funcStart); + fInfo.start_ip = funcStart; + fInfo.end_ip = funcEnd; + fInfo.lsda = lsda; + fInfo.handler = personality; + fInfo.gp = 0; + fInfo.flags = 0; + fInfo.format = encoding; + fInfo.unwind_info = 0; + fInfo.unwind_info_size = 0; + fInfo.extra = mh; + return true; +} + +template <typename A, typename R> +void UnwindCursor<A,R>::setInfoBasedOnIPRegister(bool isReturnAddress) +{ + pint_t pc = this->getReg(UNW_REG_IP); + + // if the last line of a function is a "throw" the compile sometimes + // emits no instructions after the call to __cxa_throw. This means + // the return address is actually the start of the next function. + // To disambiguate this, back up the pc when we know it is a return + // address. + if ( isReturnAddress ) + --pc; + + // ask address space object to find unwind sections for this pc + pint_t mh; + pint_t dwarfStart; + pint_t dwarfLength; + pint_t compactStart; + if ( fAddressSpace.findUnwindSections(pc, mh, dwarfStart, dwarfLength, compactStart) ) { + // if there is a compact unwind encoding table, look there first + if ( compactStart != 0 ) { + if ( this->getInfoFromCompactEncodingSection(pc, mh, compactStart) ) { +#if !FOR_DYLD + // found info in table, done unless encoding says to use dwarf + uint32_t offsetInDwarfSection; + if ( (dwarfStart != 0) && dwarfWithOffset(offsetInDwarfSection) ) { + if ( this->getInfoFromDwarfSection(pc, mh, dwarfStart, dwarfLength, offsetInDwarfSection) ) { + // found info in dwarf, done + return; + } + } +#endif + // if unwind table has entry, but entry says there is no unwind info, note that + if ( fInfo.format == 0 ) + fUnwindInfoMissing = true; + + // old compact encoding + if ( !mustUseDwarf() ) { + return; + } + } + } +#if !FOR_DYLD || __ppc__ + // if there is dwarf unwind info, look there next + if ( dwarfStart != 0 ) { + if ( this->getInfoFromDwarfSection(pc, mh, dwarfStart, dwarfLength, 0) ) { + // found info in dwarf, done + return; + } + } +#endif + } + +#if !FOR_DYLD + // the PC is not in code loaded by dyld, look through __register_frame() registered FDEs + pint_t cachedFDE = DwarfFDECache<A>::findFDE(0, pc); + if ( cachedFDE != 0 ) { + typename CFI_Parser<A>::FDE_Info fdeInfo; + typename CFI_Parser<A>::CIE_Info cieInfo; + const char* msg = CFI_Parser<A>::decodeFDE(fAddressSpace, cachedFDE, &fdeInfo, &cieInfo); + if ( msg == NULL ) { + typename CFI_Parser<A>::PrologInfo prolog; + if ( CFI_Parser<A>::parseFDEInstructions(fAddressSpace, fdeInfo, cieInfo, pc, &prolog) ) { + // save off parsed FDE info + fInfo.start_ip = fdeInfo.pcStart; + fInfo.end_ip = fdeInfo.pcEnd; + fInfo.lsda = fdeInfo.lsda; + fInfo.handler = cieInfo.personality; + fInfo.gp = prolog.spExtraArgSize; // some frameless functions need SP altered when resuming in function + fInfo.flags = 0; + fInfo.format = dwarfEncoding(); + fInfo.unwind_info = fdeInfo.fdeStart; + fInfo.unwind_info_size = fdeInfo.fdeLength; + fInfo.extra = 0; + return; + } + } + } + +#if !defined (SUPPORT_REMOTE_UNWINDING) + // lastly check for old style keymgr registration of dynamically generated FDEs + + // acquire exclusive access to libgcc_object_info + libgcc_object_info* head = (libgcc_object_info*)_keymgr_get_and_lock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST); + if ( head != NULL ) { + // look at each FDE in keymgr + for (libgcc_object* ob = head->unseen_objects; ob != NULL; ob = ob->next) { + typename CFI_Parser<A>::FDE_Info fdeInfo; + typename CFI_Parser<A>::CIE_Info cieInfo; + const char* msg = CFI_Parser<A>::decodeFDE(fAddressSpace, (pint_t)ob->fde, &fdeInfo, &cieInfo); + if ( msg == NULL ) { + // see if this FDE is for a function that includes the pc we are looking for + if ( (fdeInfo.pcStart <= pc) && (pc < fdeInfo.pcEnd) ) { + typename CFI_Parser<A>::PrologInfo prolog; + if ( CFI_Parser<A>::parseFDEInstructions(fAddressSpace, fdeInfo, cieInfo, pc, &prolog) ) { + // save off parsed FDE info + fInfo.start_ip = fdeInfo.pcStart; + fInfo.end_ip = fdeInfo.pcEnd; + fInfo.lsda = fdeInfo.lsda; + fInfo.handler = cieInfo.personality; + fInfo.gp = prolog.spExtraArgSize; // some frameless functions need SP altered when resuming in function + fInfo.flags = 0; + fInfo.format = dwarfEncoding(); + fInfo.unwind_info = fdeInfo.fdeStart; + fInfo.unwind_info_size = fdeInfo.fdeLength; + fInfo.extra = 0; + _keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST, head); + return; + } + } + } + } + } + // release libgcc_object_info + _keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST, head); +#endif // !SUPPORT_REMOTE_UNWINDING + +#endif // !FOR_DYLD + + // no unwind info, flag that we can't reliable unwind + fUnwindInfoMissing = true; +} + + +template <typename A, typename R> +int UnwindCursor<A,R>::step() +{ + // bottom of stack is defined as when no more unwind info + if ( fUnwindInfoMissing ) + return UNW_STEP_END; + + // apply unwinding to register set + int result; + if ( this->mustUseDwarf() ) + result = this->stepWithDwarfFDE(); + else + result = this->stepWithCompactEncoding(); + + // update info based on new PC + if ( result == UNW_STEP_SUCCESS ) { + this->setInfoBasedOnIPRegister(true); + if ( fUnwindInfoMissing ) + return UNW_STEP_END; + } + + return result; +} + + +template <typename A, typename R> +void UnwindCursor<A,R>::getInfo(unw_proc_info_t* info) +{ + *info = fInfo; +} + + +template <typename A, typename R> +bool UnwindCursor<A,R>::getFunctionName(char* buf, size_t bufLen, unw_word_t* offset) +{ + return fAddressSpace.findFunctionName(this->getReg(UNW_REG_IP), buf, bufLen, offset); +} + +#if defined (SUPPORT_REMOTE_UNWINDING) +template <typename A, typename R> +class RemoteUnwindCursor : UnwindCursor<A,R> +{ +public: + typedef typename A::pint_t pint_t; + RemoteUnwindCursor(A& as, unw_context_t* regs, void* arg); + virtual bool validReg(int); + virtual int getReg(int r, uint64_t*); + virtual int setReg(int, uint64_t); + virtual bool validFloatReg(int); + virtual int getFloatReg(int, double*); + virtual int setFloatReg(int, double); + virtual const char* getRegisterName(int); + virtual int step(); + virtual void setRemoteContext(void*); + virtual bool remoteUnwindCursor () const {return this->fAddressSpace.getRemoteProcInfo() != NULL; } + virtual int endOfPrologueInsns(unw_word_t, unw_word_t, unw_word_t*); + void operator delete(void* p, size_t size) {} +private: + virtual bool caller_regno_to_unwind_regno (int, int&); + + bool fIsLeafFrame; + bool fIsFirstFrame; + void* fArg; +}; + +typedef RemoteUnwindCursor<LocalAddressSpace,Registers_x86_64> AbstractRemoteUnwindCursor; + +template <typename A, typename R> +RemoteUnwindCursor<A,R>::RemoteUnwindCursor(A& as, unw_context_t* regs, void* arg) + : UnwindCursor<A,R>::UnwindCursor(regs, as), fIsFirstFrame (false), fIsLeafFrame(false), fArg(arg) +{ + COMPILE_TIME_ASSERT( sizeof(RemoteUnwindCursor<A,R>) < sizeof(unw_cursor_t) ); +} + +template <typename A, typename R> +bool RemoteUnwindCursor<A,R>::validReg(int r) +{ + int unwind_regno; + if (!caller_regno_to_unwind_regno(r, unwind_regno)) + return false; + return UnwindCursor<A,R>::fRegisters.validRegister(unwind_regno); +} + +template <typename A, typename R> +int RemoteUnwindCursor<A,R>::getReg(int regNum, uint64_t *valp) +{ + RemoteProcInfo *procinfo = UnwindCursor<A,R>::fAddressSpace.getRemoteProcInfo (); + if (procinfo == NULL) { + ABORT("getRemoteReg called with a local unwind, use getReg instead."); + } + + RemoteRegisterMap *regmap = procinfo->getRegisterMap (); + int unwind_regno; + if (regmap->caller_regno_to_unwind_regno (regNum, unwind_regno) == false) + return UNW_EBADREG; + regNum = unwind_regno; + + // we always return nonvolatile registers. If we have the entire register state available + // for this frame then we can return any register requested. + if (regmap->nonvolatile_reg_p (regNum) == true || fIsLeafFrame == true) { + return this->UnwindCursor<A,R>::getReg (unwind_regno, valp); + } + return UNW_EREGUNAVAILABLE; +} + +template <typename A, typename R> +int RemoteUnwindCursor<A,R>::setReg(int regNum, uint64_t val) +{ + RemoteProcInfo *procinfo = UnwindCursor<A,R>::fAddressSpace.getRemoteProcInfo (); + if (procinfo == NULL) { + ABORT("setRemoteReg called with a local unwind, use setReg instead."); + } + + RemoteRegisterMap *regmap = procinfo->getRegisterMap (); + int unwind_regno; + if (regmap->caller_regno_to_unwind_regno (regNum, unwind_regno) == false) + return UNW_EBADREG; + regNum = unwind_regno; + + // Only allow the registers to be set if the unwind cursor is pointing to the + // first frame. We need to track where registers were retrieved from in memory + // in every other frame. Until then, we prohibit register setting in all but + // the first frame. + if (fIsFirstFrame) { + return this->setReg(unwind_regno, val); + } + return UNW_EREGUNAVAILABLE; +} + +template <typename A, typename R> +bool RemoteUnwindCursor<A,R>::validFloatReg(int r) +{ + int unwind_regno; + if (!caller_regno_to_unwind_regno(r, unwind_regno)) + return false; + return UnwindCursor<A,R>::fRegisters.validFloatRegister(unwind_regno); +} + +template <typename A, typename R> +int RemoteUnwindCursor<A,R>::getFloatReg(int regNum, double *valp) +{ + RemoteProcInfo *procinfo = UnwindCursor<A,R>::fAddressSpace.getRemoteProcInfo (); + if (procinfo == NULL) { + ABORT("getRemoteReg called with a local unwind, use getReg instead."); + } + + RemoteRegisterMap *regmap = procinfo->getRegisterMap (); + int unwind_regno; + if (regmap->caller_regno_to_unwind_regno (regNum, unwind_regno) == false) + return UNW_EBADREG; + regNum = unwind_regno; + + // we always return nonvolatile registers. If we have the entire register state available + // for this frame then we can return any register requested. + if (regmap->nonvolatile_reg_p (regNum) == true || fIsLeafFrame == true) { + return this->UnwindCursor<A,R>::getFloatReg (unwind_regno, valp); + } + return UNW_EREGUNAVAILABLE; +} + +template <typename A, typename R> +int RemoteUnwindCursor<A,R>::setFloatReg(int regNum, double val) +{ + RemoteProcInfo *procinfo = UnwindCursor<A,R>::fAddressSpace.getRemoteProcInfo (); + if (procinfo == NULL) { + ABORT("setRemoteReg called with a local unwind, use setReg instead."); + } + + RemoteRegisterMap *regmap = procinfo->getRegisterMap (); + int unwind_regno; + if (regmap->caller_regno_to_unwind_regno (regNum, unwind_regno) == false) + return UNW_EBADREG; + regNum = unwind_regno; + + // Only allow the registers to be set if the unwind cursor is pointing to the + // first frame. We need to track where registers were retrieved from in memory + // in every other frame. Until then, we prohibit register setting in all but + // the first frame. + if (fIsFirstFrame) { + return this->setFloatReg(unwind_regno, val); + } + return UNW_EREGUNAVAILABLE; +} + + +template <typename A, typename R> +const char* RemoteUnwindCursor<A,R>::getRegisterName(int r) +{ + int t; + if (!this->caller_regno_to_unwind_regno(r, t)) + return NULL; + r = t; + return this->UnwindCursor<A,R>::getRegisterName(r); +} + +template <typename A, typename R> +int RemoteUnwindCursor<A,R>::step() +{ + pint_t pc = this->UnwindCursor<A,R>::getReg(UNW_REG_IP); + pint_t sp = this->UnwindCursor<A,R>::getReg(UNW_REG_SP); + RemoteProcInfo *procinfo = UnwindCursor<A,R>::fAddressSpace.getRemoteProcInfo(); + bool frame_is_sigtramp = false; + bool frame_is_inferior_function_call_dummy = false; + + if (procinfo == NULL) { + ABORT("stepRemote called with local unwind, use step() instead."); + return UNW_EUNSPEC; + } + struct timeval *step_remote = procinfo->timestamp_start(); + procinfo->logVerbose ("stepRemote stepping out of frame with pc value 0x%llx", pc); + + // We'll be off of the first frame once we finish this step. + fIsFirstFrame = false; + + if (UnwindCursor<A,R>::fAddressSpace.accessors() + && UnwindCursor<A,R>::fAddressSpace.accessors()->proc_is_sigtramp != NULL + && UnwindCursor<A,R>::fAddressSpace.accessors()->proc_is_sigtramp (procinfo->wrap(), pc, fArg)) { + frame_is_sigtramp = true; + } + if (UnwindCursor<A,R>::fAddressSpace.accessors() + && UnwindCursor<A,R>::fAddressSpace.accessors()->proc_is_inferior_function_call != NULL + && UnwindCursor<A,R>::fAddressSpace.accessors()->proc_is_inferior_function_call (procinfo->wrap(), pc, sp, fArg)) { + frame_is_inferior_function_call_dummy = true; + } + + // If the function we're unwinding can't be a leaf function, + // use the eh_frame or compact unwind info if possible. + // The caller should pass couldBeLeafFunc == 0 on the first step of a new context + // but we can't trust them in that. + + if ((fIsLeafFrame == false && frame_is_inferior_function_call_dummy == false) + || frame_is_sigtramp) { + R saved_registers(UnwindCursor<A,R>::fRegisters); + this->setInfoBasedOnIPRegister(true); + // bottom of stack is defined as when no more unwind info + if ( !UnwindCursor<A,R>::fUnwindInfoMissing ) { + int result; + const char *method; + if ( this->mustUseDwarf() ) { + result = this->stepWithDwarfFDE(); + method = "dwarf"; + } + else { + result = this->stepWithCompactEncoding(); + method = "compact unwind"; + } + if ( result == UNW_STEP_SUCCESS ) { + procinfo->logInfo ("Stepped via %s", method); + procinfo->timestamp_stop (step_remote, "stepRemote"); + if (frame_is_sigtramp) + fIsLeafFrame = true; + return result; + } + } + UnwindCursor<A,R>::fRegisters = saved_registers; + } + + if (frame_is_sigtramp || frame_is_inferior_function_call_dummy) + fIsLeafFrame = true; // this will be true once we complete this stepRemote() + else + fIsLeafFrame = false; + + if (frame_is_inferior_function_call_dummy) { + if (stepOutOfDebuggerDummyFrame (UnwindCursor<A,R>::fAddressSpace, UnwindCursor<A,R>::fRegisters, procinfo, pc, sp, fArg) == UNW_STEP_SUCCESS) { + procinfo->logInfo ("Stepped via stepOutOfDebuggerDummyFrame"); + procinfo->timestamp_stop (step_remote, "stepRemote"); + return UNW_STEP_SUCCESS; + } + } + + // If we haven't already seen this function we'll need to get the function bounds via + // eh frame info (if available) - it's the most accurate function bounds in a + // stripped binary. After that we'll ask the driver program (via the get_proc_bounds accessor). + + if (procinfo->haveProfile (pc) == false) { + + uint64_t text_start, text_end, eh_frame_start, eh_frame_len, compact_unwind_start, mh; + uint64_t start_addr, end_addr; + if (pc == 0) { + int ret = stepByArchitectureDefault (UnwindCursor<A,R>::fAddressSpace, UnwindCursor<A,R>::fRegisters, pc); + procinfo->logInfo ("Stepped via stepByArchitectureDefault"); + procinfo->timestamp_stop (step_remote, "stepRemote"); + return ret; + } + + // If the address is not contained in any image's address range either we've walked off + // the stack into random memory or we're backtracing through jit'ed code on the heap. + // Let's assume the latter and follow the architecture's default stack walking scheme. + + if (!procinfo->getImageAddresses (pc, mh, text_start, text_end, eh_frame_start, eh_frame_len, compact_unwind_start, fArg)) { + int ret = stepByArchitectureDefault (UnwindCursor<A,R>::fAddressSpace, UnwindCursor<A,R>::fRegisters, pc); + procinfo->logInfo ("Stepped via stepByArchitectureDefault"); + procinfo->timestamp_stop (step_remote, "stepRemote"); + return ret; + } + if (procinfo->haveFuncBounds (mh) == false) { + struct timeval *get_func_bounds = procinfo->timestamp_start(); + std::vector<FuncBounds> func_bounds; + // CFI entries are usually around 38 bytes but under-estimate a bit + // because we're not distinguishing between CIEs and FDEs. + if (eh_frame_len > 0) + func_bounds.reserve (eh_frame_len / 16); + if (procinfo->getCachingPolicy() != UNW_CACHE_NONE) { + // cache the entire eh frame section - we'll need to read the whole + // thing anyway so we might as well save it. + uint8_t *eh_buf = (uint8_t *)malloc (eh_frame_len); + if (UnwindCursor<A,R>::fAddressSpace.getBytes (eh_frame_start, eh_frame_len, eh_buf) == 0) + return UNW_EUNSPEC; + RemoteMemoryBlob *ehmem = new RemoteMemoryBlob(eh_buf, free, eh_frame_start, eh_frame_len, mh, NULL); + procinfo->addMemBlob (ehmem); + } + + if (CFI_Parser<A>::functionFuncBoundsViaFDE(UnwindCursor<A,R>::fAddressSpace, eh_frame_start, eh_frame_len, func_bounds)) { + procinfo->addFuncBounds(mh, func_bounds); + procinfo->logVerbose ("Added %d function bounds", (int) func_bounds.size()); + procinfo->timestamp_stop (get_func_bounds, "getting function bounds from EH frame FDEs"); + } + } + if (procinfo->findStartAddr (pc, start_addr, end_addr)) { + // If end_addr is 0, we might be looking at the final function in this binary image + if (start_addr != 0 && end_addr == 0) + end_addr = text_end; + procinfo->logVerbose ("Got function bounds from func bounds vector, 0x%llx-0x%llx", start_addr, end_addr); + } else { + if (UnwindCursor<A,R>::fAddressSpace.accessors()->get_proc_bounds (procinfo->wrap(), pc, &start_addr, &end_addr, fArg) != UNW_ESUCCESS) { + int ret = stepByArchitectureDefault (UnwindCursor<A,R>::fAddressSpace, UnwindCursor<A,R>::fRegisters, pc); + procinfo->logInfo ("Stepped via stepByArchitectureDefault"); + procinfo->timestamp_stop (step_remote, "stepRemote"); + return ret; + } + else { + procinfo->logVerbose ("Got function bounds from get_proc_bounds callback, 0x%llx-0x%llx", start_addr, end_addr); + } + } + if (start_addr != 0) { + procinfo->addProfile (UnwindCursor<A,R>::fAddressSpace.accessors(), UnwindCursor<A,R>::fAddressSpace.wrap(), start_addr, end_addr, fArg); + } + } + + RemoteUnwindProfile *profile = procinfo->findProfile (pc); + if (profile == NULL) + return UNW_ENOINFO; + + int retval = stepWithAssembly (UnwindCursor<A,R>::fAddressSpace, pc, profile, UnwindCursor<A,R>::fRegisters); + if (retval >= 0) { + procinfo->logInfo ("Stepped via stepWithAssembly"); + procinfo->timestamp_stop (step_remote, "stepRemote"); + return retval; + } + + retval = stepByArchitectureDefault (UnwindCursor<A,R>::fAddressSpace, UnwindCursor<A,R>::fRegisters, pc); + procinfo->logInfo ("Stepped via stepByArchitectureDefault"); + procinfo->timestamp_stop (step_remote, "stepRemote"); + return retval; +} + +template <typename A, typename R> +void RemoteUnwindCursor<A,R>::setRemoteContext(void *arg) +{ + // fill in the register state for the currently executing frame. + getRemoteContext (UnwindCursor<A,R>::fAddressSpace.getRemoteProcInfo(), UnwindCursor<A,R>::fRegisters, arg); + + // Flag that this unwind cursor is pointing at the zeroth frame. We don't + // want to use compact unwind info / eh frame info to unwind out of this + // frame. + + fIsLeafFrame = true; + fIsFirstFrame = true; +} + +// This needs to be done in many of the functions and in libuwind.cxx in one or two +// places so I'm defining a convenience method. +template <typename A, typename R> +bool RemoteUnwindCursor<A,R>::caller_regno_to_unwind_regno (int caller_regno, int& unwind_regno) +{ + RemoteProcInfo *procinfo = UnwindCursor<A,R>::fAddressSpace.getRemoteProcInfo (); + if (procinfo == NULL) { + unwind_regno = caller_regno; + return true; + } + if (procinfo->getRegisterMap()->caller_regno_to_unwind_regno (caller_regno, unwind_regno)) + return true; + return false; +} + +template <typename A, typename R> +int RemoteUnwindCursor<A,R>::endOfPrologueInsns (unw_word_t start, unw_word_t end, unw_word_t *endofprologue) +{ + RemoteProcInfo *procinfo = UnwindCursor<A,R>::fAddressSpace.getRemoteProcInfo(); + *endofprologue = start; + if (procinfo == NULL) { + ABORT("findEndOfPrologueSetup called with local unwind."); + return UNW_EUNSPEC; + } + if (procinfo->haveProfile (start) == false) { + uint64_t text_start, text_end, eh_frame_start, eh_frame_len, compact_unwind_start, mh; + if (!procinfo->getImageAddresses (start, mh, text_start, text_end, eh_frame_start, eh_frame_len, compact_unwind_start, fArg)) + return UNW_EUNSPEC; + if (end == 0) { + if (procinfo->haveFuncBounds (mh) == false) { + std::vector<FuncBounds> func_bounds; + // CFI entries are usually around 38 bytes but under-estimate a bit + // because we're not distinguishing between CIEs and FDEs. + if (eh_frame_len > 0) + func_bounds.reserve (eh_frame_len / 16); + if (procinfo->getCachingPolicy() != UNW_CACHE_NONE) { + // cache the entire eh frame section - we'll need to read the whole + // thing anyway so we might as well save it. + uint8_t *eh_buf = (uint8_t *)malloc (eh_frame_len); + if (UnwindCursor<A,R>::fAddressSpace.getBytes (eh_frame_start, eh_frame_len, eh_buf) == 0) + return UNW_EUNSPEC; + RemoteMemoryBlob *ehmem = new RemoteMemoryBlob(eh_buf, free, eh_frame_start, eh_frame_len, mh, NULL); + procinfo->addMemBlob (ehmem); + } + if (CFI_Parser<A>::functionFuncBoundsViaFDE(UnwindCursor<A,R>::fAddressSpace, eh_frame_start, eh_frame_len, func_bounds)) { + procinfo->addFuncBounds(mh, func_bounds); + } + } + uint64_t bounded_start, bounded_end; + if (procinfo->findStartAddr (start, bounded_start, bounded_end)) { + end = bounded_end; + } else { + if (UnwindCursor<A,R>::fAddressSpace.accessors()->get_proc_bounds (procinfo->wrap(), start, &bounded_start, &bounded_end, fArg) != UNW_ESUCCESS) + if (bounded_end != 0) + end = bounded_end; + } + } + if (procinfo->addProfile (UnwindCursor<A,R>::fAddressSpace.accessors(), UnwindCursor<A,R>::fAddressSpace.wrap(), start, end, fArg) == false) + return UNW_EUNSPEC; + } + RemoteUnwindProfile *profile = procinfo->findProfile (start); + if (profile == NULL) + return UNW_ENOINFO; + *endofprologue = profile->fFirstInsnPastPrologue; + return UNW_ESUCCESS; +} + +#endif // SUPPORT_REMOTE_UNWINDING + + +}; // namespace lldb_private + + +#endif // __UNWINDCURSOR_HPP__ diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1-gcc-ext.c b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1-gcc-ext.c new file mode 100644 index 00000000000..7103c719ba2 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1-gcc-ext.c @@ -0,0 +1,282 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- UnwindLevel1-gcc-ext.c ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/* + * Implements gcc extensions to the C++ ABI Exception Handling Level 1 as documented at: + * <http://www.codesourcery.com/cxx-abi/abi-eh.html> + * using libunwind + * + */ + +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> + +#include "libunwind.h" +#include "unwind.h" +#include "libunwind_priv.h" +#include "InternalMacros.h" + + +#if __ppc__ || __i386__ || __x86_64__ + +// +// Called by __cxa_rethrow() +// +EXPORT _Unwind_Reason_Code _Unwind_Resume_or_Rethrow(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_API("_Unwind_Resume_or_Rethrow(ex_obj=%p), private_1=%ld\n", exception_object, exception_object->private_1); + // if this is non-forced and a stopping place was found, then this is a re-throw + // call _Unwind_RaiseException() as if this was a new exception + if ( exception_object->private_1 == 0 ) + _Unwind_RaiseException(exception_object); + + // call through to _Unwind_Resume() which distiguishes between forced and regular exceptions + _Unwind_Resume(exception_object); + ABORT("_Unwind_Resume_or_Rethrow() called _Unwind_RaiseException() which unexpectedly returned"); +} + + + +// +// Called by personality handler during phase 2 to get base address for data relative encodings +// +EXPORT uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context* context) +{ + DEBUG_PRINT_API("_Unwind_GetDataRelBase(context=%p)\n", context); + ABORT("_Unwind_GetDataRelBase() not implemented"); +} + +// +// Called by personality handler during phase 2 to get base address for text relative encodings +// +EXPORT uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context* context) +{ + DEBUG_PRINT_API("_Unwind_GetTextRelBase(context=%p)\n", context); + ABORT("_Unwind_GetTextRelBase() not implemented"); +} + + + +// +// Scans unwind information to find the function that contains the +// specified code address "pc". +// +EXPORT void* _Unwind_FindEnclosingFunction(void* pc) +{ + DEBUG_PRINT_API("_Unwind_FindEnclosingFunction(pc=%p)\n", pc); + ABORT("_Unwind_FindEnclosingFunction() not implemented"); +} + + +// +// Walk every frame and call trace function at each one. If trace function +// returns anything other than _URC_NO_REASON, then walk is terminated. +// +EXPORT _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn callback, void* ref) +{ + unw_cursor_t cursor; + unw_context_t uc; + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + + DEBUG_PRINT_API("_Unwind_Backtrace(callback=%p)\n", callback); + + // walk each frame + while ( true ) { + + // ask libuwind to get next frame (skip over first frame which is _Unwind_Backtrace()) + if ( unw_step(&cursor) <= 0 ) { + DEBUG_PRINT_UNWINDING(" _backtrace: ended because cursor reached bottom of stack, returning %d\n", _URC_END_OF_STACK); + return _URC_END_OF_STACK; + } + + // debugging + if ( DEBUG_PRINT_UNWINDING_TEST ) { + char functionName[512]; + unw_proc_info_t frameInfo; + unw_word_t offset; + unw_get_proc_name(&cursor, functionName, 512, &offset); + unw_get_proc_info(&cursor, &frameInfo); + DEBUG_PRINT_UNWINDING(" _backtrace: start_ip=0x%llX, func=%s, lsda=0x%llX, context=%p\n", + frameInfo.start_ip, functionName, frameInfo.lsda, &cursor); + } + + // call trace function with this frame + _Unwind_Reason_Code result = (*callback)((struct _Unwind_Context*)(&cursor), ref); + if ( result != _URC_NO_REASON ) { + DEBUG_PRINT_UNWINDING(" _backtrace: ended because callback returned %d\n", result); + return result; + } + } +} + + +// +// Find dwarf unwind info for an address 'pc' in some function. +// +EXPORT const void* _Unwind_Find_FDE(const void* pc, struct dwarf_eh_bases* bases) +{ + // This is slow, but works. + // We create an unwind cursor then alter the IP to be pc + unw_cursor_t cursor; + unw_context_t uc; + unw_proc_info_t info; + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(long)pc); + unw_get_proc_info(&cursor, &info); + bases->tbase = info.extra; + bases->dbase = 0; // dbase not used on Mac OS X + bases->func = info.start_ip; + DEBUG_PRINT_API("_Unwind_Find_FDE(pc=%p) => %p\n", pc, (void*)(long)info.unwind_info); + return (void*)(long)info.unwind_info; +} + + + +EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context* context) +{ + unw_cursor_t* cursor = (unw_cursor_t*)context; + unw_word_t result; + unw_get_reg(cursor, UNW_REG_SP, &result); + DEBUG_PRINT_API("_Unwind_GetCFA(context=%p) => 0x%llX\n", context, (uint64_t)result); + return result; +} + + +// +// Called by personality handler during phase 2 to get instruction pointer. +// ipBefore is a boolean that says if IP is already adjusted to be the call +// site address. Normally IP is the return address. +// +EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context* context, int* ipBefore) +{ + DEBUG_PRINT_API("_Unwind_GetIPInfo(context=%p)\n", context); + *ipBefore = 0; + return _Unwind_GetIP(context); +} + + +// +// Called by programs with dynamic code generators that want +// to register a dynamically generated FDE. +// This function has existed on Mac OS X since 10.4, but +// never worked before. +// +EXPORT void __register_frame(const void* fde) +{ + DEBUG_PRINT_API("__register_frame(%p)\n", fde); + _unw_add_dynamic_fde((unw_word_t)(uintptr_t)fde); +} + + +// +// Called by programs with dynamic code generators that want +// to unregister a dynamically generated FDE. +// This function has existed on Mac OS X since 10.4, but +// never worked before. +// +EXPORT void __deregister_frame(const void* fde) +{ + DEBUG_PRINT_API("__deregister_frame(%p)\n", fde); + _unw_remove_dynamic_fde((unw_word_t)(uintptr_t)fde); +} + + + +// +// The following register/deregister functions are gcc extensions. +// They have existed on Mac OS X, but have never worked because Mac OS X +// before 10.6 used keymgr to track known FDEs, but these functions +// never got updated to use keymgr. +// For now, we implement these as do-nothing functions to keep any existing +// applications working. We also add the not in 10.6 symbol so that nwe +// application won't be able to use them. +// + +EXPORT void __register_frame_info_bases(const void* fde, void* ob, void* tb, void* db) +{ + DEBUG_PRINT_API("__register_frame_info_bases(%p,%p, %p, %p)\n", fde, ob, tb, db); + // do nothing, this function never worked in Mac OS X +} + +EXPORT void __register_frame_info(const void* fde, void* ob) +{ + DEBUG_PRINT_API("__register_frame_info(%p, %p)\n", fde, ob); + // do nothing, this function never worked in Mac OS X +} + + +EXPORT void __register_frame_info_table_bases(const void* fde, void* ob, void* tb, void* db) +{ + DEBUG_PRINT_API("__register_frame_info_table_bases(%p,%p, %p, %p)\n", fde, ob, tb, db); + // do nothing, this function never worked in Mac OS X +} + +EXPORT void __register_frame_info_table(const void* fde, void* ob) +{ + DEBUG_PRINT_API("__register_frame_info_table(%p, %p)\n", fde, ob); + // do nothing, this function never worked in Mac OS X +} + +EXPORT void __register_frame_table(const void* fde) +{ + DEBUG_PRINT_API("__register_frame_table(%p)\n", fde); + // do nothing, this function never worked in Mac OS X +} + +EXPORT void* __deregister_frame_info(const void* fde) +{ + DEBUG_PRINT_API("__deregister_frame_info(%p)\n", fde); + // do nothing, this function never worked in Mac OS X + return NULL; +} + +EXPORT void* __deregister_frame_info_bases(const void* fde) +{ + DEBUG_PRINT_API("__deregister_frame_info_bases(%p)\n", fde); + // do nothing, this function never worked in Mac OS X + return NULL; +} + + + + +// +// symbols in libSystem.dylib in 10.6 and later, but are in libgcc_s.dylib in earlier versions +// +NOT_HERE_BEFORE_10_6(_Unwind_Backtrace) +NOT_HERE_BEFORE_10_6(_Unwind_FindEnclosingFunction) +NOT_HERE_BEFORE_10_6(_Unwind_GetCFA) +NOT_HERE_BEFORE_10_6(_Unwind_GetDataRelBase) +NOT_HERE_BEFORE_10_6(_Unwind_GetTextRelBase) +NOT_HERE_BEFORE_10_6(_Unwind_Resume_or_Rethrow) +NOT_HERE_BEFORE_10_6(_Unwind_GetIPInfo) + +NOT_HERE_BEFORE_10_6(__register_frame) +NOT_HERE_BEFORE_10_6(__deregister_frame) + + +// +// symbols in libSystem.dylib for compatibility, but we don't want any new code using them +// +NEVER_HERE(__register_frame_info_bases) +NEVER_HERE(__register_frame_info) +NEVER_HERE(__register_frame_info_table_bases) +NEVER_HERE(__register_frame_info_table) +NEVER_HERE(__register_frame_table) +NEVER_HERE(__deregister_frame_info) +NEVER_HERE(__deregister_frame_info_bases) + + +#endif // __ppc__ || __i386__ || __x86_64__ + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1.c b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1.c new file mode 100644 index 00000000000..3aa2b6f552c --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1.c @@ -0,0 +1,443 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- UnwindLevel1.c ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/* + * + * Implements C++ ABI Exception Handling Level 1 as documented at: + * <http://www.codesourcery.com/cxx-abi/abi-eh.html> + * using libunwind + * + */ + +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "libunwind.h" +#include "unwind.h" +#include "InternalMacros.h" + +#if __ppc__ || __i386__ || __x86_64__ + +static _Unwind_Reason_Code unwind_phase1(unw_context_t* uc, struct _Unwind_Exception* exception_object) +{ + unw_cursor_t cursor1; + unw_init_local(&cursor1, uc); + + // walk each frame looking for a place to stop + for (bool handlerNotFound = true; handlerNotFound; ) { + + // ask libuwind to get next frame (skip over first which is _Unwind_RaiseException) + int stepResult = unw_step(&cursor1); + if ( stepResult == 0 ) { + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): unw_step() reached bottom => _URC_END_OF_STACK\n", exception_object); + return _URC_END_OF_STACK; + } + else if ( stepResult < 0 ) { + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): unw_step failed => _URC_FATAL_PHASE1_ERROR\n", exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + + // see if frame has code to run (has personality routine) + unw_proc_info_t frameInfo; + unw_word_t sp; + if ( unw_get_proc_info(&cursor1, &frameInfo) != UNW_ESUCCESS ) { + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): unw_get_proc_info failed => _URC_FATAL_PHASE1_ERROR\n", exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + + // debugging + if ( DEBUG_PRINT_UNWINDING_TEST ) { + char functionName[512]; + unw_word_t offset; + if ( (unw_get_proc_name(&cursor1, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip+offset > frameInfo.end_ip) ) + strcpy(functionName, ".anonymous."); + unw_word_t pc; + unw_get_reg(&cursor1, UNW_REG_IP, &pc); + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): pc=0x%llX, start_ip=0x%llX, func=%s, lsda=0x%llX, personality=0x%llX\n", + exception_object, pc, frameInfo.start_ip, functionName, frameInfo.lsda, frameInfo.handler); + } + + // if there is a personality routine, ask it if it will want to stop at this frame + if ( frameInfo.handler != 0 ) { + __personality_routine p = (__personality_routine)(long)(frameInfo.handler); + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): calling personality function %p\n", exception_object, p); + _Unwind_Reason_Code personalityResult = (*p)(1, _UA_SEARCH_PHASE, + exception_object->exception_class, exception_object, + (struct _Unwind_Context*)(&cursor1)); + switch ( personalityResult ) { + case _URC_HANDLER_FOUND: + // found a catch clause or locals that need destructing in this frame + // stop search and remember stack pointer at the frame + handlerNotFound = false; + unw_get_reg(&cursor1, UNW_REG_SP, &sp); + exception_object->private_2 = sp; + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_HANDLER_FOUND\n", exception_object); + return _URC_NO_REASON; + + case _URC_CONTINUE_UNWIND: + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object); + // continue unwinding + break; + + default: + // something went wrong + DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR\n", exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + } + } + return _URC_NO_REASON; +} + + +static _Unwind_Reason_Code unwind_phase2(unw_context_t* uc, struct _Unwind_Exception* exception_object) +{ + unw_cursor_t cursor2; + unw_init_local(&cursor2, uc); + + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p)\n", exception_object); + + // walk each frame until we reach where search phase said to stop + while ( true ) { + + // ask libuwind to get next frame (skip over first which is _Unwind_RaiseException) + int stepResult = unw_step(&cursor2); + if ( stepResult == 0 ) { + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached bottom => _URC_END_OF_STACK\n", exception_object); + return _URC_END_OF_STACK; + } + else if ( stepResult < 0 ) { + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step failed => _URC_FATAL_PHASE1_ERROR\n", exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // get info about this frame + unw_word_t sp; + unw_proc_info_t frameInfo; + unw_get_reg(&cursor2, UNW_REG_SP, &sp); + if ( unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS ) { + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_get_proc_info failed => _URC_FATAL_PHASE1_ERROR\n", exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // debugging + if ( DEBUG_PRINT_UNWINDING_TEST ) { + char functionName[512]; + unw_word_t offset; + if ( (unw_get_proc_name(&cursor2, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip+offset > frameInfo.end_ip) ) + strcpy(functionName, ".anonymous."); + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): start_ip=0x%llX, func=%s, sp=0x%llX, lsda=0x%llX, personality=0x%llX\n", + exception_object, frameInfo.start_ip, functionName, sp, frameInfo.lsda, frameInfo.handler); + } + + // if there is a personality routine, tell it we are unwinding + if ( frameInfo.handler != 0 ) { + __personality_routine p = (__personality_routine)(long)(frameInfo.handler); + _Unwind_Action action = _UA_CLEANUP_PHASE; + if ( sp == exception_object->private_2 ) + action = (_Unwind_Action)(_UA_CLEANUP_PHASE|_UA_HANDLER_FRAME); // tell personality this was the frame it marked in phase 1 + _Unwind_Reason_Code personalityResult = (*p)(1, action, + exception_object->exception_class, exception_object, + (struct _Unwind_Context*)(&cursor2)); + switch ( personalityResult ) { + case _URC_CONTINUE_UNWIND: + // continue unwinding + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object); + if ( sp == exception_object->private_2 ) { + // phase 1 said we would stop at this frame, but we did not... + ABORT("during phase1 personality function said it would stop here, but now if phase2 it did not stop here"); + } + break; + case _URC_INSTALL_CONTEXT: + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT\n", exception_object); + // personality routine says to transfer control to landing pad + // we may get control back if landing pad calls _Unwind_Resume() + if ( DEBUG_PRINT_UNWINDING_TEST ) { + unw_word_t pc; + unw_word_t sp; + unw_get_reg(&cursor2, UNW_REG_IP, &pc); + unw_get_reg(&cursor2, UNW_REG_SP, &sp); + DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering user code with ip=0x%llX, sp=0x%llX\n", exception_object, pc, sp); + } + unw_resume(&cursor2); + // unw_resume() only returns if there was an error + return _URC_FATAL_PHASE2_ERROR; + default: + // something went wrong + DEBUG_MESSAGE("personality function returned unknown result %d", personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + } + + // clean up phase did not resume at the frame that the search phase said it would + return _URC_FATAL_PHASE2_ERROR; +} + + +static _Unwind_Reason_Code unwind_phase2_forced(unw_context_t* uc, struct _Unwind_Exception* exception_object, + _Unwind_Stop_Fn stop, void* stop_parameter) +{ + unw_cursor_t cursor2; + unw_init_local(&cursor2, uc); + + // walk each frame until we reach where search phase said to stop + while ( unw_step(&cursor2) > 0 ) { + + // get info about this frame + unw_proc_info_t frameInfo; + if ( unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS ) { + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): unw_step failed => _URC_END_OF_STACK\n", exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + + // debugging + if ( DEBUG_PRINT_UNWINDING_TEST ) { + char functionName[512]; + unw_word_t offset; + if ( (unw_get_proc_name(&cursor2, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip+offset > frameInfo.end_ip) ) + strcpy(functionName, ".anonymous."); + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): start_ip=0x%llX, func=%s, lsda=0x%llX, personality=0x%llX\n", + exception_object, frameInfo.start_ip, functionName, frameInfo.lsda, frameInfo.handler); + } + + // call stop function at each frame + _Unwind_Action action = (_Unwind_Action)(_UA_FORCE_UNWIND|_UA_CLEANUP_PHASE); + _Unwind_Reason_Code stopResult = (*stop)(1, action, + exception_object->exception_class, exception_object, + (struct _Unwind_Context*)(&cursor2), stop_parameter); + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): stop function returned %d\n", exception_object, stopResult); + if ( stopResult != _URC_NO_REASON ) { + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): stopped by stop function\n", exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // if there is a personality routine, tell it we are unwinding + if ( frameInfo.handler != 0 ) { + __personality_routine p = (__personality_routine)(long)(frameInfo.handler); + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling personality function %p\n", exception_object, p); + _Unwind_Reason_Code personalityResult = (*p)(1, action, + exception_object->exception_class, exception_object, + (struct _Unwind_Context*)(&cursor2)); + switch ( personalityResult ) { + case _URC_CONTINUE_UNWIND: + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned _URC_CONTINUE_UNWIND\n", exception_object); + // destructors called, continue unwinding + break; + case _URC_INSTALL_CONTEXT: + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned _URC_INSTALL_CONTEXT\n", exception_object); + // we may get control back if landing pad calls _Unwind_Resume() + unw_resume(&cursor2); + break; + default: + // something went wrong + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned %d, _URC_FATAL_PHASE2_ERROR\n", + exception_object, personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + } + + // call stop function one last time and tell it we've reached the end of the stack + DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop function with _UA_END_OF_STACK\n", exception_object); + _Unwind_Action lastAction = (_Unwind_Action)(_UA_FORCE_UNWIND|_UA_CLEANUP_PHASE|_UA_END_OF_STACK); + (*stop)(1, lastAction, exception_object->exception_class, exception_object, (struct _Unwind_Context*)(&cursor2), stop_parameter); + + // clean up phase did not resume at the frame that the search phase said it would + return _URC_FATAL_PHASE2_ERROR; +} + + +// +// Called by __cxa_throw. Only returns if there is a fatal error +// +EXPORT _Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_API("_Unwind_RaiseException(ex_obj=%p)\n", exception_object); + unw_context_t uc; + unw_getcontext(&uc); + + // mark that this is a non-forced unwind, so _Unwind_Resume() can do the right thing + exception_object->private_1 = 0; + exception_object->private_2 = 0; + + // phase 1: the search phase + _Unwind_Reason_Code phase1 = unwind_phase1(&uc, exception_object); + if ( phase1 != _URC_NO_REASON ) + return phase1; + + // phase 2: the clean up phase + return unwind_phase2(&uc, exception_object); +} + + +// +// When _Unwind_RaiseException() is in phase2, it hands control +// to the personality function at each frame. The personality +// may force a jump to a landing pad in that function, the landing +// pad code may then call _Unwind_Resume() to continue with the +// unwinding. Note: the call to _Unwind_Resume() is from compiler +// geneated user code. All other _Unwind_* routines are called +// by the C++ runtime __cxa_* routines. +// +// Re-throwing an exception is implemented by having the code call +// __cxa_rethrow() which in turn calls _Unwind_Resume_or_Rethrow() +// +EXPORT void _Unwind_Resume(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_API("_Unwind_Resume(ex_obj=%p)\n", exception_object); + unw_context_t uc; + unw_getcontext(&uc); + + if ( exception_object->private_1 != 0 ) + unwind_phase2_forced(&uc, exception_object, (_Unwind_Stop_Fn)exception_object->private_1, (void*)exception_object->private_2); + else + unwind_phase2(&uc, exception_object); + + // clients assume _Unwind_Resume() does not return, so all we can do is abort. + ABORT("_Unwind_Resume() can't return"); +} + + + +// +// Not used by C++. +// Unwinds stack, calling "stop" function at each frame +// Could be used to implement longjmp(). +// +EXPORT _Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception* exception_object, _Unwind_Stop_Fn stop, void* stop_parameter) +{ + DEBUG_PRINT_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)\n", exception_object, stop); + unw_context_t uc; + unw_getcontext(&uc); + + // mark that this is a forced unwind, so _Unwind_Resume() can do the right thing + exception_object->private_1 = (uintptr_t)stop; + exception_object->private_2 = (uintptr_t)stop_parameter; + + // doit + return unwind_phase2_forced(&uc, exception_object, stop, stop_parameter); +} + + +// +// Called by personality handler during phase 2 to get LSDA for current frame +// +EXPORT uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context* context) +{ + unw_cursor_t* cursor = (unw_cursor_t*)context; + unw_proc_info_t frameInfo; + uintptr_t result = 0; + if ( unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS ) + result = frameInfo.lsda; + DEBUG_PRINT_API("_Unwind_GetLanguageSpecificData(context=%p) => 0x%lX\n", context, result); + if ( result != 0 ) { + if ( *((uint8_t*)result) != 0xFF ) + DEBUG_MESSAGE("lsda at 0x%lX does not start with 0xFF\n", result); + } + return result; +} + + +// +// Called by personality handler during phase 2 to get register values +// +EXPORT uintptr_t _Unwind_GetGR(struct _Unwind_Context* context, int index) +{ + unw_cursor_t* cursor = (unw_cursor_t*)context; + unw_word_t result; + unw_get_reg(cursor, index, &result); + DEBUG_PRINT_API("_Unwind_GetGR(context=%p, reg=%d) => 0x%llX\n", context, index, (uint64_t)result); + return result; +} + + +// +// Called by personality handler during phase 2 to alter register values +// +EXPORT void _Unwind_SetGR(struct _Unwind_Context* context, int index, uintptr_t new_value) +{ + DEBUG_PRINT_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%0llX)\n", context, index, (uint64_t)new_value); + unw_cursor_t* cursor = (unw_cursor_t*)context; + unw_set_reg(cursor, index, new_value); +} + + +// +// Called by personality handler during phase 2 to get instruction pointer +// +EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context* context) +{ + unw_cursor_t* cursor = (unw_cursor_t*)context; + unw_word_t result; + unw_get_reg(cursor, UNW_REG_IP, &result); + DEBUG_PRINT_API("_Unwind_GetIP(context=%p) => 0x%llX\n", context, (uint64_t)result); + return result; +} + + +// +// Called by personality handler during phase 2 to alter instruction pointer +// +EXPORT void _Unwind_SetIP(struct _Unwind_Context* context, uintptr_t new_value) +{ + DEBUG_PRINT_API("_Unwind_SetIP(context=%p, value=0x%0llX)\n", context, (uint64_t)new_value); + unw_cursor_t* cursor = (unw_cursor_t*)context; + unw_set_reg(cursor, UNW_REG_IP, new_value); +} + + +// +// Called by personality handler during phase 2 to find the start of the function +// +EXPORT uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context* context) +{ + unw_cursor_t* cursor = (unw_cursor_t*)context; + unw_proc_info_t frameInfo; + uintptr_t result = 0; + if ( unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS ) + result = frameInfo.start_ip; + DEBUG_PRINT_API("_Unwind_GetRegionStart(context=%p) => 0x%lX\n", context, result); + return result; +} + + +// +// Called by personality handler during phase 2 if a foreign exception is caught +// +EXPORT void _Unwind_DeleteException(struct _Unwind_Exception* exception_object) +{ + DEBUG_PRINT_API("_Unwind_DeleteException(ex_obj=%p)\n", exception_object); + if ( exception_object->exception_cleanup != NULL ) + (*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, exception_object); +} + + + + +// +// symbols in libSystem.dylib in 10.6 and later, but are in libgcc_s.dylib in earlier versions +// +NOT_HERE_BEFORE_10_6(_Unwind_DeleteException) +NOT_HERE_BEFORE_10_6(_Unwind_Find_FDE) +NOT_HERE_BEFORE_10_6(_Unwind_ForcedUnwind) +NOT_HERE_BEFORE_10_6(_Unwind_GetGR) +NOT_HERE_BEFORE_10_6(_Unwind_GetIP) +NOT_HERE_BEFORE_10_6(_Unwind_GetLanguageSpecificData) +NOT_HERE_BEFORE_10_6(_Unwind_GetRegionStart) +NOT_HERE_BEFORE_10_6(_Unwind_RaiseException) +NOT_HERE_BEFORE_10_6(_Unwind_Resume) +NOT_HERE_BEFORE_10_6(_Unwind_SetGR) +NOT_HERE_BEFORE_10_6(_Unwind_SetIP) + +#endif // __ppc__ || __i386__ || __x86_64__ diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/dwarf2.h b/lldb/source/Plugins/Process/Utility/libunwind/src/dwarf2.h new file mode 100644 index 00000000000..83414332c5e --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/dwarf2.h @@ -0,0 +1,245 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- dwarf2.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/* These constants were taken from version 3 of the DWARF standard, + which is Copyright (c) 2005 Free Standards Group, and + Copyright (c) 1992, 1993 UNIX International, Inc. +*/ + + +#ifndef __DWARF2__ +#define __DWARF2__ + +namespace lldb_private { + +// dwarf unwind instructions +enum { + DW_CFA_nop = 0x0, + DW_CFA_set_loc = 0x1, + DW_CFA_advance_loc1 = 0x2, + DW_CFA_advance_loc2 = 0x3, + DW_CFA_advance_loc4 = 0x4, + DW_CFA_offset_extended = 0x5, + DW_CFA_restore_extended = 0x6, + DW_CFA_undefined = 0x7, + DW_CFA_same_value = 0x8, + DW_CFA_register = 0x9, + DW_CFA_remember_state = 0xA, + DW_CFA_restore_state = 0xB, + DW_CFA_def_cfa = 0xC, + DW_CFA_def_cfa_register = 0xD, + DW_CFA_def_cfa_offset = 0xE, + DW_CFA_def_cfa_expression = 0xF, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + DW_CFA_advance_loc = 0x40, // high 2 bits are 0x1, lower 6 bits are delta + DW_CFA_offset = 0x80, // high 2 bits are 0x2, lower 6 bits are register + DW_CFA_restore = 0xC0, // high 2 bits are 0x3, lower 6 bits are register + + // GNU extensions + DW_CFA_GNU_window_save = 0x2D, + DW_CFA_GNU_args_size = 0x2E, + DW_CFA_GNU_negative_offset_extended = 0x2F +}; + + +// FSF exception handling Pointer-Encoding constants +// Used in CFI augmentation by gcc compiler +enum { + DW_EH_PE_ptr = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_signed = 0x08, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_absptr = 0x00, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + DW_EH_PE_indirect = 0x80, + DW_EH_PE_omit = 0xFF +}; + + +// DWARF expressions +enum { + DW_OP_addr = 0x03, // constant address (size target specific) + DW_OP_deref = 0x06, + DW_OP_const1u = 0x08, // 1-byte constant + DW_OP_const1s = 0x09, // 1-byte constant + DW_OP_const2u = 0x0A, // 2-byte constant + DW_OP_const2s = 0x0B, // 2-byte constant + DW_OP_const4u = 0x0C, // 4-byte constant + DW_OP_const4s = 0x0D, // 4-byte constant + DW_OP_const8u = 0x0E, // 8-byte constant + DW_OP_const8s = 0x0F, // 8-byte constant + DW_OP_constu = 0x10, // ULEB128 constant + DW_OP_consts = 0x11, // SLEB128 constant + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, // 1-byte stack index + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1A, + DW_OP_div = 0x1B, + DW_OP_minus = 0x1C, + DW_OP_mod = 0x1D, + DW_OP_mul = 0x1E, + DW_OP_neg = 0x1F, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, // ULEB128 addend + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_skip = 0x2F, // signed 2-byte constant + DW_OP_bra = 0x28, // signed 2-byte constant + DW_OP_eq = 0x29, + DW_OP_ge = 0x2A, + DW_OP_gt = 0x2B, + DW_OP_le = 0x2C, + DW_OP_lt = 0x2D, + DW_OP_ne = 0x2E, + DW_OP_lit0 = 0x30, // Literal 0 + DW_OP_lit1 = 0x31, // Literal 1 + DW_OP_lit2 = 0x32, // Literal 2 + DW_OP_lit3 = 0x33, // Literal 3 + DW_OP_lit4 = 0x34, // Literal 4 + DW_OP_lit5 = 0x35, // Literal 5 + DW_OP_lit6 = 0x36, // Literal 6 + DW_OP_lit7 = 0x37, // Literal 7 + DW_OP_lit8 = 0x38, // Literal 8 + DW_OP_lit9 = 0x39, // Literal 9 + DW_OP_lit10 = 0x3A, // Literal 10 + DW_OP_lit11 = 0x3B, // Literal 11 + DW_OP_lit12 = 0x3C, // Literal 12 + DW_OP_lit13 = 0x3D, // Literal 13 + DW_OP_lit14 = 0x3E, // Literal 14 + DW_OP_lit15 = 0x3F, // Literal 15 + DW_OP_lit16 = 0x40, // Literal 16 + DW_OP_lit17 = 0x41, // Literal 17 + DW_OP_lit18 = 0x42, // Literal 18 + DW_OP_lit19 = 0x43, // Literal 19 + DW_OP_lit20 = 0x44, // Literal 20 + DW_OP_lit21 = 0x45, // Literal 21 + DW_OP_lit22 = 0x46, // Literal 22 + DW_OP_lit23 = 0x47, // Literal 23 + DW_OP_lit24 = 0x48, // Literal 24 + DW_OP_lit25 = 0x49, // Literal 25 + DW_OP_lit26 = 0x4A, // Literal 26 + DW_OP_lit27 = 0x4B, // Literal 27 + DW_OP_lit28 = 0x4C, // Literal 28 + DW_OP_lit29 = 0x4D, // Literal 29 + DW_OP_lit30 = 0x4E, // Literal 30 + DW_OP_lit31 = 0x4F, // Literal 31 + DW_OP_reg0 = 0x50, // Contents of reg0 + DW_OP_reg1 = 0x51, // Contents of reg1 + DW_OP_reg2 = 0x52, // Contents of reg2 + DW_OP_reg3 = 0x53, // Contents of reg3 + DW_OP_reg4 = 0x54, // Contents of reg4 + DW_OP_reg5 = 0x55, // Contents of reg5 + DW_OP_reg6 = 0x56, // Contents of reg6 + DW_OP_reg7 = 0x57, // Contents of reg7 + DW_OP_reg8 = 0x58, // Contents of reg8 + DW_OP_reg9 = 0x59, // Contents of reg9 + DW_OP_reg10 = 0x5A, // Contents of reg10 + DW_OP_reg11 = 0x5B, // Contents of reg11 + DW_OP_reg12 = 0x5C, // Contents of reg12 + DW_OP_reg13 = 0x5D, // Contents of reg13 + DW_OP_reg14 = 0x5E, // Contents of reg14 + DW_OP_reg15 = 0x5F, // Contents of reg15 + DW_OP_reg16 = 0x60, // Contents of reg16 + DW_OP_reg17 = 0x61, // Contents of reg17 + DW_OP_reg18 = 0x62, // Contents of reg18 + DW_OP_reg19 = 0x63, // Contents of reg19 + DW_OP_reg20 = 0x64, // Contents of reg20 + DW_OP_reg21 = 0x65, // Contents of reg21 + DW_OP_reg22 = 0x66, // Contents of reg22 + DW_OP_reg23 = 0x67, // Contents of reg23 + DW_OP_reg24 = 0x68, // Contents of reg24 + DW_OP_reg25 = 0x69, // Contents of reg25 + DW_OP_reg26 = 0x6A, // Contents of reg26 + DW_OP_reg27 = 0x6B, // Contents of reg27 + DW_OP_reg28 = 0x6C, // Contents of reg28 + DW_OP_reg29 = 0x6D, // Contents of reg29 + DW_OP_reg30 = 0x6E, // Contents of reg30 + DW_OP_reg31 = 0x6F, // Contents of reg31 + DW_OP_breg0 = 0x70, // base register 0 + SLEB128 offset + DW_OP_breg1 = 0x71, // base register 1 + SLEB128 offset + DW_OP_breg2 = 0x72, // base register 2 + SLEB128 offset + DW_OP_breg3 = 0x73, // base register 3 + SLEB128 offset + DW_OP_breg4 = 0x74, // base register 4 + SLEB128 offset + DW_OP_breg5 = 0x75, // base register 5 + SLEB128 offset + DW_OP_breg6 = 0x76, // base register 6 + SLEB128 offset + DW_OP_breg7 = 0x77, // base register 7 + SLEB128 offset + DW_OP_breg8 = 0x78, // base register 8 + SLEB128 offset + DW_OP_breg9 = 0x79, // base register 9 + SLEB128 offset + DW_OP_breg10 = 0x7A, // base register 10 + SLEB128 offset + DW_OP_breg11 = 0x7B, // base register 11 + SLEB128 offset + DW_OP_breg12 = 0x7C, // base register 12 + SLEB128 offset + DW_OP_breg13 = 0x7D, // base register 13 + SLEB128 offset + DW_OP_breg14 = 0x7E, // base register 14 + SLEB128 offset + DW_OP_breg15 = 0x7F, // base register 15 + SLEB128 offset + DW_OP_breg16 = 0x80, // base register 16 + SLEB128 offset + DW_OP_breg17 = 0x81, // base register 17 + SLEB128 offset + DW_OP_breg18 = 0x82, // base register 18 + SLEB128 offset + DW_OP_breg19 = 0x83, // base register 19 + SLEB128 offset + DW_OP_breg20 = 0x84, // base register 20 + SLEB128 offset + DW_OP_breg21 = 0x85, // base register 21 + SLEB128 offset + DW_OP_breg22 = 0x86, // base register 22 + SLEB128 offset + DW_OP_breg23 = 0x87, // base register 23 + SLEB128 offset + DW_OP_breg24 = 0x88, // base register 24 + SLEB128 offset + DW_OP_breg25 = 0x89, // base register 25 + SLEB128 offset + DW_OP_breg26 = 0x8A, // base register 26 + SLEB128 offset + DW_OP_breg27 = 0x8B, // base register 27 + SLEB128 offset + DW_OP_breg28 = 0x8C, // base register 28 + SLEB128 offset + DW_OP_breg29 = 0x8D, // base register 29 + SLEB128 offset + DW_OP_breg30 = 0x8E, // base register 30 + SLEB128 offset + DW_OP_breg31 = 0x8F, // base register 31 + SLEB128 offset + DW_OP_regx = 0x90, // ULEB128 register + DW_OP_fbreg = 0x91, // SLEB128 offset + DW_OP_bregx = 0x92, // ULEB128 register followed by SLEB128 offset + DW_OP_piece = 0x93, // ULEB128 size of piece addressed + DW_OP_deref_size = 0x94, // 1-byte size of data retrieved + DW_OP_xderef_size = 0x95, // 1-byte size of data retrieved + DW_OP_nop = 0x96, + DW_OP_push_object_addres = 0x97, + DW_OP_call2 = 0x98, // 2-byte offset of DIE + DW_OP_call4 = 0x99, // 4-byte offset of DIE + DW_OP_call_ref = 0x9A, // 4- or 8-byte offset of DIE + DW_OP_lo_user = 0xE0, + DW_OP_APPLE_uninit = 0xF0, + DW_OP_hi_user = 0xFF +}; + + +}; // namespace lldb_private + + +#endif + + + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/libunwind_priv.h b/lldb/source/Plugins/Process/Utility/libunwind/src/libunwind_priv.h new file mode 100644 index 00000000000..fe25780c538 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/libunwind_priv.h @@ -0,0 +1,35 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- libunwind_priv.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __LIBUNWIND_PRIV__ +#define __LIBUNWIND_PRIV__ + +namespace lldb_private { +#include "libunwind.h" + +#ifdef __cplusplus +extern "C" { +#endif + // SPI + extern void unw_iterate_dwarf_unwind_cache(void (*func)(unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)); + + // IPI + extern void _unw_add_dynamic_fde(unw_word_t fde); + extern void _unw_remove_dynamic_fde(unw_word_t fde); + +#ifdef __cplusplus +} +#endif + +}; // namespace lldb_private + + +#endif + diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/libuwind.cxx b/lldb/source/Plugins/Process/Utility/libunwind/src/libuwind.cxx new file mode 100644 index 00000000000..e7e66a452f0 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/libuwind.cxx @@ -0,0 +1,421 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/ +//===-- libuwind.cxx --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if __ppc__ || __i386__ || __x86_64__ + +#include <mach/mach_types.h> +#include <mach/machine.h> +#include <new> + +#include "libunwind.h" +#include "libunwind_priv.h" + +#include "UnwindCursor.hpp" +#include "AddressSpace.hpp" + +#include "RemoteProcInfo.hpp" + +namespace lldb_private { + +// setup debug logging hooks +INITIALIZE_DEBUG_PRINT_API +INITIALIZE_DEBUG_PRINT_UNWINDING + +// internal object to represent this processes address space +static LocalAddressSpace sThisAddressSpace; + +#pragma mark Local API + +/// +/// record the registers and stack position of the caller +/// +extern int unw_getcontext(unw_context_t*); +// note: unw_getcontext() implemented in assembly + +/// +/// create a cursor of a thread in this process given 'context' recorded by unw_getcontext() +/// +EXPORT int unw_init_local(unw_cursor_t* cursor, unw_context_t* context) +{ + DEBUG_PRINT_API("unw_init_local(cursor=%p, context=%p)\n", cursor, context); + // use "placement new" to allocate UnwindCursor in the cursor buffer +#if __i386__ + new ((void*)cursor) UnwindCursor<LocalAddressSpace,Registers_x86>(context, sThisAddressSpace); +#elif __x86_64__ + new ((void*)cursor) UnwindCursor<LocalAddressSpace,Registers_x86_64>(context, sThisAddressSpace); +#elif __ppc__ + new ((void*)cursor) UnwindCursor<LocalAddressSpace,Registers_ppc>(context, sThisAddressSpace); +#endif + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + co->setInfoBasedOnIPRegister(NULL); + + return UNW_ESUCCESS; +} + +/// +/// move cursor to next frame +/// +EXPORT int unw_step(unw_cursor_t* cursor) +{ + DEBUG_PRINT_API("unw_step(cursor=%p)\n", cursor); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + return co->step(); +} + +/// +/// get value of specified register at cursor position in stack frame +/// +EXPORT int unw_get_reg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_word_t* value) +{ + DEBUG_PRINT_API("unw_get_reg(cursor=%p, regNum=%d, &value=%p)\n", cursor, regNum, value); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + + if (co->validReg(regNum) == 0) + return UNW_EBADREG; + return co->getReg(regNum, value); +} + +/// +/// get value of specified float register at cursor position in stack frame +/// +EXPORT int unw_get_fpreg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_fpreg_t* value) +{ + DEBUG_PRINT_API("unw_get_fpreg(cursor=%p, regNum=%d, &value=%p)\n", cursor, regNum, value); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + + if ( co->validFloatReg(regNum) ) { + return co->getFloatReg(regNum, value); + } + return UNW_EBADREG; +} + +/// +/// set value of specified register at cursor position in stack frame +/// +EXPORT int unw_set_reg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_word_t value) +{ + DEBUG_PRINT_API("unw_set_reg(cursor=%p, regNum=%d, value=0x%llX)\n", cursor, regNum, value); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + + if ( co->validReg(regNum) ) { + co->setReg(regNum, value); + // specical case altering IP to re-find info (being called by personality function) + if ( regNum == UNW_REG_IP ) { + unw_proc_info_t info; + co->getInfo(&info); + uint64_t orgArgSize = info.gp; + uint64_t orgFuncStart = info.start_ip; + co->setInfoBasedOnIPRegister(false); + // and adjust REG_SP if there was a DW_CFA_GNU_args_size + if ( (orgFuncStart == info.start_ip) && (orgArgSize != 0) ) + co->setReg(UNW_REG_SP, co->getReg(UNW_REG_SP) + orgArgSize); + } + return UNW_ESUCCESS; + } + return UNW_EBADREG; +} + +/// +/// set value of specified float register at cursor position in stack frame +/// +EXPORT int unw_set_fpreg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_fpreg_t value) +{ + DEBUG_PRINT_API("unw_set_fpreg(cursor=%p, regNum=%d, value=%g)\n", cursor, regNum, value); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + + if ( co->validFloatReg(regNum) ) { + return co->setFloatReg(regNum, value); + } + return UNW_EBADREG; +} + +/// +/// resume execution at cursor position (aka longjump) +/// +EXPORT int unw_resume(unw_cursor_t* cursor) +{ + DEBUG_PRINT_API("unw_resume(cursor=%p)\n", cursor); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + + co->jumpto(); + return UNW_EUNSPEC; +} + +/// +/// returns the name of a register +/// +EXPORT const char* unw_regname(unw_cursor_t* cursor, unw_regnum_t regNum) +{ + DEBUG_PRINT_API("unw_regname(cursor=%p, regNum=%d)\n", cursor, regNum); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + return co->getRegisterName(regNum); +} + +/// +/// get unwind info at cursor position in stack frame +/// +EXPORT int unw_get_proc_info(unw_cursor_t* cursor, unw_proc_info_t* info) +{ + DEBUG_PRINT_API("unw_get_proc_info(cursor=%p, &info=%p)\n", cursor, info); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + co->getInfo(info); + if ( info->end_ip == 0 ) + return UNW_ENOINFO; + else + return UNW_ESUCCESS; +} + +/// +/// checks if a register is a floating-point register +/// +EXPORT int unw_is_fpreg(unw_cursor_t* cursor, unw_regnum_t regNum) +{ + DEBUG_PRINT_API("unw_is_fpreg(cursor=%p, regNum=%d)\n", cursor, regNum); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + return co->validFloatReg(regNum); +} + +/// +/// checks if current frame is signal trampoline +/// +EXPORT int unw_is_signal_frame(unw_cursor_t* cursor) +{ + DEBUG_PRINT_API("unw_is_signal_frame(cursor=%p)\n", cursor); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + return co->isSignalFrame(); +} + +/// +/// get name of function at cursor position in stack frame +/// +EXPORT int unw_get_proc_name(unw_cursor_t* cursor, char* buf, size_t bufLen, unw_word_t* offset) +{ + DEBUG_PRINT_API("unw_get_proc_name(cursor=%p, &buf=%p, bufLen=%ld)\n", cursor, buf, bufLen); + AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor; + if ( co->getFunctionName(buf, bufLen, offset) ) + return UNW_ESUCCESS; + else + return UNW_EUNSPEC; +} + +#pragma mark Remote API + +#if defined (SUPPORT_REMOTE_UNWINDING) +EXPORT int unw_init_remote(unw_cursor_t *cursor, unw_addr_space_t as, void *arg) +{ + DEBUG_PRINT_API("init_remote(c=%p, as=%p, arg=%p)\n", cursor, as, arg); + + // API docs at http://www.nongnu.org/libunwind/docs.html say we should + // handle a local address space but we're not doing the "remote" unwinding + // with local process accessors so punt on that. + + if(as->type != UNW_REMOTE) + { + ABORT("unw_init_remote was passed a non-remote address space"); + return UNW_EINVAL; + } + + unw_accessors_t* acc = unw_get_accessors(as); + if(!acc) { + ABORT("unw_get_accessors returned NULL"); + return UNW_EINVAL; + } + + unw_addr_space_remote* remote = (unw_addr_space_remote*)as; + + // use "placement new" to allocate UnwindCursor in the cursor buffer + // It isn't really necessary to use placement new in the remote API but we'll stay consistent + // with the rest of the code here. + switch ( remote->ras->getTargetArch() ) { + case UNW_TARGET_I386: + { + Registers_x86 *r = new Registers_x86; + OtherAddressSpace<Pointer32<LittleEndian> > *addrSpace = new OtherAddressSpace<Pointer32<LittleEndian> >(as, arg); + getRemoteContext (remote->ras, *r, arg); + unw_context_t *context = (unw_context_t*) r; + new ((void*)cursor) RemoteUnwindCursor<OtherAddressSpace<Pointer32<LittleEndian> >, Registers_x86>(*addrSpace, context, arg); + break; + } + break; + case UNW_TARGET_X86_64: + { + Registers_x86_64 *r = new Registers_x86_64; + OtherAddressSpace<Pointer64<LittleEndian> > *addrSpace = new OtherAddressSpace<Pointer64<LittleEndian> >(as, arg); + getRemoteContext (remote->ras, *r, arg); + unw_context_t *context = (unw_context_t*) r; + new ((void*)cursor) RemoteUnwindCursor<OtherAddressSpace<Pointer64<LittleEndian> >, Registers_x86_64>(*addrSpace, context, arg); + break; + } + + case UNW_TARGET_PPC: + ABORT("ppc not supported for remote unwinds"); + break; + + case UNW_TARGET_ARM: + ABORT("arm not supported for remote unwinds"); + break; + + default: + return UNW_EUNSPEC; + } + + AbstractRemoteUnwindCursor* co = (AbstractRemoteUnwindCursor*)cursor; + co->setRemoteContext(arg); + + return UNW_ESUCCESS; +} + +// The documentation disagrees about whether or not this returns a pointer. Now it does. +EXPORT unw_accessors_t* unw_get_accessors(unw_addr_space_t as) +{ + if(as->type != UNW_REMOTE) + { + ABORT("unw_get_accessors was passed a non-remote address space"); + return NULL; + } + unw_addr_space_remote* remote = (unw_addr_space_remote*)as; + + if(remote->type != UNW_REMOTE) + return NULL; + + return remote->ras->getAccessors(); +} + +EXPORT unw_addr_space_t unw_create_addr_space(unw_accessors_t *ap, unw_targettype_t targarch) +{ + unw_addr_space_remote* remote = (unw_addr_space_remote*)malloc(sizeof(unw_addr_space_remote)); + remote->type = UNW_REMOTE; + remote->ras = new RemoteProcInfo(ap, targarch); + return (unw_addr_space_t)remote; +} + +EXPORT void unw_flush_caches(unw_addr_space_t as) +{ + if(as->type != UNW_REMOTE) + { + ABORT("unw_flush_caches was passed a non-remote address space"); + return; + } + unw_addr_space_remote* remote = (unw_addr_space_remote*)as; + remote->ras->flushAllCaches(); + + return; +} + +EXPORT void unw_image_was_unloaded (unw_addr_space_t as, unw_word_t mh) +{ + if(as->type != UNW_REMOTE) + { + ABORT("unw_image_was_unloaded was passed a non-remote address space"); + return; + } + unw_addr_space_remote* remote = (unw_addr_space_remote*)as; + remote->ras->flushCacheByMachHeader(mh); + + return; +} + + +EXPORT int unw_set_caching_policy(unw_addr_space_t as, unw_caching_policy_t policy) +{ + if(as->type != UNW_REMOTE) + { + ABORT("unw_set_caching_policy was passed a non-remote address space"); + return UNW_EINVAL; + } + unw_addr_space_remote* remote = (unw_addr_space_remote*)as; + return remote->ras->setCachingPolicy(policy); +} + +EXPORT unw_addr_space_t unw_local_addr_space = (unw_addr_space_t)&sThisAddressSpace; + +/// +/// delete an address_space object +/// +EXPORT void unw_destroy_addr_space(unw_addr_space_t asp) +{ + if(asp->type != UNW_REMOTE) { + ABORT("unw_destroy_addr_space was passed a non-remote address space"); + return; + } + + unw_addr_space_remote* remote = (unw_addr_space_remote*)asp; + delete remote->ras; +} + +EXPORT void unw_set_logging_level(unw_addr_space_t as, FILE *f, unw_log_level_t level) +{ + if (as->type != UNW_REMOTE) { + ABORT("unw_set_logging_level was passed a non-remote address space"); + return; + } + + unw_addr_space_remote* remote = (unw_addr_space_remote*)as; + return remote->ras->setLoggingLevel(f, level); +} + + +EXPORT int unw_end_of_prologue_setup(unw_cursor_t* cursor, unw_word_t start, unw_word_t end, unw_word_t *endofprologue) +{ + AbstractRemoteUnwindCursor* co = (AbstractRemoteUnwindCursor*)cursor; + if (!co->remoteUnwindCursor()) + ABORT("unw_end_of_prologue_setup called with a non-remote unwind cursor."); + + return co->endOfPrologueInsns (start, end, endofprologue); +} + + +#endif // SUPPORT_REMOTE_UNWINDING + +#pragma mark Dynamic unwinding API + +#if !FOR_DYLD +/// +/// SPI: walks cached dwarf entries +/// +EXPORT void unw_iterate_dwarf_unwind_cache(void (*func)(unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) +{ + DEBUG_PRINT_API("unw_iterate_dwarf_unwind_cache(func=%p)\n", func); + DwarfFDECache<LocalAddressSpace>::iterateCacheEntries(func); +} +#endif // !FOR_DYLD + +#if !FOR_DYLD +// +// IPI: for __register_frame() +// +void _unw_add_dynamic_fde(unw_word_t fde) +{ + CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo; + CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo; + const char* message = CFI_Parser<LocalAddressSpace>::decodeFDE(sThisAddressSpace, (LocalAddressSpace::pint_t)fde, & fdeInfo, &cieInfo); + if ( message == NULL ) { + // dynamically registered FDEs don't have a mach_header group they are in. Use fde as mh_group + unw_word_t mh_group = fdeInfo.fdeStart; + DwarfFDECache<LocalAddressSpace>::add(mh_group, fdeInfo.pcStart, fdeInfo.pcEnd, fdeInfo.fdeStart); + } + else { + DEBUG_MESSAGE("_unw_add_dynamic_fde: bad fde: %s", message); + } +} + +// +// IPI: for __deregister_frame() +// +void _unw_remove_dynamic_fde(unw_word_t fde) +{ + // fde is own mh_group + DwarfFDECache<LocalAddressSpace>::removeAllIn(fde); +} +#endif + +}; // namespace lldb_private + +#endif // __ppc__ || __i386__ || __x86_64__ diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/unw_getcontext.s b/lldb/source/Plugins/Process/Utility/libunwind/src/unw_getcontext.s new file mode 100644 index 00000000000..8d3a451fd92 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/libunwind/src/unw_getcontext.s @@ -0,0 +1,229 @@ + +#if __i386__ || __x86_64__ || __ppc__ + + .text + .globl _unw_getcontext +_unw_getcontext: + +#endif // __i386__ || __x86_64__ || __ppc__ + + +#if __i386__ + +# +# extern int unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# + + +# +-----------------------+ +# + thread_state pointer + +# +-----------------------+ +# + return address + +# +-----------------------+ <-- SP +# + + +# + push %eax + movl 8(%esp), %eax + movl %ebx, 4(%eax) + movl %ecx, 8(%eax) + movl %edx, 12(%eax) + movl %edi, 16(%eax) + movl %esi, 20(%eax) + movl %ebp, 24(%eax) + movl %esp, %edx + addl $8, %edx + movl %edx, 28(%eax) # store what sp was at call site as esp + # skip ss + # skip eflags + movl 4(%esp), %edx + movl %edx, 40(%eax) # store return address as eip + # skip cs + # skip ds + # skip es + # skip fs + # skip gs + movl (%esp), %edx + movl %edx, (%eax) # store original eax + popl %eax + xorl %eax, %eax # return UNW_ESUCCESS + ret + +#elif __x86_64__ + +# +# extern int unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# thread_state pointer is in rdi +# + movq %rax, (%rdi) + movq %rbx, 8(%rdi) + movq %rcx, 16(%rdi) + movq %rdx, 24(%rdi) + movq %rdi, 32(%rdi) + movq %rsi, 40(%rdi) + movq %rbp, 48(%rdi) + movq %rsp, 56(%rdi) + addq $8, 56(%rdi) + movq %r8, 64(%rdi) + movq %r9, 72(%rdi) + movq %r10, 80(%rdi) + movq %r11, 88(%rdi) + movq %r12, 96(%rdi) + movq %r13,104(%rdi) + movq %r14,112(%rdi) + movq %r15,120(%rdi) + movq (%rsp),%rsi + movq %rsi,128(%rdi) # store return address as rip + # skip rflags + # skip cs + # skip fs + # skip gs + xorl %eax, %eax # return UNW_ESUCCESS + ret + +#elif __ppc__ + +; +; extern int unw_getcontext(unw_context_t* thread_state) +; +; On entry: +; thread_state pointer is in r3 +; + stw r0, 8(r3) + mflr r0 + stw r0, 0(r3) ; store lr as ssr0 + stw r1, 12(r3) + stw r2, 16(r3) + stw r3, 20(r3) + stw r4, 24(r3) + stw r5, 28(r3) + stw r6, 32(r3) + stw r7, 36(r3) + stw r8, 40(r3) + stw r9, 44(r3) + stw r10, 48(r3) + stw r11, 52(r3) + stw r12, 56(r3) + stw r13, 60(r3) + stw r14, 64(r3) + stw r15, 68(r3) + stw r16, 72(r3) + stw r17, 76(r3) + stw r18, 80(r3) + stw r19, 84(r3) + stw r20, 88(r3) + stw r21, 92(r3) + stw r22, 96(r3) + stw r23,100(r3) + stw r24,104(r3) + stw r25,108(r3) + stw r26,112(r3) + stw r27,116(r3) + stw r28,120(r3) + stw r29,124(r3) + stw r30,128(r3) + stw r31,132(r3) + + ; save VRSave register + mfspr r0,256 + stw r0,156(r3) + ; save CR registers + mfcr r0 + stw r0,136(r3) + ; save CTR register + mfctr r0 + stw r0,148(r3) + + ; save float registers + stfd f0, 160(r3) + stfd f1, 168(r3) + stfd f2, 176(r3) + stfd f3, 184(r3) + stfd f4, 192(r3) + stfd f5, 200(r3) + stfd f6, 208(r3) + stfd f7, 216(r3) + stfd f8, 224(r3) + stfd f9, 232(r3) + stfd f10,240(r3) + stfd f11,248(r3) + stfd f12,256(r3) + stfd f13,264(r3) + stfd f14,272(r3) + stfd f15,280(r3) + stfd f16,288(r3) + stfd f17,296(r3) + stfd f18,304(r3) + stfd f19,312(r3) + stfd f20,320(r3) + stfd f21,328(r3) + stfd f22,336(r3) + stfd f23,344(r3) + stfd f24,352(r3) + stfd f25,360(r3) + stfd f26,368(r3) + stfd f27,376(r3) + stfd f28,384(r3) + stfd f29,392(r3) + stfd f30,400(r3) + stfd f31,408(r3) + + + ; save vector registers + + subi r4,r1,16 + rlwinm r4,r4,0,0,27 ; mask low 4-bits + ; r4 is now a 16-byte aligned pointer into the red zone + +#define SAVE_VECTOR_UNALIGNED(_vec, _offset) \ + stvx _vec,0,r4 @\ + lwz r5, 0(r4) @\ + stw r5, _offset(r3) @\ + lwz r5, 4(r4) @\ + stw r5, _offset+4(r3) @\ + lwz r5, 8(r4) @\ + stw r5, _offset+8(r3) @\ + lwz r5, 12(r4) @\ + stw r5, _offset+12(r3) + + SAVE_VECTOR_UNALIGNED( v0, 424+0x000) + SAVE_VECTOR_UNALIGNED( v1, 424+0x010) + SAVE_VECTOR_UNALIGNED( v2, 424+0x020) + SAVE_VECTOR_UNALIGNED( v3, 424+0x030) + SAVE_VECTOR_UNALIGNED( v4, 424+0x040) + SAVE_VECTOR_UNALIGNED( v5, 424+0x050) + SAVE_VECTOR_UNALIGNED( v6, 424+0x060) + SAVE_VECTOR_UNALIGNED( v7, 424+0x070) + SAVE_VECTOR_UNALIGNED( v8, 424+0x080) + SAVE_VECTOR_UNALIGNED( v9, 424+0x090) + SAVE_VECTOR_UNALIGNED(v10, 424+0x0A0) + SAVE_VECTOR_UNALIGNED(v11, 424+0x0B0) + SAVE_VECTOR_UNALIGNED(v12, 424+0x0C0) + SAVE_VECTOR_UNALIGNED(v13, 424+0x0D0) + SAVE_VECTOR_UNALIGNED(v14, 424+0x0E0) + SAVE_VECTOR_UNALIGNED(v15, 424+0x0F0) + SAVE_VECTOR_UNALIGNED(v16, 424+0x100) + SAVE_VECTOR_UNALIGNED(v17, 424+0x110) + SAVE_VECTOR_UNALIGNED(v18, 424+0x120) + SAVE_VECTOR_UNALIGNED(v19, 424+0x130) + SAVE_VECTOR_UNALIGNED(v20, 424+0x140) + SAVE_VECTOR_UNALIGNED(v21, 424+0x150) + SAVE_VECTOR_UNALIGNED(v22, 424+0x160) + SAVE_VECTOR_UNALIGNED(v23, 424+0x170) + SAVE_VECTOR_UNALIGNED(v24, 424+0x180) + SAVE_VECTOR_UNALIGNED(v25, 424+0x190) + SAVE_VECTOR_UNALIGNED(v26, 424+0x1A0) + SAVE_VECTOR_UNALIGNED(v27, 424+0x1B0) + SAVE_VECTOR_UNALIGNED(v28, 424+0x1C0) + SAVE_VECTOR_UNALIGNED(v29, 424+0x1D0) + SAVE_VECTOR_UNALIGNED(v30, 424+0x1E0) + SAVE_VECTOR_UNALIGNED(v31, 424+0x1F0) + + li r3, 0 ; return UNW_ESUCCESS + blr + + + +#endif + |