diff options
Diffstat (limited to 'lldb/source/Plugins')
152 files changed, 62249 insertions, 0 deletions
diff --git a/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp b/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp new file mode 100644 index 00000000000..09cc09d024c --- /dev/null +++ b/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp @@ -0,0 +1,578 @@ +//===-- ABIMacOSX_i386.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ABIMacOSX_i386.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +#include "llvm/ADT/Triple.h" + +#include <vector> + +using namespace lldb; +using namespace lldb_private; + +static const char *pluginName = "ABIMacOSX_i386"; +static const char *pluginDesc = "Mac OS X ABI for i386 targets"; +static const char *pluginShort = "abi.macosx-i386"; + +size_t +ABIMacOSX_i386::GetRedZoneSize () const +{ + return 0; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +lldb_private::ABI * +ABIMacOSX_i386::CreateInstance (const ConstString &triple) +{ + llvm::StringRef tripleStr(triple.GetCString()); + llvm::Triple llvmTriple(tripleStr); + + if (llvmTriple.getArch() != llvm::Triple::x86) + return NULL; + + return new ABIMacOSX_i386; +} + +bool +ABIMacOSX_i386::PrepareTrivialCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + lldb::addr_t arg) const +{ + RegisterContext *reg_ctx = thread.GetRegisterContext(); + if (!reg_ctx) + return false; + + uint32_t ebpID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + uint32_t eipID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + uint32_t espID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + + // Make room for the argument on the stack + + sp -= 4; + + // Align the SP + + sp &= ~(0xfull); // 16-byte alignment + + // Write the argument on the stack + + uint32_t argU32 = arg & 0xffffffffull; + Error error; + if (thread.GetProcess().WriteMemory (sp, &argU32, sizeof(argU32), error) != sizeof(argU32)) + return false; + + // The return address is pushed onto the stack. + + sp -= 4; + uint32_t returnAddressU32 = returnAddress; + if (thread.GetProcess().WriteMemory (sp, &returnAddressU32, sizeof(returnAddressU32), error) != sizeof(returnAddressU32)) + return false; + + // %esp is set to the actual stack value. + + if (!reg_ctx->WriteRegisterFromUnsigned(espID, sp)) + return false; + + // %ebp is set to a fake value, in our case 0x0x00000000 + + if (!reg_ctx->WriteRegisterFromUnsigned(ebpID, 0x00000000)) + return false; + + // %eip is set to the address of the called function. + + if (!reg_ctx->WriteRegisterFromUnsigned(eipID, functionAddress)) + return false; + + return true; +} + +bool +ABIMacOSX_i386::PrepareNormalCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + ValueList &args) const +{ + RegisterContext *reg_ctx = thread.GetRegisterContext(); + if (!reg_ctx) + return false; + Error error; + uint32_t ebpID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + uint32_t eipID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + uint32_t espID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + + // Do the argument layout + + std::vector <uint32_t> argLayout; // 4-byte chunks, as discussed in the ABI Function Call Guide + + size_t numArgs = args.GetSize(); + size_t index; + + for (index = 0; index < numArgs; ++index) + { + Value *val = args.GetValueAtIndex(index); + + if (!val) + return false; + + switch (val->GetValueType()) + { + case Value::eValueTypeScalar: + { + Scalar &scalar = val->GetScalar(); + switch (scalar.GetType()) + { + case Scalar::e_void: + default: + return false; + case Scalar::e_sint: + case Scalar::e_uint: + case Scalar::e_slong: + case Scalar::e_ulong: + case Scalar::e_slonglong: + case Scalar::e_ulonglong: + { + uint64_t data = scalar.ULongLong(); + + switch (scalar.GetByteSize()) + { + default: + return false; + case 1: + argLayout.push_back((uint32_t)(data & 0xffull)); + break; + case 2: + argLayout.push_back((uint32_t)(data & 0xffffull)); + break; + case 4: + argLayout.push_back((uint32_t)(data & 0xffffffffull)); + break; + case 8: + argLayout.push_back((uint32_t)(data & 0xffffffffull)); + argLayout.push_back((uint32_t)(data >> 32)); + break; + } + } + break; + case Scalar::e_float: + { + float data = scalar.Float(); + uint32_t dataRaw = *((uint32_t*)(&data)); + argLayout.push_back(dataRaw); + } + break; + case Scalar::e_double: + { + double data = scalar.Double(); + uint32_t *dataRaw = ((uint32_t*)(&data)); + argLayout.push_back(dataRaw[0]); + argLayout.push_back(dataRaw[1]); + } + break; + case Scalar::e_long_double: + { + long double data = scalar.Double(); + uint32_t *dataRaw = ((uint32_t*)(&data)); + while ((argLayout.size() * 4) & 0xf) + argLayout.push_back(0); + argLayout.push_back(dataRaw[0]); + argLayout.push_back(dataRaw[1]); + argLayout.push_back(dataRaw[2]); + argLayout.push_back(dataRaw[3]); + } + break; + } + } + break; + case Value::eValueTypeHostAddress: + switch (val->GetContextType()) + { + default: + return false; + case Value::eContextTypeOpaqueClangQualType: + { + void *val_type = val->GetOpaqueClangQualType(); + uint32_t cstr_length; + + if (ClangASTContext::IsCStringType (val_type, cstr_length)) + { + const char *cstr = (const char*)val->GetScalar().ULongLong(); + cstr_length = strlen(cstr); + + // Push the string onto the stack immediately. + + sp -= (cstr_length + 1); + + if (thread.GetProcess().WriteMemory(sp, cstr, cstr_length + 1, error) != (cstr_length + 1)) + return false; + + // Put the address of the string into the argument array. + + argLayout.push_back((uint32_t)(sp & 0xffffffff)); + } + else + { + return false; + } + } + break; + } + break; + case Value::eValueTypeFileAddress: + case Value::eValueTypeLoadAddress: + default: + return false; + } + } + + // Make room for the arguments on the stack + + sp -= 4 * argLayout.size(); + + // Align the SP + + sp &= ~(0xfull); // 16-byte alignment + + // Write the arguments on the stack + + size_t numChunks = argLayout.size(); + + for (index = 0; index < numChunks; ++index) + if (thread.GetProcess().WriteMemory(sp + (index * 4), &argLayout[index], sizeof(uint32_t), error) != sizeof(uint32_t)) + return false; + + // The return address is pushed onto the stack. + + sp -= 4; + uint32_t returnAddressU32 = returnAddress; + if (thread.GetProcess().WriteMemory (sp, &returnAddressU32, sizeof(returnAddressU32), error) != sizeof(returnAddressU32)) + return false; + + // %esp is set to the actual stack value. + + if (!reg_ctx->WriteRegisterFromUnsigned(espID, sp)) + return false; + + // %ebp is set to a fake value, in our case 0x0x00000000 + + if (!reg_ctx->WriteRegisterFromUnsigned(ebpID, 0x00000000)) + return false; + + // %eip is set to the address of the called function. + + if (!reg_ctx->WriteRegisterFromUnsigned(eipID, functionAddress)) + return false; + + return true; +} + +static bool ReadIntegerArgument(Scalar &scalar, + unsigned int bit_width, + bool is_signed, + Process &process, + addr_t ¤t_stack_argument) +{ + if (bit_width > 64) + return false; // Scalar can't hold large integer arguments + + uint64_t arg_contents; + uint32_t read_data; + Error error; + + if (bit_width > 32) + { + if (process.ReadMemory(current_stack_argument, &read_data, sizeof(read_data), error) != sizeof(read_data)) + return false; + + arg_contents = read_data; + + if (process.ReadMemory(current_stack_argument + 4, &read_data, sizeof(read_data), error) != sizeof(read_data)) + return false; + + arg_contents |= ((uint64_t)read_data) << 32; + + current_stack_argument += 8; + } + else { + if (process.ReadMemory(current_stack_argument, &read_data, sizeof(read_data), error) != sizeof(read_data)) + return false; + + arg_contents = read_data; + + current_stack_argument += 4; + } + + if (is_signed) + { + switch (bit_width) + { + default: + return false; + case 8: + scalar = (int8_t)(arg_contents & 0xff); + break; + case 16: + scalar = (int16_t)(arg_contents & 0xffff); + break; + case 32: + scalar = (int32_t)(arg_contents & 0xffffffff); + break; + case 64: + scalar = (int64_t)arg_contents; + break; + } + } + else + { + switch (bit_width) + { + default: + return false; + case 8: + scalar = (uint8_t)(arg_contents & 0xff); + break; + case 16: + scalar = (uint16_t)(arg_contents & 0xffff); + break; + case 32: + scalar = (uint32_t)(arg_contents & 0xffffffff); + break; + case 64: + scalar = (uint64_t)arg_contents; + break; + } + } + + return true; +} + +bool +ABIMacOSX_i386::GetArgumentValues (Thread &thread, + ValueList &values) const +{ + unsigned int num_values = values.GetSize(); + unsigned int value_index; + + // Extract the Clang AST context from the PC so that we can figure out type + // sizes + + clang::ASTContext *ast_context = thread.CalculateTarget()->GetScratchClangASTContext()->getASTContext(); + + // Get the pointer to the first stack argument so we have a place to start + // when reading data + + RegisterContext *reg_ctx = thread.GetRegisterContext(); + + if (!reg_ctx) + return false; + + addr_t sp = reg_ctx->GetSP(0); + + if (!sp) + return false; + + addr_t current_stack_argument = sp + 4; // jump over return address + + for (value_index = 0; + value_index < num_values; + ++value_index) + { + Value *value = values.GetValueAtIndex(value_index); + + if (!value) + return false; + + // We currently only support extracting values with Clang QualTypes. + // Do we care about others? + switch (value->GetContextType()) + { + default: + return false; + case Value::eContextTypeOpaqueClangQualType: + { + void *value_type = value->GetOpaqueClangQualType(); + bool is_signed; + + if (ClangASTContext::IsIntegerType (value_type, is_signed)) + { + size_t bit_width = ClangASTContext::GetTypeBitSize(ast_context, value_type); + + ReadIntegerArgument(value->GetScalar(), + bit_width, + is_signed, + thread.GetProcess(), + current_stack_argument); + } + else if (ClangASTContext::IsPointerType (value_type)) + { + ReadIntegerArgument(value->GetScalar(), + 32, + false, + thread.GetProcess(), + current_stack_argument); + } + } + break; + } + } + + return true; +} + +bool +ABIMacOSX_i386::GetReturnValue (Thread &thread, + Value &value) const +{ + switch (value.GetContextType()) + { + default: + return false; + case Value::eContextTypeOpaqueClangQualType: + { + // Extract the Clang AST context from the PC so that we can figure out type + // sizes + + clang::ASTContext *ast_context = thread.CalculateTarget()->GetScratchClangASTContext()->getASTContext(); + + // Get the pointer to the first stack argument so we have a place to start + // when reading data + + RegisterContext *reg_ctx = thread.GetRegisterContext(); + + void *value_type = value.GetOpaqueClangQualType(); + bool is_signed; + + if (ClangASTContext::IsIntegerType (value_type, is_signed)) + { + size_t bit_width = ClangASTContext::GetTypeBitSize(ast_context, value_type); + + unsigned eax_id = reg_ctx->GetRegisterInfoByName("eax", 0)->reg; + unsigned edx_id = reg_ctx->GetRegisterInfoByName("edx", 0)->reg; + + switch (bit_width) + { + default: + case 128: + // Scalar can't hold 128-bit literals, so we don't handle this + return false; + case 64: + uint64_t raw_value; + raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff; + raw_value |= (thread.GetRegisterContext()->ReadRegisterAsUnsigned(edx_id, 0) & 0xffffffff) << 32; + if (is_signed) + value.GetScalar() = (int64_t)raw_value; + else + value.GetScalar() = (uint64_t)raw_value; + break; + case 32: + if (is_signed) + value.GetScalar() = (int32_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff); + else + value.GetScalar() = (uint32_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff); + break; + case 16: + if (is_signed) + value.GetScalar() = (int16_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffff); + else + value.GetScalar() = (uint16_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffff); + break; + case 8: + if (is_signed) + value.GetScalar() = (int8_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xff); + else + value.GetScalar() = (uint8_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xff); + break; + } + } + else if (ClangASTContext::IsPointerType (value_type)) + { + unsigned eax_id = reg_ctx->GetRegisterInfoByName("eax", 0)->reg; + uint32_t ptr = thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff; + value.GetScalar() = ptr; + } + else + { + // not handled yet + return false; + } + } + break; + } + + return true; +} + +void +ABIMacOSX_i386::Initialize() +{ + PluginManager::RegisterPlugin (pluginName, + pluginDesc, + CreateInstance); +} + +void +ABIMacOSX_i386::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +ABIMacOSX_i386::GetPluginName() +{ + return pluginName; +} + +const char * +ABIMacOSX_i386::GetShortPluginName() +{ + return pluginShort; +} + +uint32_t +ABIMacOSX_i386::GetPluginVersion() +{ + return 1; +} + +void +ABIMacOSX_i386::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +ABIMacOSX_i386::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +ABIMacOSX_i386::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} diff --git a/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h b/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h new file mode 100644 index 00000000000..c27569b5a14 --- /dev/null +++ b/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h @@ -0,0 +1,93 @@ +//===-- ABIMacOSX_i386.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_ABIMacOSX_i386_h_ +#define liblldb_ABIMacOSX_i386_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" +#include "lldb/Core/Value.h" + +namespace lldb_private { + + class ABIMacOSX_i386 : + public lldb_private::ABI + { + public: + ~ABIMacOSX_i386() { } + + virtual size_t + GetRedZoneSize () const; + + virtual bool + PrepareTrivialCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + lldb::addr_t arg) const; + + virtual bool + PrepareNormalCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + ValueList &args) const; + + virtual bool + GetArgumentValues (Thread &thread, + ValueList &values) const; + + virtual bool + GetReturnValue (Thread &thread, + Value &value) const; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ABI * + CreateInstance (const ConstString &triple); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + protected: + private: + ABIMacOSX_i386() : lldb_private::ABI() { } // Call CreateInstance instead. + }; + +} // namespace lldb_private + +#endif // liblldb_ABI_h_ diff --git a/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp b/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp new file mode 100644 index 00000000000..004be60c377 --- /dev/null +++ b/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp @@ -0,0 +1,412 @@ +//===-- ABISysV_x86_64.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ABISysV_x86_64.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Value.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" + +#include "llvm/ADT/Triple.h" + +using namespace lldb; +using namespace lldb_private; + +static const char *pluginName = "ABISysV_x86_64"; +static const char *pluginDesc = "System V ABI for x86_64 targets"; +static const char *pluginShort = "abi.sysv-x86_64"; + +size_t +ABISysV_x86_64::GetRedZoneSize () const +{ + return 128; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +lldb_private::ABI * +ABISysV_x86_64::CreateInstance (const ConstString &triple) +{ + llvm::StringRef tripleStr(triple.GetCString()); + llvm::Triple llvmTriple(tripleStr); + + if (llvmTriple.getArch() != llvm::Triple::x86_64) + return NULL; + + return new ABISysV_x86_64; +} + +bool +ABISysV_x86_64::PrepareTrivialCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + lldb::addr_t arg) const +{ + RegisterContext *reg_ctx = thread.GetRegisterContext(); + if (!reg_ctx) + return false; + + uint32_t rdiID = reg_ctx->GetRegisterInfoByName("rdi", 0)->reg; + uint32_t rbpID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + uint32_t ripID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + uint32_t rspID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + + // The argument is in %rdi, and not on the stack. + + if (!reg_ctx->WriteRegisterFromUnsigned(rdiID, arg)) + return false; + + // First, align the SP + + sp &= ~(0xfull); // 16-byte alignment + + // The return address is pushed onto the stack. + + sp -= 8; + uint64_t returnAddressU64 = returnAddress; + Error error; + if (thread.GetProcess().WriteMemory (sp, &returnAddressU64, sizeof(returnAddressU64), error) != sizeof(returnAddressU64)) + return false; + + // %rsp is set to the actual stack value. + + if (!reg_ctx->WriteRegisterFromUnsigned(rspID, sp)) + return false; + + // %rbp is set to a fake value, in our case 0x0000000000000000. + + if (!reg_ctx->WriteRegisterFromUnsigned(rbpID, 0x000000000000000)) + return false; + + // %rip is set to the address of the called function. + + if (!reg_ctx->WriteRegisterFromUnsigned(ripID, functionAddress)) + return false; + + return true; +} + +bool +ABISysV_x86_64::PrepareNormalCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + ValueList &args) const +{ + return false; +} + +static bool ReadIntegerArgument(Scalar &scalar, + unsigned int bit_width, + bool is_signed, + Thread &thread, + uint32_t *argument_register_ids, + unsigned int ¤t_argument_register, + addr_t ¤t_stack_argument) +{ + if (bit_width > 64) + return false; // Scalar can't hold large integer arguments + + uint64_t arg_contents; + + if (current_argument_register < 6) + { + arg_contents = thread.GetRegisterContext()->ReadRegisterAsUnsigned(argument_register_ids[current_argument_register], 0); + current_argument_register++; + } + else + { + uint8_t arg_data[sizeof(arg_contents)]; + Error error; + thread.GetProcess().ReadMemory(current_stack_argument, arg_data, sizeof(arg_contents), error); + DataExtractor arg_data_extractor(arg_data, sizeof(arg_contents), thread.GetProcess().GetByteOrder(), thread.GetProcess().GetAddressByteSize()); + uint32_t offset = 0; + arg_contents = arg_data_extractor.GetMaxU64(&offset, bit_width / 8); + if (!offset) + return false; + current_stack_argument += (bit_width / 8); + } + + if (is_signed) + { + switch (bit_width) + { + default: + return false; + case 8: + scalar = (int8_t)(arg_contents & 0xff); + break; + case 16: + scalar = (int16_t)(arg_contents & 0xffff); + break; + case 32: + scalar = (int32_t)(arg_contents & 0xffffffff); + break; + case 64: + scalar = (int64_t)arg_contents; + break; + } + } + else + { + switch (bit_width) + { + default: + return false; + case 8: + scalar = (uint8_t)(arg_contents & 0xff); + break; + case 16: + scalar = (uint16_t)(arg_contents & 0xffff); + break; + case 32: + scalar = (uint32_t)(arg_contents & 0xffffffff); + break; + case 64: + scalar = (uint64_t)arg_contents; + break; + } + } + + return true; +} + +bool +ABISysV_x86_64::GetArgumentValues (Thread &thread, + ValueList &values) const +{ + unsigned int num_values = values.GetSize(); + unsigned int value_index; + + // For now, assume that the types in the AST values come from the Target's + // scratch AST. + + clang::ASTContext *ast_context = thread.CalculateTarget()->GetScratchClangASTContext()->getASTContext(); + + // Extract the register context so we can read arguments from registers + + RegisterContext *reg_ctx = thread.GetRegisterContext(); + + if (!reg_ctx) + return false; + + // Get the pointer to the first stack argument so we have a place to start + // when reading data + + addr_t sp = reg_ctx->GetSP(0); + + if (!sp) + return false; + + addr_t current_stack_argument = sp + 8; // jump over return address + + uint32_t argument_register_ids[6]; + + argument_register_ids[0] = reg_ctx->GetRegisterInfoByName("rdi", 0)->reg; + argument_register_ids[1] = reg_ctx->GetRegisterInfoByName("rsi", 0)->reg; + argument_register_ids[2] = reg_ctx->GetRegisterInfoByName("rdx", 0)->reg; + argument_register_ids[3] = reg_ctx->GetRegisterInfoByName("rcx", 0)->reg; + argument_register_ids[4] = reg_ctx->GetRegisterInfoByName("r8", 0)->reg; + argument_register_ids[5] = reg_ctx->GetRegisterInfoByName("r9", 0)->reg; + + unsigned int current_argument_register = 0; + + for (value_index = 0; + value_index < num_values; + ++value_index) + { + Value *value = values.GetValueAtIndex(value_index); + + if (!value) + return false; + + // We currently only support extracting values with Clang QualTypes. + // Do we care about others? + switch (value->GetContextType()) + { + default: + return false; + case Value::eContextTypeOpaqueClangQualType: + { + void *value_type = value->GetOpaqueClangQualType(); + bool is_signed; + + if (ClangASTContext::IsIntegerType (value_type, is_signed)) + { + size_t bit_width = ClangASTContext::GetTypeBitSize(ast_context, value_type); + + ReadIntegerArgument(value->GetScalar(), + bit_width, + is_signed, + thread, + argument_register_ids, + current_argument_register, + current_stack_argument); + } + else if (ClangASTContext::IsPointerType (value_type)) + { + ReadIntegerArgument(value->GetScalar(), + 64, + false, + thread, + argument_register_ids, + current_argument_register, + current_stack_argument); + } + } + break; + } + } + + return true; +} + +bool +ABISysV_x86_64::GetReturnValue (Thread &thread, + Value &value) const +{ + switch (value.GetContextType()) + { + default: + return false; + case Value::eContextTypeOpaqueClangQualType: + { + void *value_type = value.GetOpaqueClangQualType(); + bool is_signed; + + RegisterContext *reg_ctx = thread.GetRegisterContext(); + + if (!reg_ctx) + return false; + + if (ClangASTContext::IsIntegerType (value_type, is_signed)) + { + // For now, assume that the types in the AST values come from the Target's + // scratch AST. + + clang::ASTContext *ast_context = thread.CalculateTarget()->GetScratchClangASTContext()->getASTContext(); + + // Extract the register context so we can read arguments from registers + + size_t bit_width = ClangASTContext::GetTypeBitSize(ast_context, value_type); + unsigned rax_id = reg_ctx->GetRegisterInfoByName("rax", 0)->reg; + + switch (bit_width) + { + default: + case 128: + // Scalar can't hold 128-bit literals, so we don't handle this + return false; + case 64: + if (is_signed) + value.GetScalar() = (int64_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0)); + else + value.GetScalar() = (uint64_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0)); + break; + case 32: + if (is_signed) + value.GetScalar() = (int32_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0) & 0xffffffff); + else + value.GetScalar() = (uint32_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0) & 0xffffffff); + break; + case 16: + if (is_signed) + value.GetScalar() = (int16_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0) & 0xffff); + else + value.GetScalar() = (uint16_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0) & 0xffff); + break; + case 8: + if (is_signed) + value.GetScalar() = (int8_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0) & 0xff); + else + value.GetScalar() = (uint8_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0) & 0xff); + break; + } + } + else if (ClangASTContext::IsPointerType (value_type)) + { + unsigned rax_id = reg_ctx->GetRegisterInfoByName("rax", 0)->reg; + value.GetScalar() = (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0); + } + else + { + // not handled yet + return false; + } + } + break; + } + + return true; +} + +void +ABISysV_x86_64::Initialize() +{ + PluginManager::RegisterPlugin (pluginName, + pluginDesc, + CreateInstance); +} + +void +ABISysV_x86_64::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +ABISysV_x86_64::GetPluginName() +{ + return pluginName; +} + +const char * +ABISysV_x86_64::GetShortPluginName() +{ + return pluginShort; +} + +uint32_t +ABISysV_x86_64::GetPluginVersion() +{ + return 1; +} + +void +ABISysV_x86_64::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +ABISysV_x86_64::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +ABISysV_x86_64::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} diff --git a/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h b/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h new file mode 100644 index 00000000000..b89d7c6581c --- /dev/null +++ b/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h @@ -0,0 +1,92 @@ +//===-- ABISysV_x86_64.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ABISysV_x86_64_h_ +#define liblldb_ABISysV_x86_64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" + +namespace lldb_private { + +class ABISysV_x86_64 : + public lldb_private::ABI +{ +public: + ~ABISysV_x86_64() { } + + virtual size_t + GetRedZoneSize () const; + + virtual bool + PrepareTrivialCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + lldb::addr_t arg) const; + + virtual bool + PrepareNormalCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + ValueList &args) const; + + virtual bool + GetArgumentValues (Thread &thread, + ValueList &values) const; + + virtual bool + GetReturnValue (Thread &thread, + Value &value) const; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ABI * + CreateInstance (const ConstString &triple); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); +protected: +private: + ABISysV_x86_64() : lldb_private::ABI() { } // Call CreateInstance instead. +}; + +} // namespace lldb_private + +#endif // liblldb_ABI_h_ diff --git a/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVM.cpp b/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVM.cpp new file mode 100644 index 00000000000..db2d561b24a --- /dev/null +++ b/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVM.cpp @@ -0,0 +1,468 @@ +//===-- DisassemblerLLVM.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DisassemblerLLVM.h" + +#include "llvm-c/EnhancedDisassembly.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/SymbolContext.h" + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" + +#include <memory> +#include <string> + +using namespace lldb; +using namespace lldb_private; + + +static +int DataExtractorByteReader(uint8_t *byte, uint64_t address, void *arg) +{ + DataExtractor &extractor = *((DataExtractor *)arg); + + if (extractor.ValidOffset(address)) + { + *byte = *(extractor.GetDataStart() + address); + return 0; + } + else + { + return -1; + } +} + +namespace { + struct RegisterReaderArg { + const lldb::addr_t instructionPointer; + const EDDisassemblerRef disassembler; + + RegisterReaderArg(lldb::addr_t ip, + EDDisassemblerRef dis) : + instructionPointer(ip), + disassembler(dis) + { + } + }; +} + +static int IPRegisterReader(uint64_t *value, unsigned regID, void* arg) +{ + uint64_t instructionPointer = ((RegisterReaderArg*)arg)->instructionPointer; + EDDisassemblerRef disassembler = ((RegisterReaderArg*)arg)->disassembler; + + if(EDRegisterIsProgramCounter(disassembler, regID)) { + *value = instructionPointer; + return 0; + } + + return -1; +} + +DisassemblerLLVM::Instruction::Instruction(EDDisassemblerRef disassembler) : + Disassembler::Instruction (), + m_disassembler (disassembler) +{ +} + +DisassemblerLLVM::Instruction::~Instruction() +{ +} + +static void +PadString(Stream *s, const std::string &str, size_t width) +{ + int diff = width - str.length(); + + if (diff > 0) + s->Printf("%s%*.*s", str.c_str(), diff, diff, ""); + else + s->Printf("%s ", str.c_str()); +} + +void +DisassemblerLLVM::Instruction::Dump +( + Stream *s, + lldb::addr_t base_address, + DataExtractor *bytes, + uint32_t bytes_offset, + const lldb_private::ExecutionContext exe_ctx, + bool raw +) +{ + const size_t opcodeColumnWidth = 7; + const size_t operandColumnWidth = 25; + + // If we have an address, print it out + if (base_address != LLDB_INVALID_ADDRESS) + s->Printf("0x%llx: ", base_address); + + // If we are supposed to show bytes, "bytes" will be non-NULL. + if (bytes) + { + uint32_t bytes_dumped = bytes->Dump(s, bytes_offset, eFormatBytes, 1, EDInstByteSize(m_inst), UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0) - bytes_offset; + // Allow for 8 bytes of opcodes normally + const uint32_t default_num_opcode_bytes = 9; + if (bytes_dumped * 3 < (default_num_opcode_bytes*3)) + { + uint32_t indent_level = (default_num_opcode_bytes*3) - (bytes_dumped * 3); + s->Printf("%*.*s", indent_level, indent_level, ""); + } + } + + int numTokens = EDNumTokens(m_inst); + + int currentOpIndex = -1; + + RegisterReaderArg rra(base_address + EDInstByteSize(m_inst), m_disassembler); + + lldb_private::Process *process = exe_ctx.process; + + bool printTokenized = false; + + if (numTokens != -1) + { + printTokenized = true; + + // Handle the opcode column. + + StreamString opcode; + + int tokenIndex = 0; + + EDTokenRef token; + const char *tokenStr; + + if (EDGetToken(&token, m_inst, tokenIndex)) + printTokenized = false; + + if (!printTokenized || !EDTokenIsOpcode(token)) + printTokenized = false; + + if (!printTokenized || EDGetTokenString(&tokenStr, token)) + printTokenized = false; + + // Put the token string into our opcode string + opcode.PutCString(tokenStr); + + // If anything follows, it probably starts with some whitespace. Skip it. + + tokenIndex++; + + if (printTokenized && tokenIndex < numTokens) + { + if(!printTokenized || EDGetToken(&token, m_inst, tokenIndex)) + printTokenized = false; + + if(!printTokenized || !EDTokenIsWhitespace(token)) + printTokenized = false; + } + + tokenIndex++; + + // Handle the operands and the comment. + + StreamString operands; + StreamString comment; + + if (printTokenized) + { + bool show_token; + + for (; tokenIndex < numTokens; ++tokenIndex) + { + if (EDGetToken(&token, m_inst, tokenIndex)) + return; + + if (raw) + { + show_token = true; + } + else + { + int operandIndex = EDOperandIndexForToken(token); + + if (operandIndex >= 0) + { + if (operandIndex != currentOpIndex) + { + show_token = true; + + currentOpIndex = operandIndex; + EDOperandRef operand; + + if (!EDGetOperand(&operand, m_inst, currentOpIndex)) + { + if (EDOperandIsMemory(operand)) + { + uint64_t operand_value; + + if (!EDEvaluateOperand(&operand_value, operand, IPRegisterReader, &rra)) + { + if (EDInstIsBranch(m_inst)) + { + operands.Printf("0x%llx ", operand_value); + show_token = false; + } + else + { + // Put the address value into the comment + comment.Printf("0x%llx ", operand_value); + } + + lldb_private::Address so_addr; + if (process) + { + if (process->ResolveLoadAddress(operand_value, so_addr)) + { + so_addr.Dump(&comment, process, Address::DumpStyleResolvedDescription, Address::DumpStyleSectionNameOffset); + } + } + } // EDEvaluateOperand + } // EDOperandIsMemory + } // EDGetOperand + } // operandIndex != currentOpIndex + } // operandIndex >= 0 + } // else(raw) + + if (show_token) + { + if(EDGetTokenString(&tokenStr, token)) + { + printTokenized = false; + break; + } + + operands.PutCString(tokenStr); + } + } // for (tokenIndex) + + if (printTokenized) + { + if (operands.GetString().empty()) + { + s->PutCString(opcode.GetString().c_str()); + } + else + { + PadString(s, opcode.GetString(), opcodeColumnWidth); + + if (comment.GetString().empty()) + { + s->PutCString(operands.GetString().c_str()); + } + else + { + PadString(s, operands.GetString(), operandColumnWidth); + + s->PutCString("; "); + s->PutCString(comment.GetString().c_str()); + } // else (comment.GetString().empty()) + } // else (operands.GetString().empty()) + } // printTokenized + } // for (tokenIndex) + } // numTokens != -1 + + if (!printTokenized) + { + const char *str; + + if (EDGetInstString(&str, m_inst)) + return; + else + s->PutCString(str); + } +} + +bool +DisassemblerLLVM::Instruction::DoesBranch() const +{ + return EDInstIsBranch(m_inst); +} + +size_t +DisassemblerLLVM::Instruction::GetByteSize() const +{ + return EDInstByteSize(m_inst); +} + +size_t +DisassemblerLLVM::Instruction::Extract(const DataExtractor &data, uint32_t data_offset) +{ + if (EDCreateInsts(&m_inst, 1, m_disassembler, DataExtractorByteReader, data_offset, (void*)(&data))) + return EDInstByteSize(m_inst); + else + return 0; +} + +static inline const char * +TripleForCPU(cpu_type_t cpuType) +{ + switch (cpuType) + { + default: + return NULL; + case CPU_TYPE_X86: + return "i386-unknown-unknown"; + case CPU_TYPE_X86_64: + return "x86_64-unknown-unknown"; + } +} + +static inline EDAssemblySyntax_t +SyntaxForCPU(cpu_type_t cpuType) +{ + switch (cpuType) + { + default: + return (EDAssemblySyntax_t)0; // default + case CPU_TYPE_X86: + case CPU_TYPE_X86_64: + return kEDAssemblySyntaxX86ATT; + } +} + +Disassembler * +DisassemblerLLVM::CreateInstance(const ArchSpec &arch) +{ + cpu_type_t cpuType = arch.GetCPUType(); + + if (TripleForCPU(cpuType)) + return new DisassemblerLLVM(arch); + else + return NULL; +} + +DisassemblerLLVM::DisassemblerLLVM(const ArchSpec &arch) : + Disassembler(arch) +{ + cpu_type_t cpuType = arch.GetCPUType(); + + const char *triple = TripleForCPU(cpuType); + assert(triple && "Unhandled CPU type!"); + + EDAssemblySyntax_t syntax = SyntaxForCPU(cpuType); + + assert(!EDGetDisassembler(&m_disassembler, triple, syntax) && "No disassembler created!"); +} + +DisassemblerLLVM::~DisassemblerLLVM() +{ +} + +size_t +DisassemblerLLVM::ParseInstructions +( + const DataExtractor& data, + uint32_t data_offset, + uint32_t num_instructions, + lldb::addr_t base_addr +) +{ + size_t total_inst_byte_size = 0; + + m_instruction_list.Clear(); + + while (data.ValidOffset(data_offset) && num_instructions) + { + Instruction::shared_ptr inst_sp (new Instruction(m_disassembler)); + + size_t inst_byte_size = inst_sp->Extract(data, data_offset); + + if (inst_byte_size == 0) + break; + + m_instruction_list.AppendInstruction(inst_sp); + + total_inst_byte_size += inst_byte_size; + data_offset += inst_byte_size; + num_instructions--; + } + + return total_inst_byte_size; +} + +void +DisassemblerLLVM::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +DisassemblerLLVM::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +DisassemblerLLVM::GetPluginNameStatic() +{ + return "disassembler.llvm"; +} + +const char * +DisassemblerLLVM::GetPluginDescriptionStatic() +{ + return "Disassembler that uses LLVM opcode tables to disassemble i386 and x86_64."; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +DisassemblerLLVM::GetPluginName() +{ + return "DisassemblerLLVM"; +} + +const char * +DisassemblerLLVM::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +DisassemblerLLVM::GetPluginVersion() +{ + return 1; +} + +void +DisassemblerLLVM::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +DisassemblerLLVM::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +DisassemblerLLVM::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + diff --git a/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVM.h b/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVM.h new file mode 100644 index 00000000000..98166cb558c --- /dev/null +++ b/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVM.h @@ -0,0 +1,111 @@ +//===-- DisassemblerLLVM.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_DisassemblerLLVM_h_ +#define liblldb_DisassemblerLLVM_h_ + +#include "lldb/Core/Disassembler.h" +#include "lldb/Host/Mutex.h" + +struct EDDisassembler; +typedef EDDisassembler *EDDisassemblerRef; + +struct EDInst; +typedef EDInst *EDInstRef; + +class DisassemblerLLVM : public lldb_private::Disassembler +{ +public: + class Instruction : public lldb_private::Disassembler::Instruction + { + public: + Instruction(EDDisassemblerRef disassembler); + + virtual + ~Instruction(); + + void + Dump (lldb_private::Stream *s, + lldb::addr_t base_address, + lldb_private::DataExtractor *bytes, + uint32_t bytes_offset, + const lldb_private::ExecutionContext exe_ctx, + bool raw); + + bool + DoesBranch () const; + + size_t + GetByteSize() const; + + size_t + Extract (const lldb_private::DataExtractor &data, + uint32_t data_offset); + + protected: + EDDisassemblerRef m_disassembler; + EDInstRef m_inst; + }; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::Disassembler * + CreateInstance(const lldb_private::ArchSpec &arch); + + + DisassemblerLLVM(const lldb_private::ArchSpec &arch); + + virtual + ~DisassemblerLLVM(); + + size_t + ParseInstructions (const lldb_private::DataExtractor& data, + uint32_t data_offset, + uint32_t num_instructions, + lldb::addr_t base_addr); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + +protected: + EDDisassemblerRef m_disassembler; +}; + +#endif // liblldb_DisassemblerLLVM_h_ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp new file mode 100644 index 00000000000..a8dbf053e3d --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp @@ -0,0 +1,1129 @@ +//===-- DynamicLoaderMacOSXDYLD.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/StackFrame.h" + +#include "DynamicLoaderMacOSXDYLD.h" +#include "DynamicLoaderMacOSXDYLDLog.h" + +//#define ENABLE_DEBUG_PRINTF // COMMENT THIS LINE OUT PRIOR TO CHECKIN +#ifdef ENABLE_DEBUG_PRINTF +#include <stdio.h> +#define DEBUG_PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_PRINTF(fmt, ...) +#endif + +using namespace lldb; +using namespace lldb_private; + + +/// FIXME - The ObjC Runtime trampoline handler doesn't really belong here. +/// I am putting it here so I can invoke it in the Trampoline code here, but +/// it should be moved to the ObjC Runtime support when it is set up. + +//---------------------------------------------------------------------- +// Create an instance of this class. This function is filled into +// the plugin info class that gets handed out by the plugin factory and +// allows the lldb to instantiate an instance of this class. +//---------------------------------------------------------------------- +DynamicLoader * +DynamicLoaderMacOSXDYLD::CreateInstance (Process* process) +{ + return new DynamicLoaderMacOSXDYLD (process); +} + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +DynamicLoaderMacOSXDYLD::DynamicLoaderMacOSXDYLD (Process* process) : + DynamicLoader(process), + m_dyld(), + m_dyld_all_image_infos_addr(LLDB_INVALID_ADDRESS), + m_dyld_all_image_infos(), + m_break_id(LLDB_INVALID_BREAK_ID), + m_dyld_image_infos(), + m_mutex(Mutex::eMutexTypeRecursive), + m_objc_trampoline_handler_ap(NULL) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DynamicLoaderMacOSXDYLD::~DynamicLoaderMacOSXDYLD() +{ + Clear(true); +} + +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderMacOSXDYLD::DidAttach () +{ + PrivateInitialize(m_process); + if (NeedToLocateDYLD ()) + LocateDYLD (); + SetNotificationBreakpoint (); + UpdateAllImageInfos(); +} + +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderMacOSXDYLD::DidLaunch () +{ + PrivateInitialize(m_process); + if (NeedToLocateDYLD ()) + LocateDYLD (); + SetNotificationBreakpoint (); + UpdateAllImageInfos(); +} + + +//---------------------------------------------------------------------- +// Clear out the state of this class. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::Clear (bool clear_process) +{ + Mutex::Locker locker(m_mutex); + + if (m_process->IsAlive() && LLDB_BREAK_ID_IS_VALID(m_break_id)) + m_process->ClearBreakpointSiteByID(m_break_id); + + if (clear_process) + m_process = NULL; + m_dyld.Clear(false); + m_dyld_all_image_infos_addr = LLDB_INVALID_ADDRESS; + m_dyld_all_image_infos.Clear(); + m_break_id = LLDB_INVALID_BREAK_ID; + m_dyld_image_infos.clear(); +} + +//---------------------------------------------------------------------- +// Check if we have found DYLD yet +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::DidSetNotificationBreakpoint() const +{ + return LLDB_BREAK_ID_IS_VALID (m_break_id); +} + +//---------------------------------------------------------------------- +// Try and figure out where dyld is by first asking the Process +// if it knows (which currently calls down in the the lldb::Process +// to get the DYLD info (available on SnowLeopard only). If that fails, +// then check in the default addresses. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::LocateDYLD() +{ + if (m_dyld_all_image_infos_addr == LLDB_INVALID_ADDRESS) + m_dyld_all_image_infos_addr = m_process->GetImageInfoAddress (); + + if (m_dyld_all_image_infos_addr != LLDB_INVALID_ADDRESS) + { + if (ReadAllImageInfosStructure ()) + { + if (m_dyld_all_image_infos.dyldImageLoadAddress != LLDB_INVALID_ADDRESS) + return ReadDYLDInfoFromMemoryAndSetNotificationCallback (m_dyld_all_image_infos.dyldImageLoadAddress); + else + return ReadDYLDInfoFromMemoryAndSetNotificationCallback (m_dyld_all_image_infos_addr & 0xfffffffffff00000ull); + } + } + + // Check some default values + Module *executable = m_process->GetTarget().GetExecutableModule().get(); + + if (executable) + { + if (executable->GetArchitecture().GetAddressByteSize() == 8) + { + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(0x7fff5fc00000ull); + } +#if defined (__arm__) + else + { + ArchSpec arm_arch("arm"); + if (arm_arch == executable->Arch()) + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(0x2fe00000); + } +#endif + return ReadDYLDInfoFromMemoryAndSetNotificationCallback(0x8fe00000); + } + return false; +} + +//---------------------------------------------------------------------- +// Assume that dyld is in memory at ADDR and try to parse it's load +// commands +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::ReadDYLDInfoFromMemoryAndSetNotificationCallback(lldb::addr_t addr) +{ + DataExtractor data; // Load command data + if (ReadMachHeader (addr, &m_dyld.header, &data)) + { + if (m_dyld.header.filetype == MH_DYLINKER) + { + m_dyld.address = addr; + ModuleSP dyld_module_sp; + if (ParseLoadCommands (data, m_dyld, &m_dyld.file_spec)) + { + if (m_dyld.file_spec) + { + ArchSpec dyld_arch(m_dyld.header.cputype, m_dyld.header.cpusubtype); + dyld_module_sp = m_process->GetTarget().GetImages().FindFirstModuleForFileSpec (m_dyld.file_spec); + + if (dyld_module_sp.get() == NULL || dyld_module_sp->GetArchitecture() != dyld_arch) + { + dyld_module_sp = m_process->GetTarget().GetSharedModule (m_dyld.file_spec, + dyld_arch, + &m_dyld.uuid); + } + + UpdateImageLoadAddress(dyld_module_sp.get(), m_dyld); + } + } + + if (m_dyld_all_image_infos_addr == LLDB_INVALID_ADDRESS && dyld_module_sp.get()) + { + static ConstString g_dyld_all_image_infos ("dyld_all_image_infos"); + const Symbol *symbol = dyld_module_sp->FindFirstSymbolWithNameAndType (g_dyld_all_image_infos, eSymbolTypeData); + if (symbol) + m_dyld_all_image_infos_addr = symbol->GetValue().GetLoadAddress(m_process); + } + + // Update all image infos + UpdateAllImageInfos(); + + // If we didn't have an executable before, but now we do, then the + // dyld module shared pointer might be unique and we may need to add + // it again (since Target::SetExecutableModule() will clear the + // images). So append the dyld module back to the list if it is + /// unique! + if (m_process->GetTarget().GetImages().AppendInNeeded (dyld_module_sp)) + UpdateImageLoadAddress(dyld_module_sp.get(), m_dyld); + + return true; + } + } + return false; +} + +bool +DynamicLoaderMacOSXDYLD::NeedToLocateDYLD () const +{ + return m_dyld_all_image_infos_addr == LLDB_INVALID_ADDRESS; +} + +bool +DynamicLoaderMacOSXDYLD::UpdateCommPageLoadAddress(Module *module) +{ + bool changed = false; + if (module) + { + ObjectFile *image_object_file = module->GetObjectFile(); + if (image_object_file) + { + SectionList *section_list = image_object_file->GetSectionList (); + if (section_list) + { + uint32_t num_sections = section_list->GetSize(); + for (uint32_t i=0; i<num_sections; ++i) + { + Section* section = section_list->GetSectionAtIndex (i).get(); + if (section) + { + const addr_t new_section_load_addr = section->GetFileAddress (); + const addr_t old_section_load_addr = m_process->GetSectionLoadAddress (section); + if (old_section_load_addr == LLDB_INVALID_ADDRESS || + old_section_load_addr != new_section_load_addr) + { + if (m_process->SectionLoaded (section, section->GetFileAddress ())) + changed = true; + } + } + } + } + } + } + return changed; +} + +//---------------------------------------------------------------------- +// Update the load addresses for all segments in MODULE using the +// updated INFO that is passed in. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::UpdateImageLoadAddress (Module *module, struct DYLDImageInfo& info) +{ + bool changed = false; + if (module) + { + ObjectFile *image_object_file = module->GetObjectFile(); + if (image_object_file) + { + SectionList *section_list = image_object_file->GetSectionList (); + if (section_list) + { + // All sections listed in the dyld image info structure will all + // either be fixed up already, or they will all be off by a single + // slide amount that is determined by finding the first segment + // that is at file offset zero which also has bytes (a file size + // that is greater than zero) in the object file. + + // Determine the slide amount (if any) + info.slide = 0; + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) + { + // Iterate through the object file sections to find the + // first section that starts of file offset zero and that + // has bytes in the file... + Section *section = section_list->GetSectionAtIndex (sect_idx).get(); + if (section) + { + // Find the first section that begins at file offset zero + // a file size (skip page zero). + if (section->GetFileOffset() == 0 && section->GetFileSize() > 0) + { + // We have now found the section, lets match it up + // with the section in the dyld image info structure. + const Segment *dyld_segment = info.FindSegment (section->GetName()); + if (dyld_segment) + info.slide = info.address - dyld_segment->addr; + // We have found the slide amount, so we can exit + // this for loop. + break; + } + } + } + + // We now know the slide amount, so go through all sections + // and update the load addresses with the correct values. + uint32_t num_segments = info.segments.size(); + for (uint32_t i=0; i<num_segments; ++i) + { + SectionSP section_sp(section_list->FindSectionByName(info.segments[i].name)); + assert (section_sp.get() != NULL); + const addr_t new_section_load_addr = info.segments[i].addr + info.slide; + const addr_t old_section_load_addr = m_process->GetSectionLoadAddress (section_sp.get()); + if (old_section_load_addr == LLDB_INVALID_ADDRESS || + old_section_load_addr != new_section_load_addr) + { + if (m_process->SectionLoaded (section_sp.get(), new_section_load_addr)) + changed = true; + } + } + } + } + } + return changed; +} + +//---------------------------------------------------------------------- +// Update the load addresses for all segments in MODULE using the +// updated INFO that is passed in. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::UnloadImageLoadAddress (Module *module, struct DYLDImageInfo& info) +{ + bool changed = false; + if (module) + { + ObjectFile *image_object_file = module->GetObjectFile(); + if (image_object_file) + { + SectionList *section_list = image_object_file->GetSectionList (); + if (section_list) + { + uint32_t num_segments = info.segments.size(); + for (uint32_t i=0; i<num_segments; ++i) + { + SectionSP section_sp(section_list->FindSectionByName(info.segments[i].name)); + assert (section_sp.get() != NULL); + const addr_t old_section_load_addr = info.segments[i].addr + info.slide; + if (m_process->SectionUnloaded (section_sp.get(), old_section_load_addr)) + changed = true; + } + } + } + } + return changed; +} + + +//---------------------------------------------------------------------- +// Static callback function that gets called when our DYLD notification +// breakpoint gets hit. We update all of our image infos and then +// let our super class DynamicLoader class decide if we should stop +// or not (based on global preference). +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::NotifyBreakpointHit (void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) +{ + // Let the event know that the images have changed + DynamicLoaderMacOSXDYLD* dyld_instance = (DynamicLoaderMacOSXDYLD*) baton; + dyld_instance->UpdateAllImageInfos(); + // Return true to stop the target, false to just let the target run + return dyld_instance->GetStopWhenImagesChange(); +} + +bool +DynamicLoaderMacOSXDYLD::ReadAllImageInfosStructure () +{ + Mutex::Locker locker(m_mutex); + m_dyld_all_image_infos.Clear(); + if (m_dyld_all_image_infos_addr != LLDB_INVALID_ADDRESS) + { + const ByteOrder endian = m_process->GetByteOrder(); + const uint32_t addr_size = m_process->GetAddressByteSize(); + uint8_t buf[256]; + const size_t count = 2 * sizeof(uint32_t) + // version + dylib_info_count + addr_size * 2 + // dylib_info_addr + notification + 2 + addr_size - 2 + // processDetachedFromSharedRegion + libSystemInitialized + pad + addr_size; // dyldImageLoadAddress + Error error; + const size_t bytes_read = m_process->ReadMemory (m_dyld_all_image_infos_addr, buf, count, error); + if (bytes_read == count) + { + DataExtractor data(buf, count, endian, addr_size); + uint32_t offset = 0; + m_dyld_all_image_infos.version = data.GetU32(&offset); + m_dyld_all_image_infos.dylib_info_count = data.GetU32(&offset); + m_dyld_all_image_infos.dylib_info_addr = data.GetPointer(&offset); + m_dyld_all_image_infos.notification = data.GetPointer(&offset); + m_dyld_all_image_infos.processDetachedFromSharedRegion = data.GetU8(&offset); + if (m_dyld_all_image_infos.version >= 2) + { + m_dyld_all_image_infos.libSystemInitialized = data.GetU8(&offset); + // Adjust for padding. + offset += addr_size - 2; + m_dyld_all_image_infos.dyldImageLoadAddress = data.GetPointer(&offset); + } + return true; + } + } + return false; +} + +//---------------------------------------------------------------------- +// If we have found where the "_dyld_all_image_infos" lives in memory, +// read the current info from it, and then update all image load +// addresses (or lack thereof). +//---------------------------------------------------------------------- +uint32_t +DynamicLoaderMacOSXDYLD::UpdateAllImageInfos() +{ + if (ReadAllImageInfosStructure ()) + { + Mutex::Locker locker(m_mutex); + uint32_t idx; + Error error; + uint32_t i = 0; + DYLDImageInfo::collection old_dyld_all_image_infos; + old_dyld_all_image_infos.swap(m_dyld_image_infos); + + // If we made it here, we are assuming that the all dylib info data should + // be valid, lets read the info array. + const ByteOrder endian = m_process->GetByteOrder(); + const uint32_t addr_size = m_process->GetAddressByteSize(); + + if (m_dyld_all_image_infos.dylib_info_count > 0) + { + if (m_dyld_all_image_infos.dylib_info_addr == 0) + { + // DYLD is updating the images right now... + } + else + { + m_dyld_image_infos.resize(m_dyld_all_image_infos.dylib_info_count); + const size_t count = m_dyld_image_infos.size() * 3 * addr_size; + DataBufferHeap info_data(count, 0); + Error error; + const size_t bytes_read = m_process->ReadMemory (m_dyld_all_image_infos.dylib_info_addr, + info_data.GetBytes(), + info_data.GetByteSize(), + error); + if (bytes_read == count) + { + uint32_t info_data_offset = 0; + DataExtractor info_data_ref(info_data.GetBytes(), info_data.GetByteSize(), endian, addr_size); + for (i = 0; info_data_ref.ValidOffset(info_data_offset); i++) + { + assert (i < m_dyld_image_infos.size()); + m_dyld_image_infos[i].address = info_data_ref.GetPointer(&info_data_offset); + lldb::addr_t path_addr = info_data_ref.GetPointer(&info_data_offset); + m_dyld_image_infos[i].mod_date = info_data_ref.GetPointer(&info_data_offset); + + char raw_path[PATH_MAX]; + m_process->ReadMemory (path_addr, raw_path, sizeof(raw_path), error); + m_dyld_image_infos[i].file_spec.SetFile(raw_path); + } + assert(i == m_dyld_all_image_infos.dylib_info_count); + + UpdateAllImageInfosHeaderAndLoadCommands(); + } + else + { + DEBUG_PRINTF( "unable to read all data for all_dylib_infos."); + m_dyld_image_infos.clear(); + } + } + } + else + { + m_dyld_image_infos.clear(); + } + + // If our new list is smaller than our old list, we have unloaded + // some shared libraries + if (m_dyld_image_infos.size() < old_dyld_all_image_infos.size()) + { + ModuleList unloaded_module_list; + for (idx = m_dyld_image_infos.size(); idx < old_dyld_all_image_infos.size(); ++idx) + { + ModuleSP unload_image_module_sp(m_process->GetTarget().GetImages().FindFirstModuleForFileSpec (old_dyld_all_image_infos[idx].file_spec)); + if (unload_image_module_sp.get()) + { + if (UnloadImageLoadAddress (unload_image_module_sp.get(), old_dyld_all_image_infos[idx])) + unloaded_module_list.AppendInNeeded (unload_image_module_sp); + } + } + if (unloaded_module_list.GetSize() > 0) + m_process->GetTarget().ModulesDidUnload (unloaded_module_list); + } + } + else + { + m_dyld_image_infos.clear(); + } + + const uint32_t num_dylibs = m_dyld_image_infos.size(); + if (num_dylibs > 0) + { + ModuleList loaded_module_list; + for (uint32_t idx = 0; idx<num_dylibs; ++idx) + { + ArchSpec arch_spec(m_dyld_image_infos[idx].header.cputype, m_dyld_image_infos[idx].header.cpusubtype); + ModuleSP image_module_sp(m_process->GetTarget().GetImages().FindFirstModuleForFileSpec (m_dyld_image_infos[idx].file_spec)); + if (image_module_sp.get() == NULL || image_module_sp->GetArchitecture() != arch_spec) + { + image_module_sp = m_process->GetTarget().GetSharedModule (m_dyld_image_infos[idx].file_spec, + arch_spec, + &m_dyld_image_infos[idx].uuid); + } + + if (image_module_sp) + { + ObjectFile *objfile = image_module_sp->GetObjectFile (); + if (objfile) + { + SectionList *sections = objfile->GetSectionList(); + if (sections) + { + ConstString commpage_dbstr("__commpage"); + Section *commpage_section = sections->FindSectionByName(commpage_dbstr).get(); + if (commpage_section) + { + FileSpec objfile_file_spec(objfile->GetFileSpec()); + ModuleSP commpage_image_module_sp(m_process->GetTarget().GetImages().FindFirstModuleForFileSpec (objfile_file_spec, &commpage_dbstr)); + if (commpage_image_module_sp.get() == NULL) + { + commpage_image_module_sp = m_process->GetTarget().GetSharedModule (m_dyld_image_infos[idx].file_spec, + arch_spec, + &m_dyld_image_infos[idx].uuid, + &commpage_dbstr, + objfile->GetOffset() + commpage_section->GetOffset()); + UpdateCommPageLoadAddress(commpage_image_module_sp.get()); + } + } + } + } + + // UpdateImageLoadAddress will return true if any segments + // change load address. We need to check this so we don't + // mention that all loaded shared libraries are newly loaded + // each time we hit out dyld breakpoint since dyld will list all + // shared libraries each time. + if (UpdateImageLoadAddress (image_module_sp.get(), m_dyld_image_infos[idx])) + { + loaded_module_list.AppendInNeeded (image_module_sp); + } + } + } + PutToLog(DynamicLoaderMacOSXDYLDLog::GetLogIfAllCategoriesSet (1)); + if (loaded_module_list.GetSize() > 0) + { + // FIXME: This should really be in the Runtime handlers class, which should get + // called by the target's ModulesDidLoad, but we're doing it all locally for now + // to save time. + // Also, I'm assuming there can be only one libobjc dylib loaded... + + if (m_objc_trampoline_handler_ap.get() == NULL) + { + size_t num_modules = loaded_module_list.GetSize(); + for (int i = 0; i < num_modules; i++) + { + if (ObjCTrampolineHandler::ModuleIsObjCLibrary (loaded_module_list.GetModuleAtIndex (i))) + { + m_objc_trampoline_handler_ap.reset (new ObjCTrampolineHandler(m_process->GetSP(), loaded_module_list.GetModuleAtIndex (i))); + break; + } + } + } + m_process->GetTarget().ModulesDidLoad (loaded_module_list); + } + } + return m_dyld_image_infos.size(); +} + +//---------------------------------------------------------------------- +// Read a mach_header at ADDR into HEADER, and also fill in the load +// command data into LOAD_COMMAND_DATA if it is non-NULL. +// +// Returns true if we succeed, false if we fail for any reason. +//---------------------------------------------------------------------- +bool +DynamicLoaderMacOSXDYLD::ReadMachHeader (lldb::addr_t addr, struct mach_header *header, DataExtractor *load_command_data) +{ + DataBufferHeap header_bytes(sizeof(struct mach_header), 0); + Error error; + size_t bytes_read = m_process->ReadMemory (addr, + header_bytes.GetBytes(), + header_bytes.GetByteSize(), + error); + if (bytes_read == sizeof(struct mach_header)) + { + uint32_t offset = 0; + ::memset (header, 0, sizeof(header)); + + // Get the magic byte unswapped so we can figure out what we are dealing with + DataExtractor data(header_bytes.GetBytes(), header_bytes.GetByteSize(), eByteOrderHost, 4); + header->magic = data.GetU32(&offset); + lldb::addr_t load_cmd_addr = addr; + data.SetByteOrder(DynamicLoaderMacOSXDYLD::GetByteOrderFromMagic(header->magic)); + switch (header->magic) + { + case MH_MAGIC: + case MH_CIGAM: + data.SetAddressByteSize(4); + load_cmd_addr += sizeof(struct mach_header); + break; + + case MH_MAGIC_64: + case MH_CIGAM_64: + data.SetAddressByteSize(8); + load_cmd_addr += sizeof(struct mach_header_64); + break; + + default: + return false; + } + + // Read the rest of dyld's mach header + if (data.GetU32(&offset, &header->cputype, (sizeof(struct mach_header)/sizeof(uint32_t)) - 1)) + { + if (load_command_data == NULL) + return true; // We were able to read the mach_header and weren't asked to read the load command bytes + + DataBufferSP load_cmd_data_sp(new DataBufferHeap(header->sizeofcmds, 0)); + + size_t load_cmd_bytes_read = m_process->ReadMemory (load_cmd_addr, + load_cmd_data_sp->GetBytes(), + load_cmd_data_sp->GetByteSize(), + error); + + if (load_cmd_bytes_read == header->sizeofcmds) + { + // Set the load command data and also set the correct endian + // swap settings and the correct address size + load_command_data->SetData(load_cmd_data_sp, 0, header->sizeofcmds); + load_command_data->SetByteOrder(data.GetByteOrder()); + load_command_data->SetAddressByteSize(data.GetAddressByteSize()); + return true; // We successfully read the mach_header and the load command data + } + + return false; // We weren't able to read the load command data + } + } + return false; // We failed the read the mach_header +} + + +//---------------------------------------------------------------------- +// Parse the load commands for an image +//---------------------------------------------------------------------- +uint32_t +DynamicLoaderMacOSXDYLD::ParseLoadCommands (const DataExtractor& data, struct DYLDImageInfo& dylib_info, FileSpec *lc_id_dylinker) +{ + uint32_t offset = 0; + uint32_t cmd_idx; + Segment segment; + dylib_info.Clear (true); + + for (cmd_idx = 0; cmd_idx < dylib_info.header.ncmds; cmd_idx++) + { + // Clear out any load command specific data from DYLIB_INFO since + // we are about to read it. + + if (data.ValidOffsetForDataOfSize (offset, sizeof(struct load_command))) + { + struct load_command load_cmd; + uint32_t load_cmd_offset = offset; + load_cmd.cmd = data.GetU32 (&offset); + load_cmd.cmdsize = data.GetU32 (&offset); + switch (load_cmd.cmd) + { + case LC_SEGMENT: + { + segment.name.SetTrimmedCStringWithLength ((const char *)data.GetData(&offset, 16), 16); + segment.addr = data.GetU32 (&offset); + segment.size = data.GetU32 (&offset); + dylib_info.segments.push_back (segment); + } + break; + + case LC_SEGMENT_64: + { + segment.name.SetTrimmedCStringWithLength ((const char *)data.GetData(&offset, 16), 16); + segment.addr = data.GetU64 (&offset); + segment.size = data.GetU64 (&offset); + dylib_info.segments.push_back (segment); + } + break; + + case LC_ID_DYLINKER: + if (lc_id_dylinker) + { + uint32_t name_offset = load_cmd_offset + data.GetU32 (&offset); + const char *path = data.PeekCStr (name_offset); + lc_id_dylinker->SetFile (path); + } + break; + + case LC_UUID: + dylib_info.uuid.SetBytes(data.GetData (&offset, 16)); + break; + + default: + break; + } + // Set offset to be the beginning of the next load command. + offset = load_cmd_offset + load_cmd.cmdsize; + } + } + return cmd_idx; +} + +//---------------------------------------------------------------------- +// Read the mach_header and load commands for each image that the +// _dyld_all_image_infos structure points to and cache the results. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::UpdateAllImageInfosHeaderAndLoadCommands() +{ + uint32_t exe_idx = UINT32_MAX; + // Read any UUID values that we can get + for (uint32_t i = 0; i < m_dyld_all_image_infos.dylib_info_count; i++) + { + if (!m_dyld_image_infos[i].UUIDValid()) + { + DataExtractor data; // Load command data + if (!ReadMachHeader (m_dyld_image_infos[i].address, &m_dyld_image_infos[i].header, &data)) + continue; + + ParseLoadCommands (data, m_dyld_image_infos[i], NULL); + + if (m_dyld_image_infos[i].header.filetype == MH_EXECUTE) + exe_idx = i; + } + } + + if (exe_idx < m_dyld_image_infos.size()) + { + bool set_executable = false; + ArchSpec dyld_exe_arch_spec(m_dyld_image_infos[exe_idx].header.cputype, m_dyld_image_infos[exe_idx].header.cpusubtype); + ModuleSP exe_module_sp(m_process->GetTarget().GetExecutableModule()); + if (exe_module_sp.get()) + { + if (exe_module_sp->GetFileSpec() != m_dyld_image_infos[exe_idx].file_spec || + exe_module_sp->GetArchitecture() != dyld_exe_arch_spec) + set_executable = true; + } + else + set_executable = true; + + if (set_executable) + { + exe_module_sp = m_process->GetTarget().GetSharedModule (m_dyld_image_infos[exe_idx].file_spec, + dyld_exe_arch_spec, + &m_dyld_image_infos[exe_idx].uuid); + if (exe_module_sp.get()) + { + // If we found the file where it purported to be, then it should + // be safe to load dependent images. + bool get_dependent_images = exe_module_sp->GetFileSpec() == m_dyld_image_infos[exe_idx].file_spec; + + m_process->GetTarget().SetExecutableModule (exe_module_sp, get_dependent_images); + } + } + } +} + +//---------------------------------------------------------------------- +// Dump a Segment to the file handle provided. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::Segment::PutToLog (Log *log, lldb::addr_t slide) const +{ + if (log) + log->Printf("\t\t%16s [0x%16.16llx - 0x%16.16llx)", name.AsCString(""), addr + slide, addr + slide + size); +} + +const DynamicLoaderMacOSXDYLD::Segment * +DynamicLoaderMacOSXDYLD::DYLDImageInfo::FindSegment (const ConstString &name) const +{ + const size_t num_segments = segments.size(); + for (size_t i=0; i<num_segments; ++i) + { + if (segments[i].name == name) + return &segments[i]; + } + return NULL; +} + + +//---------------------------------------------------------------------- +// Dump an image info structure to the file handle provided. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::DYLDImageInfo::PutToLog (Log *log) const +{ + if (log == NULL) + return; + uint8_t *u = (uint8_t *)uuid.GetBytes(); + + if (address == LLDB_INVALID_ADDRESS) + { + if (u) + { + log->Printf("\t modtime=0x%8.8llx uuid=%2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X path='%s/%s' (UNLOADED)", + mod_date, + u[ 0], u[ 1], u[ 2], u[ 3], + u[ 4], u[ 5], u[ 6], u[ 7], + u[ 8], u[ 9], u[10], u[11], + u[12], u[13], u[14], u[15], + file_spec.GetDirectory().AsCString(), + file_spec.GetFilename().AsCString()); + } + else + log->Printf("\t modtime=0x%8.8llx path='%s/%s' (UNLOADED)", + mod_date, + file_spec.GetDirectory().AsCString(), + file_spec.GetFilename().AsCString()); + } + else + { + if (u) + { + log->Printf("\taddress=0x%16.16llx modtime=0x%8.8llx uuid=%2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X path='%s/%s'", + address, + mod_date, + u[ 0], u[ 1], u[ 2], u[ 3], + u[ 4], u[ 5], u[ 6], u[ 7], + u[ 8], u[ 9], u[10], u[11], + u[12], u[13], u[14], u[15], + file_spec.GetDirectory().AsCString(), + file_spec.GetFilename().AsCString()); + } + else + { + log->Printf("\taddress=0x%16.16llx modtime=0x%8.8llx path='%s/%s'", + address, + mod_date, + file_spec.GetDirectory().AsCString(), + file_spec.GetFilename().AsCString()); + + } + for (uint32_t i=0; i<segments.size(); ++i) + segments[i].PutToLog(log, slide); + } +} + +//---------------------------------------------------------------------- +// Dump the _dyld_all_image_infos members and all current image infos +// that we have parsed to the file handle provided. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::PutToLog(Log *log) const +{ + if (log == NULL) + return; + + Mutex::Locker locker(m_mutex); + log->Printf("dyld_all_image_infos = { version=%d, count=%d, addr=0x%8.8llx, notify=0x%8.8llx }", + m_dyld_all_image_infos.version, + m_dyld_all_image_infos.dylib_info_count, + (uint64_t)m_dyld_all_image_infos.dylib_info_addr, + (uint64_t)m_dyld_all_image_infos.notification); + size_t i; + const size_t count = m_dyld_image_infos.size(); + if (count > 0) + { + log->Printf("\tdyld_image_infos"); + for (i = 0; i<count; i++) + m_dyld_image_infos[i].PutToLog(log); + } +} + +//---------------------------------------------------------------------- +// Static callback function that gets called when the process state +// changes. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::Initialize(void *baton, Process *process) +{ + ((DynamicLoaderMacOSXDYLD*)baton)->PrivateInitialize(process); +} + +void +DynamicLoaderMacOSXDYLD::PrivateInitialize(Process *process) +{ + DEBUG_PRINTF("DynamicLoaderMacOSXDYLD::%s() process state = %s\n", __FUNCTION__, StateAsCString(m_process->GetState())); + Clear(true); + m_process = process; +} + + +//---------------------------------------------------------------------- +// Static callback function that gets called when the process state +// changes. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::ProcessStateChanged(void *baton, Process *process, StateType state) +{ + ((DynamicLoaderMacOSXDYLD*)baton)->PrivateProcessStateChanged(process, state); +} + +bool +DynamicLoaderMacOSXDYLD::SetNotificationBreakpoint () +{ + DEBUG_PRINTF("DynamicLoaderMacOSXDYLD::%s() process state = %s\n", __FUNCTION__, StateAsCString(m_process->GetState())); + if (m_break_id == LLDB_INVALID_BREAK_ID) + { + if (m_dyld_all_image_infos.notification != LLDB_INVALID_ADDRESS) + { + Address so_addr; + // Set the notification breakpoint and install a breakpoint + // callback function that will get called each time the + // breakpoint gets hit. We will use this to track when shared + // libraries get loaded/unloaded. + + if (m_process->ResolveLoadAddress(m_dyld_all_image_infos.notification, so_addr)) + { + Breakpoint *dyld_break = m_process->GetTarget().CreateBreakpoint (so_addr, true).get(); + dyld_break->SetCallback (DynamicLoaderMacOSXDYLD::NotifyBreakpointHit, this, true); + m_break_id = dyld_break->GetID(); + } + } + } + return m_break_id != LLDB_INVALID_BREAK_ID; +} + +//----------------------------------------------------------------------Target.h + +// Member function that gets called when the process state changes. +//---------------------------------------------------------------------- +void +DynamicLoaderMacOSXDYLD::PrivateProcessStateChanged (Process *process, StateType state) +{ + DEBUG_PRINTF("DynamicLoaderMacOSXDYLD::%s(%s)\n", __FUNCTION__, StateAsCString(state)); + switch (state) + { + case eStateAttaching: + case eStateLaunching: + case eStateInvalid: + case eStateUnloaded: + case eStateExited: + case eStateDetached: + Clear(false); + break; + + case eStateStopped: + // Keep trying find dyld and set our notification breakpoint each time + // we stop until we succeed + if (!DidSetNotificationBreakpoint () && m_process->IsAlive()) + { + if (NeedToLocateDYLD ()) + LocateDYLD (); + + SetNotificationBreakpoint (); + } + break; + + case eStateRunning: + case eStateStepping: + case eStateCrashed: + case eStateSuspended: + break; + + default: + break; + } +} + +ThreadPlanSP +DynamicLoaderMacOSXDYLD::GetStepThroughTrampolinePlan (Thread &thread, bool stop_others) +{ + ThreadPlanSP thread_plan_sp; + StackFrame *current_frame = thread.GetStackFrameAtIndex(0).get(); + const SymbolContext ¤t_context = current_frame->GetSymbolContext(eSymbolContextSymbol); + Symbol *current_symbol = current_context.symbol; + + if (current_symbol != NULL) + { + if (current_symbol->IsTrampoline()) + { + const ConstString &trampoline_name = current_symbol->GetMangled().GetName(); + if (trampoline_name) + { + SymbolContextList target_symbols; + ModuleList &images = thread.GetProcess().GetTarget().GetImages(); + images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode, target_symbols); + // FIXME - Make the Run to Address take multiple addresses, and + // run to any of them. + if (target_symbols.GetSize() == 1) + { + SymbolContext context; + AddressRange addr_range; + if (target_symbols.GetContextAtIndex(0, context)) + { + context.GetAddressRange (eSymbolContextEverything, addr_range); + thread_plan_sp.reset (new ThreadPlanRunToAddress (thread, addr_range.GetBaseAddress(), stop_others)); + } + } + else if (target_symbols.GetSize() > 1) + { + Log *log = DynamicLoaderMacOSXDYLDLog::GetLogIfAllCategoriesSet (1); + if (log) + { + log->Printf ("Found more than one symbol for trampoline target: \"%s\"", trampoline_name.AsCString()); + } + } + else + { + Log *log = DynamicLoaderMacOSXDYLDLog::GetLogIfAllCategoriesSet (1); + if (log) + { + log->Printf ("Could not find symbol for trampoline target: \"%s\"", trampoline_name.AsCString()); + } + } + } + } + } + + if (thread_plan_sp == NULL && m_objc_trampoline_handler_ap.get()) + thread_plan_sp = m_objc_trampoline_handler_ap->GetStepThroughDispatchPlan (thread, stop_others); + + return thread_plan_sp; +} + +void +DynamicLoaderMacOSXDYLD::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +DynamicLoaderMacOSXDYLD::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +DynamicLoaderMacOSXDYLD::GetPluginNameStatic() +{ + return "dynamic-loader.macosx-dyld"; +} + +const char * +DynamicLoaderMacOSXDYLD::GetPluginDescriptionStatic() +{ + return "Dynamic loader plug-in that watches for shared library loads/unloads in MacOSX user processes."; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +DynamicLoaderMacOSXDYLD::GetPluginName() +{ + return "DynamicLoaderMacOSXDYLD"; +} + +const char * +DynamicLoaderMacOSXDYLD::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +DynamicLoaderMacOSXDYLD::GetPluginVersion() +{ + return 1; +} + +void +DynamicLoaderMacOSXDYLD::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +DynamicLoaderMacOSXDYLD::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +DynamicLoaderMacOSXDYLD::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h new file mode 100644 index 00000000000..724a8b6bd6d --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h @@ -0,0 +1,360 @@ +//===-- DynamicLoaderMacOSXDYLD.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_DynamicLoaderMacOSXDYLD_h_ +#define liblldb_DynamicLoaderMacOSXDYLD_h_ + +// C Includes +#include <mach-o/loader.h> + +// C++ Includes +#include <map> +#include <vector> +#include <string> + +// Other libraries and framework includes +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" +#include "ObjCTrampolineHandler.h" + +class DynamicLoaderMacOSXDYLD : public lldb_private::DynamicLoader +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance (lldb_private::Process *process); + + DynamicLoaderMacOSXDYLD (lldb_private::Process *process); + + virtual + ~DynamicLoaderMacOSXDYLD (); + //------------------------------------------------------------------ + /// Called after attaching a process. + /// + /// Allow DynamicLoader plug-ins to execute some code after + /// attaching to a process. + //------------------------------------------------------------------ + virtual void + DidAttach (); + + virtual void + DidLaunch (); + + //------------------------------------------------------------------ + // Process::Notifications callback functions + //------------------------------------------------------------------ + static void + Initialize (void *baton, + lldb_private::Process *process); + + static void + ProcessStateChanged (void *baton, + lldb_private::Process *process, + lldb::StateType state); + + virtual lldb::ThreadPlanSP + GetStepThroughTrampolinePlan (lldb_private::Thread &thread, + bool stop_others); + + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + + + +protected: + void + PrivateInitialize (lldb_private::Process *process); + + void + PrivateProcessStateChanged (lldb_private::Process *process, + lldb::StateType state); + bool + LocateDYLD (); + + bool + DidSetNotificationBreakpoint () const; + + void + Clear (bool clear_process); + + void + PutToLog (lldb_private::Log *log) const; + + bool + ReadDYLDInfoFromMemoryAndSetNotificationCallback (lldb::addr_t addr); + + uint32_t + UpdateAllImageInfos (); + + static bool + NotifyBreakpointHit (void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + void + UpdateAllImageInfosHeaderAndLoadCommands (); + + bool + UpdateCommPageLoadAddress (lldb_private::Module *module); + + uint32_t + AddrByteSize() + { + switch (m_dyld.header.magic) + { + case MH_MAGIC: + case MH_CIGAM: + return 4; + + case MH_MAGIC_64: + case MH_CIGAM_64: + return 8; + + default: + break; + } + return 0; + } + + static lldb::ByteOrder + GetByteOrderFromMagic (uint32_t magic) + { + switch (magic) + { + case MH_MAGIC: + case MH_MAGIC_64: + return lldb::eByteOrderHost; + + case MH_CIGAM: + case MH_CIGAM_64: + if (lldb::eByteOrderHost == lldb::eByteOrderBig) + return lldb::eByteOrderLittle; + else + return lldb::eByteOrderBig; + + default: + break; + } + return lldb::eByteOrderInvalid; + } + + bool + ReadMachHeader (lldb::addr_t addr, + struct mach_header *header, + lldb_private::DataExtractor *load_command_data); + class Segment + { + public: + + Segment() : + name(), + addr(LLDB_INVALID_ADDRESS), + size(0) + { + } + + lldb_private::ConstString name; + lldb::addr_t addr; + lldb::addr_t size; + + bool + operator==(const Segment& rhs) const + { + return name == rhs.name && addr == rhs.addr && size == rhs.size; + } + + void + PutToLog (lldb_private::Log *log, + lldb::addr_t slide) const; + + }; + + struct DYLDImageInfo + { + lldb::addr_t address; // Address of mach header for this dylib + lldb::addr_t slide; // The amount to slide all segments by if there is a global slide. + lldb::addr_t mod_date; // Modification date for this dylib + lldb_private::FileSpec file_spec; // Resolved path for this dylib + lldb_private::UUID uuid; // UUID for this dylib if it has one, else all zeros + struct mach_header header; // The mach header for this image + std::vector<Segment> segments; // All segment vmaddr and vmsize pairs for this executable (from memory of inferior) + + DYLDImageInfo() : + address(LLDB_INVALID_ADDRESS), + slide(0), + mod_date(0), + file_spec(), + uuid(), + header(), + segments() + { + } + + void + Clear(bool load_cmd_data_only) + { + if (!load_cmd_data_only) + { + address = LLDB_INVALID_ADDRESS; + slide = 0; + mod_date = 0; + file_spec.Clear(); + ::bzero (&header, sizeof(header)); + } + uuid.Clear(); + segments.clear(); + } + + bool + operator == (const DYLDImageInfo& rhs) const + { + return address == rhs.address + && slide == rhs.slide + && mod_date == rhs.mod_date + && file_spec == rhs.file_spec + && uuid == rhs.uuid + && memcmp(&header, &rhs.header, sizeof(header)) == 0 + && segments == rhs.segments; + } + + bool + UUIDValid() const + { + return uuid.IsValid(); + } + + const Segment * + FindSegment (const lldb_private::ConstString &name) const; + + void + PutToLog (lldb_private::Log *log) const; + + typedef std::vector<DYLDImageInfo> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + }; + + struct DYLDAllImageInfos + { + uint32_t version; + uint32_t dylib_info_count; // Version >= 1 + lldb::addr_t dylib_info_addr; // Version >= 1 + lldb::addr_t notification; // Version >= 1 + bool processDetachedFromSharedRegion; // Version >= 1 + bool libSystemInitialized; // Version >= 2 + lldb::addr_t dyldImageLoadAddress; // Version >= 2 + + DYLDAllImageInfos() : + version (0), + dylib_info_count (0), + dylib_info_addr (LLDB_INVALID_ADDRESS), + notification (LLDB_INVALID_ADDRESS), + processDetachedFromSharedRegion (false), + libSystemInitialized (false), + dyldImageLoadAddress (LLDB_INVALID_ADDRESS) + { + } + + void + Clear() + { + version = 0; + dylib_info_count = 0; + dylib_info_addr = LLDB_INVALID_ADDRESS; + notification = LLDB_INVALID_ADDRESS; + processDetachedFromSharedRegion = false; + libSystemInitialized = false; + dyldImageLoadAddress = LLDB_INVALID_ADDRESS; + } + + bool + IsValid() const + { + return version >= 1 || version <= 6; + } + }; + + void + RegisterNotificationCallbacks(); + + void + UnregisterNotificationCallbacks(); + + uint32_t + ParseLoadCommands (const lldb_private::DataExtractor& data, + struct DYLDImageInfo& dylib_info, + lldb_private::FileSpec *lc_id_dylinker); + + bool + UpdateImageLoadAddress(lldb_private::Module *module, + struct DYLDImageInfo& info); + + bool + UnloadImageLoadAddress (lldb_private::Module *module, + struct DYLDImageInfo& info); + + bool + NeedToLocateDYLD () const; + + bool + SetNotificationBreakpoint (); + + bool + ReadAllImageInfosStructure (); + + DYLDImageInfo m_dyld; // Info about the curent dyld being used + lldb::addr_t m_dyld_all_image_infos_addr; + DYLDAllImageInfos m_dyld_all_image_infos; + lldb::user_id_t m_break_id; + DYLDImageInfo::collection m_dyld_image_infos; // Current shared libraries information + mutable lldb_private::Mutex m_mutex; + lldb_private::Process::Notifications m_notification_callbacks; + std::auto_ptr<lldb_private::ObjCTrampolineHandler> m_objc_trampoline_handler_ap; + +private: + DISALLOW_COPY_AND_ASSIGN (DynamicLoaderMacOSXDYLD); +}; + +#endif // liblldb_DynamicLoaderMacOSXDYLD_h_ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.cpp new file mode 100644 index 00000000000..946c8f9310c --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.cpp @@ -0,0 +1,72 @@ +//===-- DynamicLoaderMacOSXDYLDLog.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DynamicLoaderMacOSXDYLDLog.h" +#include "lldb/Core/Log.h" + +using namespace lldb_private; + +static Log * +LogAccessor (bool get, Log *log) +{ + static Log* g_log = NULL; // Leak for now as auto_ptr was being cleaned up + // by global constructors before other threads + // were done with it. + if (get) + { +// // Debug code below for enabling logging by default +// if (g_log == NULL) +// { +// g_log = new Log("/dev/stdout", false); +// g_log->GetMask().SetAllFlagBits(0xffffffffu); +// g_log->GetOptions().Set(LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_THREAD_NAME); +// } + } + else + { + if (g_log) + delete g_log; + g_log = log; + } + + return g_log; +} + +Log * +DynamicLoaderMacOSXDYLDLog::GetLogIfAllCategoriesSet (uint32_t mask) +{ + Log *log = LogAccessor (true, NULL); + if (log && mask) + { + uint32_t log_mask = log->GetMask().GetAllFlagBits(); + if ((log_mask & mask) != mask) + return NULL; + } + return log; +} + +void +DynamicLoaderMacOSXDYLDLog::SetLog (Log *log) +{ + LogAccessor (false, log); +} + + +void +DynamicLoaderMacOSXDYLDLog::LogIf (uint32_t mask, const char *format, ...) +{ + Log *log = DynamicLoaderMacOSXDYLDLog::GetLogIfAllCategoriesSet (mask); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.h b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.h new file mode 100644 index 00000000000..9282ba57647 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLDLog.h @@ -0,0 +1,34 @@ +//===-- DynamicLoaderMacOSXDYLDLog.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_DynamicLoaderMacOSXDYLDLog_h_ +#define liblldb_DynamicLoaderMacOSXDYLDLog_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +#include "lldb/lldb-private.h" + +// Project includes + +class DynamicLoaderMacOSXDYLDLog +{ +public: + static lldb_private::Log * + GetLogIfAllCategoriesSet (uint32_t mask); + + static void + SetLog (lldb_private::Log *log); + + static void + LogIf (uint32_t mask, const char *format, ...); +}; + +#endif // liblldb_DynamicLoaderMacOSXDYLDLog_h_ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.cpp new file mode 100644 index 00000000000..169dc89c791 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.cpp @@ -0,0 +1,328 @@ +//===-- ObjCTrampolineHandler.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjCTrampolineHandler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Core/Value.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/ExecutionContext.h" +#include "ThreadPlanStepThroughObjCTrampoline.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" + +using namespace lldb; +using namespace lldb_private; + +const ObjCTrampolineHandler::DispatchFunction +ObjCTrampolineHandler::g_dispatch_functions[] = +{ + // NAME STRET SUPER FIXUP TYPE + {"objc_msgSend", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSend_fixup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSend_fixedup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {"objc_msgSend_stret", true, false, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSend_stret_fixup", true, false, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSend_stret_fixedup", true, false, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {"objc_msgSend_fpret", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSend_fpret_fixup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSend_fpret_fixedup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {"objc_msgSend_fp2ret", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSend_fp2ret_fixup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSend_fp2ret_fixedup", false, false, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {"objc_msgSendSuper", false, true, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSendSuper_stret", true, true, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSendSuper2", false, true, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSendSuper2_fixup", false, true, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSendSuper2_fixedup", false, true, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {"objc_msgSendSuper2_stret", true, true, ObjCTrampolineHandler::DispatchFunction::eFixUpNone }, + {"objc_msgSendSuper2_stret_fixup", true, true, ObjCTrampolineHandler::DispatchFunction::eFixUpToFix }, + {"objc_msgSendSuper2_stret_fixedup", true, true, ObjCTrampolineHandler::DispatchFunction::eFixUpFixed }, + {NULL} +}; + +bool +ObjCTrampolineHandler::ModuleIsObjCLibrary (const ModuleSP &module_sp) +{ + const FileSpec &module_file_spec = module_sp->GetFileSpec(); + static ConstString ObjCName ("libobjc.A.dylib"); + + if (module_file_spec) + { + if (module_file_spec.GetFilename() == ObjCName) + return true; + } + + return false; +} + +ObjCTrampolineHandler::ObjCTrampolineHandler (ProcessSP process_sp, ModuleSP objc_module) : + m_process_sp (process_sp), + m_objc_module_sp (objc_module), + m_impl_fn_addr (LLDB_INVALID_ADDRESS), + m_impl_stret_fn_addr (LLDB_INVALID_ADDRESS) +{ + // Look up the known resolution functions: + + ConstString get_impl_name("class_getMethodImplementation"); + ConstString get_impl_stret_name("class_getMethodImplementation_stret"); + + const Symbol *class_getMethodImplementation = m_objc_module_sp->FindFirstSymbolWithNameAndType (get_impl_name, eSymbolTypeCode); + const Symbol *class_getMethodImplementation_stret = m_objc_module_sp->FindFirstSymbolWithNameAndType (get_impl_stret_name, eSymbolTypeCode); + + if (class_getMethodImplementation) + m_impl_fn_addr = class_getMethodImplementation->GetValue().GetLoadAddress(m_process_sp.get()); + if (class_getMethodImplementation_stret) + m_impl_stret_fn_addr = class_getMethodImplementation_stret->GetValue().GetLoadAddress(m_process_sp.get()); + + // FIXME: Do some kind of logging here. + if (m_impl_fn_addr == LLDB_INVALID_ADDRESS || m_impl_stret_fn_addr == LLDB_INVALID_ADDRESS) + return; + + // Look up the addresses for the objc dispatch functions and cache them. For now I'm inspecting the symbol + // names dynamically to figure out how to dispatch to them. If it becomes more complicated than this we can + // turn the g_dispatch_functions char * array into a template table, and populate the DispatchFunction map + // from there. + + for (int i = 0; g_dispatch_functions[i].name != NULL; i++) + { + ConstString name_const_str(g_dispatch_functions[i].name); + const Symbol *msgSend_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType (name_const_str, eSymbolTypeCode); + if (msgSend_symbol) + { + // FixMe: Make g_dispatch_functions static table of DisptachFunctions, and have the map be address->index. + // Problem is we also need to lookup the dispatch function. For now we could have a side table of stret & non-stret + // dispatch functions. If that's as complex as it gets, we're fine. + + lldb::addr_t sym_addr = msgSend_symbol->GetValue().GetLoadAddress(m_process_sp.get()); + + m_msgSend_map.insert(std::pair<lldb::addr_t, int>(sym_addr, i)); + } + } +} + +ThreadPlanSP +ObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool stop_others) +{ + ThreadPlanSP ret_plan_sp; + lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC(); + + MsgsendMap::iterator pos; + pos = m_msgSend_map.find (curr_pc); + if (pos != m_msgSend_map.end()) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + const DispatchFunction *this_dispatch = &g_dispatch_functions[(*pos).second]; + + lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0); + + Process *process = thread.CalculateProcess(); + const ABI *abi = process->GetABI(); + if (abi == NULL) + return ret_plan_sp; + + Target *target = thread.CalculateTarget(); + + // FIXME: Since neither the value nor the Clang QualType know their ASTContext, + // we have to make sure the type we put in our value list comes from the same ASTContext + // the ABI will use to get the argument values. THis is the bottom-most frame's module. + + ClangASTContext *clang_ast_context = target->GetScratchClangASTContext(); + ValueList argument_values; + Value input_value; + void *clang_void_ptr_type = clang_ast_context->GetVoidPtrType(false); + input_value.SetValueType (Value::eValueTypeScalar); + input_value.SetContext (Value::eContextTypeOpaqueClangQualType, clang_void_ptr_type); + + int obj_index; + int sel_index; + + // If this is a struct return dispatch, then the first argument is the + // return struct pointer, and the object is the second, and the selector is the third. + // Otherwise the object is the first and the selector the second. + if (this_dispatch->stret_return) + { + obj_index = 1; + sel_index = 2; + argument_values.PushValue(input_value); + argument_values.PushValue(input_value); + argument_values.PushValue(input_value); + } + else + { + obj_index = 0; + sel_index = 1; + argument_values.PushValue(input_value); + argument_values.PushValue(input_value); + } + + + bool success = abi->GetArgumentValues (thread, argument_values); + if (!success) + return ret_plan_sp; + + // Okay, the first value here is the object, we actually want the class of that object. + // For now we're just going with the ISA. + // FIXME: This should really be the return value of [object class] to properly handle KVO interposition. + + Value isa_value(*(argument_values.GetValueAtIndex(obj_index))); + + // This is a little cheesy, but since object->isa is the first field, + // making the object value a load address value and resolving it will get + // the pointer sized data pointed to by that value... + ExecutionContext exec_ctx; + thread.Calculate (exec_ctx); + + isa_value.SetValueType(Value::eValueTypeLoadAddress); + isa_value.ResolveValue(&exec_ctx, clang_ast_context->getASTContext()); + + if (this_dispatch->fixedup == DispatchFunction::eFixUpFixed) + { + // For the FixedUp method the Selector is actually a pointer to a + // structure, the second field of which is the selector number. + Value *sel_value = argument_values.GetValueAtIndex(sel_index); + sel_value->GetScalar() += process->GetAddressByteSize(); + sel_value->SetValueType(Value::eValueTypeLoadAddress); + sel_value->ResolveValue(&exec_ctx, clang_ast_context->getASTContext()); + } + else if (this_dispatch->fixedup == DispatchFunction::eFixUpToFix) + { + // FIXME: If the method dispatch is not "fixed up" then the selector is actually a + // pointer to the string name of the selector. We need to look that up... + // For now I'm going to punt on that and just return no plan. + if (log) + log->Printf ("Punting on stepping into un-fixed-up method dispatch."); + return ret_plan_sp; + } + + // FIXME: If this is a dispatch to the super-class, we need to get the super-class from + // the class, and disaptch to that instead. + // But for now I just punt and return no plan. + if (this_dispatch->is_super) + { + if (log) + log->Printf ("Punting on stepping into super method dispatch."); + return ret_plan_sp; + } + + ValueList dispatch_values; + dispatch_values.PushValue (isa_value); + dispatch_values.PushValue(*(argument_values.GetValueAtIndex(sel_index))); + + if (log) + { + log->Printf("Resolving method call for class - 0x%llx and selector - 0x%llx", + dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(), + dispatch_values.GetValueAtIndex(1)->GetScalar().ULongLong()); + } + + lldb::addr_t impl_addr = LookupInCache (dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(), + dispatch_values.GetValueAtIndex(1)->GetScalar().ULongLong()); + + if (impl_addr == LLDB_INVALID_ADDRESS) + { + + Address resolve_address(NULL, this_dispatch->stret_return ? m_impl_stret_fn_addr : m_impl_fn_addr); + + StreamString errors; + { + // Scope for mutex locker: + Mutex::Locker (m_impl_function_mutex); + if (!m_impl_function.get()) + { + m_impl_function.reset(new ClangFunction(process->GetTargetTriple().GetCString(), + clang_ast_context, + clang_void_ptr_type, + resolve_address, + dispatch_values)); + + unsigned num_errors = m_impl_function->CompileFunction(errors); + if (num_errors) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf ("Error compiling function: \"%s\".", errors.GetData()); + return ret_plan_sp; + } + + errors.Clear(); + if (!m_impl_function->WriteFunctionWrapper(exec_ctx, errors)) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf ("Error Inserting function: \"%s\".", errors.GetData()); + return ret_plan_sp; + } + } + + } + + errors.Clear(); + + // Now write down the argument values for this call. + lldb::addr_t args_addr = LLDB_INVALID_ADDRESS; + if (!m_impl_function->WriteFunctionArguments (exec_ctx, args_addr, resolve_address, dispatch_values, errors)) + return ret_plan_sp; + + ret_plan_sp.reset (new ThreadPlanStepThroughObjCTrampoline (thread, this, args_addr, + argument_values.GetValueAtIndex(0)->GetScalar().ULongLong(), + dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(), + dispatch_values.GetValueAtIndex(1)->GetScalar().ULongLong(), + stop_others)); + } + else + { + if (log) + log->Printf ("Found implementation address in cache: 0x%llx", impl_addr); + + ret_plan_sp.reset (new ThreadPlanRunToAddress (thread, impl_addr, stop_others)); + } + } + + return ret_plan_sp; +} + +void +ObjCTrampolineHandler::AddToCache (lldb::addr_t class_addr, lldb::addr_t selector, lldb::addr_t impl_addr) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + { + log->Printf ("Caching: class 0x%llx selector 0x%llx implementation 0x%llx.", class_addr, selector, impl_addr); + } + m_impl_cache.insert (std::pair<ClassAndSel,lldb::addr_t> (ClassAndSel(class_addr, selector), impl_addr)); +} + +lldb::addr_t +ObjCTrampolineHandler::LookupInCache (lldb::addr_t class_addr, lldb::addr_t selector) +{ + MsgImplMap::iterator pos, end = m_impl_cache.end(); + pos = m_impl_cache.find (ClassAndSel(class_addr, selector)); + if (pos != end) + return (*pos).second; + return LLDB_INVALID_ADDRESS; +} + +ClangFunction * +ObjCTrampolineHandler::GetLookupImplementationWrapperFunction () +{ + return m_impl_function.get(); +} diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.h b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.h new file mode 100644 index 00000000000..bc06d267d2f --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ObjCTrampolineHandler.h @@ -0,0 +1,133 @@ +//===-- ObjCTrampolineHandler.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_ObjCTrampolineHandler_h_ +#define lldb_ObjCTrampolineHandler_h_ + +// C Includes +// C++ Includes +#include <map> +#include <string> +// Other libraries and framework includes +// Project includes +#include "lldb.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Host/Mutex.h" + + +namespace lldb_private +{ +using namespace lldb; + +class ObjCTrampolineHandler { +public: + + ObjCTrampolineHandler (ProcessSP process_sp, ModuleSP objc_module_sp); + + ~ObjCTrampolineHandler() {} + + static bool ModuleIsObjCLibrary (const ModuleSP &module_sp); + + ThreadPlanSP + GetStepThroughDispatchPlan (Thread &thread, bool stop_others); + + void + AddToCache (lldb::addr_t class_addr, lldb::addr_t sel, lldb::addr_t impl_addr); + + lldb::addr_t + LookupInCache (lldb::addr_t class_addr, lldb::addr_t sel); + + ClangFunction * + GetLookupImplementationWrapperFunction (); + + + struct DispatchFunction { + public: + typedef enum + { + eFixUpNone, + eFixUpFixed, + eFixUpToFix + } FixUpState; + + const char *name; + bool stret_return; + bool is_super; + FixUpState fixedup; + }; + +private: + static const DispatchFunction g_dispatch_functions[]; + + typedef std::map<lldb::addr_t, int> MsgsendMap; // This table maps an dispatch fn address to the index in g_dispatch_functions + MsgsendMap m_msgSend_map; + ProcessSP m_process_sp; + ModuleSP m_objc_module_sp; + lldb::addr_t get_impl_addr; + std::auto_ptr<ClangFunction> m_impl_function; + Mutex m_impl_function_mutex; + lldb::addr_t m_impl_fn_addr; + lldb::addr_t m_impl_stret_fn_addr; + + + // We keep a map of <Class,Selector>->Implementation so we don't have to call the resolver + // function over and over. + + // FIXME: We need to watch for the loading of Protocols, and flush the cache for any + // class that we see so changed. + + struct ClassAndSel + { + ClassAndSel() + { + sel_addr = LLDB_INVALID_ADDRESS; + class_addr = LLDB_INVALID_ADDRESS; + } + ClassAndSel (lldb::addr_t in_sel_addr, lldb::addr_t in_class_addr) : + class_addr (in_class_addr), + sel_addr(in_sel_addr) + { + } + bool operator== (const ClassAndSel &rhs) + { + if (class_addr == rhs.class_addr + && sel_addr == rhs.sel_addr) + return true; + else + return false; + } + + bool operator< (const ClassAndSel &rhs) const + { + if (class_addr < rhs.class_addr) + return true; + else if (class_addr > rhs.class_addr) + return false; + else + { + if (sel_addr < rhs.sel_addr) + return true; + else + return false; + } + } + + lldb::addr_t class_addr; + lldb::addr_t sel_addr; + }; + + typedef std::map<ClassAndSel,lldb::addr_t> MsgImplMap; + MsgImplMap m_impl_cache; + +}; + +}; // using namespace lldb_private + +#endif // lldb_ObjCTrampolineHandler_h_ diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.cpp new file mode 100644 index 00000000000..a0cb0a86f18 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.cpp @@ -0,0 +1,151 @@ +//===-- ThreadPlanStepThroughObjCTrampoline.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 "ThreadPlanStepThroughObjCTrampoline.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Core/Log.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanStepThroughObjCTrampoline constructor +//---------------------------------------------------------------------- +ThreadPlanStepThroughObjCTrampoline::ThreadPlanStepThroughObjCTrampoline( + Thread &thread, + ObjCTrampolineHandler *trampoline_handler, + lldb::addr_t args_addr, + lldb::addr_t object_ptr, + lldb::addr_t class_ptr, + lldb::addr_t sel_ptr, + bool stop_others) : + ThreadPlan ("MacOSX Step through ObjC Trampoline", thread, eVoteNoOpinion, eVoteNoOpinion), + m_objc_trampoline_handler (trampoline_handler), + m_impl_function (trampoline_handler->GetLookupImplementationWrapperFunction()), + m_args_addr (args_addr), + m_object_ptr (object_ptr), + m_class_ptr (class_ptr), + m_sel_ptr (sel_ptr), + m_stop_others (stop_others) +{ + +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ThreadPlanStepThroughObjCTrampoline::~ThreadPlanStepThroughObjCTrampoline() +{ +} + +void +ThreadPlanStepThroughObjCTrampoline::DidPush () +{ + StreamString errors; + ExecutionContext exc_context; + m_thread.Calculate(exc_context); + m_func_sp.reset(m_impl_function->GetThreadPlanToCallFunction (exc_context, m_args_addr, errors, m_stop_others)); + m_func_sp->SetPrivate(true); + m_thread.QueueThreadPlan (m_func_sp, false); +} + +void +ThreadPlanStepThroughObjCTrampoline::GetDescription (Stream *s, + lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + s->Printf("Step through ObjC trampoline"); + else + { + s->Printf ("Stepping to implementation of ObjC method - obj: 0x%llx class: 0x%llx selector: 0x%llx", + m_object_ptr, m_class_ptr, m_sel_ptr); + } +} + +bool +ThreadPlanStepThroughObjCTrampoline::ValidatePlan (Stream *error) +{ + return true; +} + +bool +ThreadPlanStepThroughObjCTrampoline::PlanExplainsStop () +{ + // This plan should actually never stop when it is on the top of the plan + // stack, since it does all it's running in client plans. + return false; +} + +lldb::StateType +ThreadPlanStepThroughObjCTrampoline::RunState () +{ + return eStateRunning; +} + +bool +ThreadPlanStepThroughObjCTrampoline::ShouldStop (Event *event_ptr) +{ + if (m_func_sp.get() == NULL || m_thread.IsThreadPlanDone(m_func_sp.get())) + { + m_func_sp.reset(); + if (!m_run_to_sp) + { + Value target_addr_value; + ExecutionContext exc_context; + m_thread.Calculate(exc_context); + m_impl_function->FetchFunctionResults (exc_context, m_args_addr, target_addr_value); + m_impl_function->DeallocateFunctionResults(exc_context, m_args_addr); + lldb::addr_t target_addr = target_addr_value.GetScalar().ULongLong(); + Address target_address(NULL, target_addr); + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf("Running to ObjC method implementation: 0x%llx", target_addr); + + m_objc_trampoline_handler->AddToCache (m_class_ptr, m_sel_ptr, target_addr); + + // Extract the target address from the value: + + m_run_to_sp.reset(new ThreadPlanRunToAddress(m_thread, target_address, m_stop_others)); + m_thread.QueueThreadPlan(m_run_to_sp, false); + m_run_to_sp->SetPrivate(true); + return false; + } + else if (m_thread.IsThreadPlanDone(m_run_to_sp.get())) + { + SetPlanComplete(); + return true; + } + } + return false; +} + +// The base class MischiefManaged does some cleanup - so you have to call it +// in your MischiefManaged derived class. +bool +ThreadPlanStepThroughObjCTrampoline::MischiefManaged () +{ + if (IsPlanComplete()) + return true; + else + return false; +} + +bool +ThreadPlanStepThroughObjCTrampoline::WillStop() +{ + return true; +} diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.h b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.h new file mode 100644 index 00000000000..8033718277e --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/ThreadPlanStepThroughObjCTrampoline.h @@ -0,0 +1,94 @@ +//===-- ThreadPlanStepThroughObjCTrampoline.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_ThreadPlanStepThroughObjCTrampoline_h_ +#define lldb_ThreadPlanStepThroughObjCTrampoline_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb-types.h" +#include "lldb-enumerations.h" +#include "lldb/Target/ThreadPlan.h" +#include "ObjCTrampolineHandler.h" + +namespace lldb_private +{ + +class ThreadPlanStepThroughObjCTrampoline : public ThreadPlan +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ThreadPlanStepThroughObjCTrampoline(Thread &thread, + ObjCTrampolineHandler *trampoline_handler, + lldb::addr_t args_addr, + lldb::addr_t object_ptr, + lldb::addr_t class_ptr, + lldb::addr_t sel_ptr, + bool stop_others); + + virtual ~ThreadPlanStepThroughObjCTrampoline(); + + virtual void + GetDescription (Stream *s, + lldb::DescriptionLevel level); + + virtual bool + ValidatePlan (Stream *error); + + virtual bool + PlanExplainsStop (); + + + virtual lldb::StateType + RunState (); + + virtual bool + ShouldStop (Event *event_ptr); + + // The base class MischiefManaged does some cleanup - so you have to call it + // in your MischiefManaged derived class. + virtual bool + MischiefManaged (); + + virtual void + DidPush(); + + virtual bool + WillStop(); + + + +protected: + //------------------------------------------------------------------ + // Classes that inherit from ThreadPlanStepThroughObjCTrampoline can see and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For ThreadPlanStepThroughObjCTrampoline only + //------------------------------------------------------------------ + ThreadPlanSP m_func_sp; // This is the function call plan. We fill it at start, then set it + // to NULL when this plan is done. That way we know to go to: + lldb::addr_t m_args_addr; // Stores the address for our step through function result structure. + ThreadPlanSP m_run_to_sp; // The plan that runs to the target. + bool m_stop_others; + ObjCTrampolineHandler *m_objc_trampoline_handler; + ClangFunction *m_impl_function; // This is a pointer to a impl function that + // is owned by the client that pushes this plan. + lldb::addr_t m_object_ptr; + lldb::addr_t m_class_ptr; + lldb::addr_t m_sel_ptr; +}; + +}; // namespace lldb_private +#endif // lldb_ThreadPlanStepThroughObjCTrampoline_h_ diff --git a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp new file mode 100644 index 00000000000..c2fb2a552b3 --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp @@ -0,0 +1,428 @@ +//===-- ObjectContainerBSDArchive.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjectContainerBSDArchive.h" + +#include <ar.h> + +#include "lldb/Core/Stream.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/ObjectFile.h" + +using namespace lldb; +using namespace lldb_private; + + + +ObjectContainerBSDArchive::Object::Object() : + ar_name(), + ar_date(0), + ar_uid(0), + ar_gid(0), + ar_mode(0), + ar_size(0), + ar_file_offset(0), + ar_file_size(0) +{ +} + +void +ObjectContainerBSDArchive::Object::Clear() +{ + ar_name.Clear(); + ar_date = 0; + ar_uid = 0; + ar_gid = 0; + ar_mode = 0; + ar_size = 0; + ar_file_offset = 0; + ar_file_size = 0; +} + +uint32_t +ObjectContainerBSDArchive::Object::Extract (const DataExtractor& data, uint32_t offset) +{ + size_t ar_name_len = 0; + std::string str; + char *err; + str.assign ((const char *)data.GetData(&offset, 16), 16); + if (str.find(AR_EFMT1) == 0) + { + // If the name is longer than 16 bytes, or contains an embedded space + // then it will use this format where the length of the name is + // here and the name characters are after this header. + ar_name_len = strtoul(str.c_str() + 3, &err, 10); + } + else + { + // Strip off any spaces (if the object file name contains spaces it + // will use the extended format above). + str.erase (str.find(' ')); + ar_name.SetCString(str.c_str()); + } + + str.assign ((const char *)data.GetData(&offset, 12), 12); + ar_date = strtoul(str.c_str(), &err, 10); + + str.assign ((const char *)data.GetData(&offset, 6), 6); + ar_uid = strtoul(str.c_str(), &err, 10); + + str.assign ((const char *)data.GetData(&offset, 6), 6); + ar_gid = strtoul(str.c_str(), &err, 10); + + str.assign ((const char *)data.GetData(&offset, 8), 8); + ar_mode = strtoul(str.c_str(), &err, 8); + + str.assign ((const char *)data.GetData(&offset, 10), 10); + ar_size = strtoul(str.c_str(), &err, 10); + + str.assign ((const char *)data.GetData(&offset, 2), 2); + if (str == ARFMAG) + { + if (ar_name_len > 0) + { + str.assign ((const char *)data.GetData(&offset, ar_name_len), ar_name_len); + ar_name.SetCString (str.c_str()); + } + ar_file_offset = offset; + ar_file_size = ar_size - ar_name_len; + return offset; + } + return LLDB_INVALID_INDEX32; +} + +ObjectContainerBSDArchive::Archive::Archive +( + const lldb_private::ArchSpec &arch, + const lldb_private::TimeValue &time +) : + m_arch (arch), + m_time (time), + m_objects() +{ +} + +ObjectContainerBSDArchive::Archive::~Archive () +{ +} + +size_t +ObjectContainerBSDArchive::Archive::ParseObjects (DataExtractor &data) +{ + std::string str; + uint32_t offset = 0; + str.assign((const char *)data.GetData(&offset, SARMAG), SARMAG); + if (str == ARMAG) + { + Object obj; + do + { + offset = obj.Extract (data, offset); + if (offset == LLDB_INVALID_INDEX32) + break; + uint32_t obj_idx = m_objects.size(); + m_objects.push_back(obj); + // Insert all of the C strings out of order for now... + m_object_name_to_index_map.Append (obj.ar_name.GetCString(), obj_idx); + offset += obj.ar_file_size; + obj.Clear(); + } while (data.ValidOffset(offset)); + + // Now sort all of the object name pointers + m_object_name_to_index_map.Sort (); + } + return m_objects.size(); +} + +ObjectContainerBSDArchive::Object * +ObjectContainerBSDArchive::Archive::FindObject (const ConstString &object_name) +{ + const UniqueCStringMap<uint32_t>::Entry *match = m_object_name_to_index_map.FindFirstValueForName (object_name.GetCString()); + if (match) + return &m_objects[match->value]; + return NULL; +} + + +ObjectContainerBSDArchive::Archive::shared_ptr +ObjectContainerBSDArchive::Archive::FindCachedArchive (const FileSpec &file, const ArchSpec &arch, const TimeValue &time) +{ + Mutex::Locker locker(Archive::GetArchiveCacheMutex ()); + shared_ptr archive_sp; + Archive::Map &archive_map = Archive::GetArchiveCache (); + Archive::Map::iterator pos; + for (pos = archive_map.find (file); pos != archive_map.end() && pos->first == file; ++pos) + { + if (pos->second->GetArchitecture() == arch && + pos->second->GetModificationTime() == time) + { + archive_sp = pos->second; + } + } + return archive_sp; +} + +ObjectContainerBSDArchive::Archive::shared_ptr +ObjectContainerBSDArchive::Archive::ParseAndCacheArchiveForFile +( + const FileSpec &file, + const ArchSpec &arch, + const TimeValue &time, + DataExtractor &data +) +{ + shared_ptr archive_sp(new Archive (arch, time)); + if (archive_sp) + { + if (archive_sp->ParseObjects (data) > 0) + { + Mutex::Locker locker(Archive::GetArchiveCacheMutex ()); + Archive::GetArchiveCache().insert(std::make_pair(file, archive_sp)); + } + else + { + archive_sp.reset(); + } + } + return archive_sp; +} + +ObjectContainerBSDArchive::Archive::Map & +ObjectContainerBSDArchive::Archive::GetArchiveCache () +{ + static Archive::Map g_archive_map; + return g_archive_map; +} + +Mutex & +ObjectContainerBSDArchive::Archive::GetArchiveCacheMutex () +{ + static Mutex g_archive_map_mutex (Mutex::eMutexTypeRecursive); + return g_archive_map_mutex; +} + + +void +ObjectContainerBSDArchive::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +ObjectContainerBSDArchive::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +ObjectContainerBSDArchive::GetPluginNameStatic() +{ + return "object-container.bsd-archive"; +} + +const char * +ObjectContainerBSDArchive::GetPluginDescriptionStatic() +{ + return "BSD Archive object container reader."; +} + + +ObjectContainer * +ObjectContainerBSDArchive::CreateInstance +( + Module* module, + DataBufferSP& dataSP, + const FileSpec *file, + addr_t offset, + addr_t length) +{ + if (file) + { + std::string object; + + Archive::shared_ptr archive_sp (Archive::FindCachedArchive (*file, module->GetArchitecture(), module->GetModificationTime())); + + if (archive_sp) + { + // We already have this archive in our cache, use it + std::auto_ptr<ObjectContainerBSDArchive> container_ap(new ObjectContainerBSDArchive (module, dataSP, file, offset, length)); + if (container_ap.get()) + { + container_ap->SetArchive (archive_sp); + return container_ap.release(); + } + } + + if (dataSP) + { + if (ObjectContainerBSDArchive::MagicBytesMatch(dataSP)) + { + // Read everything since we need that in order to index all the + // objects in the archive + dataSP = file->ReadFileContents(offset, length); + + std::auto_ptr<ObjectContainerBSDArchive> container_ap(new ObjectContainerBSDArchive (module, dataSP, file, offset, length)); + if (container_ap->ParseHeader()) + return container_ap.release(); + } + } + } + return NULL; +} + + + +bool +ObjectContainerBSDArchive::MagicBytesMatch (DataBufferSP& dataSP) +{ + DataExtractor data(dataSP, eByteOrderHost, 4); + uint32_t offset = 0; + const char* armag = (const char* )data.PeekData (offset, sizeof(ar_hdr)); + if (armag && ::strncmp(armag, ARMAG, SARMAG) == 0) + { + armag += offsetof(struct ar_hdr, ar_fmag) + SARMAG; + if (strncmp(armag, ARFMAG, 2) == 0) + return true; + } + return false; +} + +ObjectContainerBSDArchive::ObjectContainerBSDArchive +( + Module* module, + DataBufferSP& dataSP, + const lldb_private::FileSpec *file, + lldb::addr_t offset, + lldb::addr_t size +) : + ObjectContainer (module, file, offset, size, dataSP), + m_archive_sp () +{ +} +void +ObjectContainerBSDArchive::SetArchive (Archive::shared_ptr &archive_sp) +{ + m_archive_sp = archive_sp; +} + + + +ObjectContainerBSDArchive::~ObjectContainerBSDArchive() +{ +} + +bool +ObjectContainerBSDArchive::ParseHeader () +{ + if (m_archive_sp.get() == NULL) + { + if (m_data.GetByteSize() > 0) + { + m_archive_sp = Archive::ParseAndCacheArchiveForFile (m_file, + m_module->GetArchitecture(), + m_module->GetModificationTime(), + m_data); + // The archive might be huge, so clear "m_data" to free up the + // memory since it will contain the entire file (possibly more than + // one architecture slice). We already have an index of all objects + // in the file, so we will be ready to serve up those objects. + m_data.Clear(); + } + } + return m_archive_sp.get() != NULL; +} + +void +ObjectContainerBSDArchive::Dump (Stream *s) const +{ + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + const size_t num_archs = GetNumArchitectures(); + const size_t num_objects = GetNumObjects(); + s->Printf("ObjectContainerBSDArchive, num_archs = %u, num_objects = %u", num_archs, num_objects); + uint32_t i; + ArchSpec arch; + s->IndentMore(); + for (i=0; i<num_archs; i++) + { + s->Indent(); + GetArchitectureAtIndex(i, arch); + s->Printf("arch[%u] = %s\n", arch.AsCString()); + } + for (i=0; i<num_objects; i++) + { + s->Indent(); + s->Printf("object[%u] = %s\n", GetObjectNameAtIndex (i)); + } + s->IndentLess(); + s->EOL(); +} + +ObjectFile * +ObjectContainerBSDArchive::GetObjectFile (const FileSpec *file) +{ + if (m_module->GetObjectName() && m_archive_sp) + { + Object *object = m_archive_sp->FindObject (m_module->GetObjectName()); + if (object) + return ObjectFile::FindPlugin (m_module, file, m_offset + object->ar_file_offset, object->ar_file_size); + } + return NULL; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +ObjectContainerBSDArchive::GetPluginName() +{ + return "object-container.bsd-archive"; +} + +const char * +ObjectContainerBSDArchive::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectContainerBSDArchive::GetPluginVersion() +{ + return 1; +} + +void +ObjectContainerBSDArchive::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +ObjectContainerBSDArchive::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +ObjectContainerBSDArchive::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + + diff --git a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h new file mode 100644 index 00000000000..88aba019ead --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h @@ -0,0 +1,183 @@ +//===-- ObjectContainerBSDArchive.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_ObjectContainerBSDArchive_h_ +#define liblldb_ObjectContainerBSDArchive_h_ + +#include "lldb/Symbol/ObjectContainer.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Host/TimeValue.h" + +class ObjectContainerBSDArchive : + public lldb_private::ObjectContainer +{ +public: + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::ObjectContainer * + CreateInstance (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec *file, + lldb::addr_t offset, + lldb::addr_t length); + + static bool + MagicBytesMatch (lldb::DataBufferSP& dataSP); + + //------------------------------------------------------------------ + // Member Functions + //------------------------------------------------------------------ + ObjectContainerBSDArchive (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec *file, + lldb::addr_t offset, + lldb::addr_t length); + + virtual + ~ObjectContainerBSDArchive(); + + virtual bool + ParseHeader (); + + virtual void + Dump (lldb_private::Stream *s) const; + + virtual lldb_private::ObjectFile * + GetObjectFile (const lldb_private::FileSpec *file); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + + +protected: + + struct Object + { + Object(); + + void + Clear(); + + uint32_t + Extract (const lldb_private::DataExtractor& data, uint32_t offset); + + lldb_private::ConstString ar_name; // name + uint32_t ar_date; // modification time + uint16_t ar_uid; // user id + uint16_t ar_gid; // group id + uint16_t ar_mode; // octal file permissions + uint32_t ar_size; // size in bytes + uint32_t ar_file_offset; // file offset in bytes from the beginning of the file of the object data + uint32_t ar_file_size; // length of the object data + + typedef std::vector<Object> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + }; + + class Archive + { + public: + typedef lldb::SharedPtr<Archive>::Type shared_ptr; + typedef std::multimap<lldb_private::FileSpec, shared_ptr> Map; + + static Map & + GetArchiveCache (); + + static lldb_private::Mutex & + GetArchiveCacheMutex (); + + static Archive::shared_ptr + FindCachedArchive (const lldb_private::FileSpec &file, + const lldb_private::ArchSpec &arch, + const lldb_private::TimeValue &mod_time); + + static Archive::shared_ptr + ParseAndCacheArchiveForFile (const lldb_private::FileSpec &file, + const lldb_private::ArchSpec &arch, + const lldb_private::TimeValue &mod_time, + lldb_private::DataExtractor &data); + + Archive (const lldb_private::ArchSpec &arch, + const lldb_private::TimeValue &mod_time); + + ~Archive (); + + size_t + ParseObjects (lldb_private::DataExtractor &data); + + Object * + FindObject (const lldb_private::ConstString &object_name); + + const lldb_private::TimeValue & + GetModificationTime() + { + return m_time; + } + + const lldb_private::ArchSpec & + GetArchitecture () + { + return m_arch; + } + + protected: + + //---------------------------------------------------------------------- + // Member Variables + //---------------------------------------------------------------------- + lldb_private::ArchSpec m_arch; + lldb_private::TimeValue m_time; + Object::collection m_objects; + lldb_private::UniqueCStringMap<uint32_t> m_object_name_to_index_map; + }; + + void + SetArchive (Archive::shared_ptr &archive_sp); + + Archive::shared_ptr m_archive_sp; +}; + +#endif // liblldb_ObjectContainerBSDArchive_h_ diff --git a/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp b/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp new file mode 100644 index 00000000000..015e498a7cc --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.cpp @@ -0,0 +1,259 @@ +//===-- ObjectContainerUniversalMachO.cpp -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjectContainerUniversalMachO.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/ObjectFile.h" + +using namespace lldb; +using namespace lldb_private; + + +void +ObjectContainerUniversalMachO::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +ObjectContainerUniversalMachO::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +ObjectContainerUniversalMachO::GetPluginNameStatic() +{ + return "object-container.mach-o"; +} + +const char * +ObjectContainerUniversalMachO::GetPluginDescriptionStatic() +{ + return "Universal mach-o object container reader."; +} + + +ObjectContainer * +ObjectContainerUniversalMachO::CreateInstance +( + Module* module, + DataBufferSP& dataSP, + const FileSpec *file, + addr_t offset, + addr_t length +) +{ + if (ObjectContainerUniversalMachO::MagicBytesMatch(dataSP)) + { + std::auto_ptr<ObjectContainerUniversalMachO> container_ap(new ObjectContainerUniversalMachO (module, dataSP, file, offset, length)); + if (container_ap->ParseHeader()) + { + return container_ap.release(); + } + } + return NULL; +} + + + +bool +ObjectContainerUniversalMachO::MagicBytesMatch (DataBufferSP& dataSP) +{ + DataExtractor data(dataSP, eByteOrderHost, 4); + uint32_t offset = 0; + uint32_t magic = data.GetU32(&offset); + return magic == FAT_MAGIC || magic == FAT_CIGAM; +} + +ObjectContainerUniversalMachO::ObjectContainerUniversalMachO +( + Module* module, + DataBufferSP& dataSP, + const FileSpec *file, + addr_t offset, + addr_t length +) : + ObjectContainer (module, file, offset, length, dataSP), + m_header(), + m_fat_archs() +{ + memset(&m_header, 0, sizeof(m_header)); +} + + +ObjectContainerUniversalMachO::~ObjectContainerUniversalMachO() +{ +} + +bool +ObjectContainerUniversalMachO::ParseHeader () +{ + // Store the file offset for this universal file as we could have a universal .o file + // in a BSD archive, or be contained in another kind of object. + uint32_t offset = 0; + // Universal mach-o files always have their headers in big endian. + m_data.SetByteOrder (eByteOrderBig); + m_header.magic = m_data.GetU32(&offset); + + if (m_header.magic == FAT_MAGIC) + { + m_data.SetAddressByteSize(4); + + m_header.nfat_arch = m_data.GetU32(&offset); + + const size_t nfat_arch_size = sizeof(fat_arch_t) * m_header.nfat_arch; + // See if the current data we have is enough for all of the fat headers? + if (!m_data.ValidOffsetForDataOfSize(offset, nfat_arch_size)) + { + // The fat headers are larger than the number of bytes we have been + // given when this class was constructed. We will read the exact number + // of bytes that we need. + DataBufferSP data_sp(m_file.ReadFileContents(m_offset, nfat_arch_size + sizeof(fat_header_t))); + m_data.SetData (data_sp); + } + + // Now we should have enough data for all of the fat headers, so lets index + // them so we know how many architectures that this univeral binary contains. + uint32_t arch_idx = 0; + for (arch_idx = 0; arch_idx < m_header.nfat_arch; ++arch_idx) + { + if (m_data.ValidOffsetForDataOfSize(offset, sizeof(fat_arch_t))) + { + fat_arch_t arch; + if (m_data.GetU32(&offset, &arch, sizeof(fat_arch_t)/sizeof(uint32_t))) + { + m_fat_archs.push_back(arch); + } + } + } + // Now that we have indexed the universal headers, we no longer need any cached data. + m_data.Clear(); + + return true; + } + else + { + memset(&m_header, 0, sizeof(m_header)); + } + + return false; +} + +void +ObjectContainerUniversalMachO::Dump (Stream *s) const +{ + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + const size_t num_archs = GetNumArchitectures(); + const size_t num_objects = GetNumObjects(); + s->Printf("ObjectContainerUniversalMachO, num_archs = %u, num_objects = %u", num_archs, num_objects); + uint32_t i; + ArchSpec arch; + s->IndentMore(); + for (i=0; i<num_archs; i++) + { + s->Indent(); + GetArchitectureAtIndex(i, arch); + s->Printf("arch[%u] = %s\n", arch.AsCString()); + } + for (i=0; i<num_objects; i++) + { + s->Indent(); + s->Printf("object[%u] = %s\n", GetObjectNameAtIndex (i)); + } + s->IndentLess(); + s->EOL(); +} + +size_t +ObjectContainerUniversalMachO::GetNumArchitectures () const +{ + return m_header.nfat_arch; +} + +bool +ObjectContainerUniversalMachO::GetArchitectureAtIndex (uint32_t idx, ArchSpec& arch) const +{ + if (idx < m_header.nfat_arch) + { + arch.SetArch(m_fat_archs[idx].cputype, m_fat_archs[idx].cpusubtype); + return true; + } + return false; +} + +ObjectFile * +ObjectContainerUniversalMachO::GetObjectFile (const FileSpec *file) +{ + uint32_t arch_idx = 0; + const ArchSpec arch = m_module->GetArchitecture(); + ArchSpec curr_arch; + for (arch_idx = 0; arch_idx < m_header.nfat_arch; ++arch_idx) + { + if (GetArchitectureAtIndex (arch_idx, curr_arch)) + { + if (arch == curr_arch) + { + return ObjectFile::FindPlugin (m_module, file, m_offset + m_fat_archs[arch_idx].offset, m_fat_archs[arch_idx].size); + } + } + } + return NULL; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +ObjectContainerUniversalMachO::GetPluginName() +{ + return "ObjectContainerUniversalMachO"; +} + +const char * +ObjectContainerUniversalMachO::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectContainerUniversalMachO::GetPluginVersion() +{ + return 1; +} + +void +ObjectContainerUniversalMachO::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +ObjectContainerUniversalMachO::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +ObjectContainerUniversalMachO::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + + diff --git a/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h b/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h new file mode 100644 index 00000000000..8a7f975bc67 --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h @@ -0,0 +1,103 @@ +//===-- ObjectContainerUniversalMachO.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_ObjectContainerUniversalMachO_h_ +#define liblldb_ObjectContainerUniversalMachO_h_ + +#include <mach-o/fat.h> + +#include "lldb/Symbol/ObjectContainer.h" +#include "lldb/Core/FileSpec.h" + +class ObjectContainerUniversalMachO : + public lldb_private::ObjectContainer +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::ObjectContainer * + CreateInstance (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec *file, + lldb::addr_t offset, + lldb::addr_t length); + + static bool + MagicBytesMatch (lldb::DataBufferSP& dataSP); + + //------------------------------------------------------------------ + // Member Functions + //------------------------------------------------------------------ + ObjectContainerUniversalMachO (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec *file, + lldb::addr_t offset, + lldb::addr_t length); + + virtual + ~ObjectContainerUniversalMachO(); + + virtual bool + ParseHeader (); + + virtual void + Dump (lldb_private::Stream *s) const; + + virtual size_t + GetNumArchitectures () const; + + virtual bool + GetArchitectureAtIndex (uint32_t cpu_idx, lldb_private::ArchSpec& arch) const; + + virtual lldb_private::ObjectFile * + GetObjectFile (const lldb_private::FileSpec *file); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + + +protected: + typedef struct fat_header fat_header_t; + typedef struct fat_arch fat_arch_t; + fat_header_t m_header; + std::vector<fat_arch_t> m_fat_archs; +}; + +#endif // liblldb_ObjectContainerUniversalMachO_h_ diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp new file mode 100644 index 00000000000..b34dd3db289 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -0,0 +1,929 @@ +//===-- ObjectFileELF.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjectFileELF.h" + +#include <mach/machine.h> +#include <assert.h> + +#include <algorithm> + +#include <stdint.h> +#include "elf.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/ObjectFile.h" + +#define CASE_AND_STREAM(s, def, width) case def: s->Printf("%-*s", width, #def); break; + +static uint32_t ELFMachineToMachCPU(Elf32_Half machine); + +using namespace lldb; +using namespace lldb_private; +using namespace std; + + +#include <mach-o/nlist.h> +#include <mach-o/stab.h> + + +void +ObjectFileELF::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +ObjectFileELF::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +ObjectFileELF::GetPluginNameStatic() +{ + return "object-file.elf32"; +} + +const char * +ObjectFileELF::GetPluginDescriptionStatic() +{ + return "ELF object file reader (32-bit)."; +} + + +ObjectFile * +ObjectFileELF::CreateInstance (Module* module, DataBufferSP& dataSP, const FileSpec* file, addr_t offset, addr_t length) +{ + if (ObjectFileELF::MagicBytesMatch(dataSP)) + { + std::auto_ptr<ObjectFile> objfile_ap(new ObjectFileELF (module, dataSP, file, offset, length)); + if (objfile_ap.get() && objfile_ap->ParseHeader()) + return objfile_ap.release(); + } + return NULL; +} + +bool +ObjectFileELF::MagicBytesMatch (DataBufferSP& dataSP) +{ + DataExtractor data(dataSP, eByteOrderHost, 4); + const uint8_t* magic = data.PeekData(0, 4); + if (magic != NULL) + { + return magic[EI_MAG0] == 0x7f + && magic[EI_MAG1] == 'E' + && magic[EI_MAG2] == 'L' + && magic[EI_MAG3] == 'F'; + } + return false; +} + + +ObjectFileELF::ObjectFileELF(Module* module, DataBufferSP& dataSP, const FileSpec* file, addr_t offset, addr_t length) : + ObjectFile (module, file, offset, length, dataSP), + m_header(), + m_program_headers(), + m_section_headers(), + m_sections_ap(), + m_symtab_ap(), + m_shstr_data() +{ + if (file) + m_file = *file; + ::bzero (&m_header, sizeof(m_header)); +} + + +ObjectFileELF::~ObjectFileELF() +{ +} + +ByteOrder +ObjectFileELF::GetByteOrder () const +{ + if (m_header.e_ident[EI_DATA] == ELFDATA2MSB) + return eByteOrderBig; + if (m_header.e_ident[EI_DATA] == ELFDATA2LSB) + return eByteOrderLittle; + return eByteOrderInvalid; +} + +size_t +ObjectFileELF::GetAddressByteSize () const +{ + return m_data.GetAddressByteSize(); +} + +bool +ObjectFileELF::ParseHeader () +{ + m_data.SetAddressByteSize(4); + uint32_t offset = GetOffset(); + if (m_data.GetU8(&offset, m_header.e_ident, EI_NIDENT) == NULL) + return false; + + m_data.SetByteOrder(GetByteOrder()); + + // Read e_type and e_machine + if (m_data.GetU16(&offset, &m_header.e_type, 2) == NULL) + return false; + + // read e_version, e_entry, e_phoff, e_shoff, e_flags + if (m_data.GetU32(&offset, &m_header.e_version, 5) == NULL) + return false; + + // Read e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, e_shstrndx + if (m_data.GetU16(&offset, &m_header.e_ehsize, 6) == NULL) + return false; + + return true; +} + +bool +ObjectFileELF::GetUUID (UUID* uuid) +{ + return false; +} + +uint32_t +ObjectFileELF::GetDependentModules(FileSpecList& files) +{ + return 0; +} + +//---------------------------------------------------------------------- +// ParseProgramHeaders +//---------------------------------------------------------------------- +size_t +ObjectFileELF::ParseProgramHeaders() +{ + // We have already parsed the program headers + if (!m_program_headers.empty()) + return m_program_headers.size(); + + uint32_t offset = 0; + if (m_header.e_phnum > 0) + { + m_program_headers.resize(m_header.e_phnum); + + if (m_program_headers.size() != m_header.e_phnum) + return 0; + + const size_t byte_size = m_header.e_phnum * m_header.e_phentsize; + DataBufferSP buffer_sp(m_file.ReadFileContents(m_offset + m_header.e_phoff, byte_size)); + + if (buffer_sp.get() == NULL || buffer_sp->GetByteSize() != byte_size) + return 0; + + DataExtractor data(buffer_sp, m_data.GetByteOrder(), m_data.GetAddressByteSize()); + + uint32_t idx; + for (idx = 0; idx < m_header.e_phnum; ++idx) + { + if (data.GetU32(&offset, &m_program_headers[idx].p_type, 8) == NULL) + return 0; + } + if (idx < m_program_headers.size()) + m_program_headers.resize(idx); + } + + return m_program_headers.size(); +} + + +//---------------------------------------------------------------------- +// ParseSectionHeaders +//---------------------------------------------------------------------- +size_t +ObjectFileELF::ParseSectionHeaders() +{ + // We have already parsed the section headers + if (!m_section_headers.empty()) + return m_section_headers.size(); + + if (m_header.e_shnum > 0) + { + uint32_t offset = 0; + + m_section_headers.resize(m_header.e_shnum); + + if (m_section_headers.size() != m_header.e_shnum) + return 0; + + const size_t byte_size = m_header.e_shnum * m_header.e_shentsize; + DataBufferSP buffer_sp(m_file.ReadFileContents(m_offset + m_header.e_shoff, byte_size)); + + if (buffer_sp.get() == NULL || buffer_sp->GetByteSize() != byte_size) + return 0; + + DataExtractor data(buffer_sp, m_data.GetByteOrder(), m_data.GetAddressByteSize()); + + uint32_t idx; + for (idx = 0; idx < m_header.e_shnum; ++idx) + { + if (data.GetU32(&offset, &m_section_headers[idx].sh_name, 10) == NULL) + break; + } + if (idx < m_section_headers.size()) + m_section_headers.resize(idx); + } + + return m_section_headers.size(); +} + +size_t +ObjectFileELF::GetSectionHeaderStringTable() +{ + if (m_shstr_data.GetByteSize() == 0) + { + if (m_header.e_shstrndx && m_header.e_shstrndx < m_section_headers.size()) + { + const size_t byte_size = m_section_headers[m_header.e_shstrndx].sh_size; + DataBufferSP buffer_sp(m_file.ReadFileContents(m_offset + m_section_headers[m_header.e_shstrndx].sh_offset, byte_size)); + + if (buffer_sp.get() == NULL || buffer_sp->GetByteSize() != byte_size) + return 0; + + m_shstr_data.SetData(buffer_sp); + } + } + return m_shstr_data.GetByteSize(); +} + +uint32_t +ObjectFileELF::GetSectionIndexByName(const char *name) +{ + if (ParseSectionHeaders() && GetSectionHeaderStringTable()) + { + uint32_t offset = 1; // Skip leading NULL string at offset 0; + while (m_shstr_data.ValidOffset(offset)) + { + uint32_t sh_name = offset; // Save offset in case we find a match + const char* sectionHeaderName = m_shstr_data.GetCStr(&offset); + if (sectionHeaderName) + { + if (strcmp(name, sectionHeaderName) == 0) + { + SectionHeaderCollIter pos; + for (pos = m_section_headers.begin(); pos != m_section_headers.end(); ++pos) + { + if ( (*pos).sh_name == sh_name ) + { + // section indexes are 1 based + return std::distance(m_section_headers.begin(), pos) + 1; + } + } + return UINT32_MAX; + } + } + else + { + return UINT32_MAX; + } + } + } + + return UINT32_MAX; +} + +SectionList * +ObjectFileELF::GetSectionList() +{ + if (m_sections_ap.get() == NULL) + { + m_sections_ap.reset(new SectionList()); + if (ParseSectionHeaders() && GetSectionHeaderStringTable()) + { + uint32_t sh_idx = 0; + const size_t num_sections = m_section_headers.size(); + for (sh_idx = 0; sh_idx < num_sections; ++sh_idx) + { + ConstString section_name(m_shstr_data.PeekCStr(m_section_headers[sh_idx].sh_name)); + uint64_t section_file_size = m_section_headers[sh_idx].sh_type == SHT_NOBITS ? 0 : m_section_headers[sh_idx].sh_size; + SectionSP section_sp(new Section(NULL, // Parent section + GetModule(), // Module to which this section belongs + sh_idx + 1, // Section ID is the 1 based + section_name, // Name of this section + eSectionTypeOther, // TODO: fill this in appropriately for ELF... + m_section_headers[sh_idx].sh_addr, // File VM address + m_section_headers[sh_idx].sh_size, // VM size in bytes of this section + m_section_headers[sh_idx].sh_offset, // Offset to the data for this section in the file + section_file_size, // Size in bytes of this section as found in the the file + m_section_headers[sh_idx].sh_flags)); // Flags for this section + if (section_sp.get()) + m_sections_ap->AddSection(section_sp); + + } + } + } + return m_sections_ap.get(); +} + +static void +ParseSymbols (Symtab *symtab, SectionList *section_list, const Elf32_Shdr &symtab_shdr, const DataExtractor& symtab_data, const DataExtractor& strtab_data) +{ + assert (sizeof(Elf32_Sym) == symtab_shdr.sh_entsize); + const uint32_t num_symbols = symtab_data.GetByteSize() / sizeof(Elf32_Sym); + uint32_t offset = 0; + Elf32_Sym symbol; + uint32_t i; + static ConstString text_section_name(".text"); + static ConstString init_section_name(".init"); + static ConstString fini_section_name(".fini"); + static ConstString ctors_section_name(".ctors"); + static ConstString dtors_section_name(".dtors"); + + static ConstString data_section_name(".data"); + static ConstString rodata_section_name(".rodata"); + static ConstString rodata1_section_name(".rodata1"); + static ConstString data2_section_name(".data1"); + static ConstString bss_section_name(".bss"); + + for (i=0; i<num_symbols; ++i) + { + // if (symtab_data.GetU32(&offset, &symbol.st_name, 3) == 0) + // break; + + if (!symtab_data.ValidOffsetForDataOfSize(offset, sizeof(Elf32_Sym))) + break; + + symbol.st_name = symtab_data.GetU32 (&offset); + symbol.st_value = symtab_data.GetU32 (&offset); + symbol.st_size = symtab_data.GetU32 (&offset); + symbol.st_info = symtab_data.GetU8 (&offset); + symbol.st_other = symtab_data.GetU8 (&offset); + symbol.st_shndx = symtab_data.GetU16 (&offset); + + Section * symbol_section = NULL; + SymbolType symbol_type = eSymbolTypeInvalid; + + switch (symbol.st_shndx) + { + case SHN_ABS: + symbol_type = eSymbolTypeAbsolute; + break; + case SHN_UNDEF: + symbol_type = eSymbolTypeUndefined; + break; + default: + symbol_section = section_list->GetSectionAtIndex (symbol.st_shndx).get(); + break; + } + + switch (ELF32_ST_BIND (symbol.st_info)) + { + default: + case STT_NOTYPE: + // The symbol's type is not specified. + break; + + case STT_OBJECT: + // The symbol is associated with a data object, such as a variable, an array, etc. + symbol_type == eSymbolTypeData; + break; + + case STT_FUNC: + // The symbol is associated with a function or other executable code. + symbol_type == eSymbolTypeCode; + break; + + case STT_SECTION: + // The symbol is associated with a section. Symbol table entries of + // this type exist primarily for relocation and normally have + // STB_LOCAL binding. + break; + + case STT_FILE: + // Conventionally, the symbol's name gives the name of the source + // file associated with the object file. A file symbol has STB_LOCAL + // binding, its section index is SHN_ABS, and it precedes the other + // STB_LOCAL symbols for the file, if it is present. + symbol_type == eSymbolTypeObjectFile; + break; + } + + if (symbol_type == eSymbolTypeInvalid) + { + if (symbol_section) + { + const ConstString §_name = symbol_section->GetName(); + if (sect_name == text_section_name || + sect_name == init_section_name || + sect_name == fini_section_name || + sect_name == ctors_section_name || + sect_name == dtors_section_name) + { + symbol_type = eSymbolTypeCode; + } + else + if (sect_name == data_section_name || + sect_name == data2_section_name || + sect_name == rodata_section_name || + sect_name == rodata1_section_name || + sect_name == bss_section_name) + { + symbol_type = eSymbolTypeData; + } + } + } + + uint64_t symbol_value = symbol.st_value; + if (symbol_section != NULL) + symbol_value -= symbol_section->GetFileAddress(); + const char *symbol_name = strtab_data.PeekCStr(symbol.st_name); + + Symbol dc_symbol(i, // ID is the original symbol table index + symbol_name, // symbol name + false, // Is the symbol name mangled? + symbol_type, // type of this symbol + ELF32_ST_BIND (symbol.st_info) == STB_GLOBAL, // Is this globally visible? + false, // Is this symbol debug info? + false, // Is this symbol a trampoline? + false, // Is this symbol artificial? + symbol_section, // section pointer if symbol_value is an offset within a section, else NULL + symbol_value, // offset from section if section is non-NULL, else the value for this symbol + symbol.st_size, // size in bytes of this symbol + symbol.st_other << 8 | symbol.st_info); // symbol flags + symtab->AddSymbol(dc_symbol); + } +} + + +Symtab * +ObjectFileELF::GetSymtab() +{ + if (m_symtab_ap.get() == NULL) + { + m_symtab_ap.reset(new Symtab(this)); + + if (ParseSectionHeaders() && GetSectionHeaderStringTable()) + { + uint32_t symtab_idx = UINT32_MAX; + uint32_t dynsym_idx = UINT32_MAX; + uint32_t sh_idx = 0; + const size_t num_sections = m_section_headers.size(); + for (sh_idx = 0; sh_idx < num_sections; ++sh_idx) + { + if (m_section_headers[sh_idx].sh_type == SHT_SYMTAB) + { + symtab_idx = sh_idx; + break; + } + if (m_section_headers[sh_idx].sh_type == SHT_DYNSYM) + { + dynsym_idx = sh_idx; + } + } + + SectionList *section_list = NULL; + static ConstString g_symtab(".symtab"); + static ConstString g_strtab(".strtab"); + static ConstString g_dynsym(".dynsym"); + static ConstString g_dynstr(".dynstr"); + // Check if we found a full symbol table? + if (symtab_idx < num_sections) + { + section_list = GetSectionList(); + if (section_list) + { + Section *symtab_section = section_list->FindSectionByName(g_symtab).get(); + Section *strtab_section = section_list->FindSectionByName(g_strtab).get(); + if (symtab_section && strtab_section) + { + DataExtractor symtab_data; + DataExtractor strtab_data; + if (symtab_section->ReadSectionDataFromObjectFile (this, symtab_data) > 0 && + strtab_section->ReadSectionDataFromObjectFile (this, strtab_data) > 0) + { + ParseSymbols (m_symtab_ap.get(), section_list, m_section_headers[symtab_idx], symtab_data, strtab_data); + } + } + } + } + // Check if we found a reduced symbol table that gets used for dynamic linking? + else if (dynsym_idx < num_sections) + { + section_list = GetSectionList(); + if (section_list) + { + Section *dynsym_section = section_list->FindSectionByName(g_dynsym).get(); + Section *dynstr_section = section_list->FindSectionByName(g_dynstr).get(); + if (dynsym_section && dynstr_section) + { + DataExtractor dynsym_data; + DataExtractor dynstr_data; + if (dynsym_section->ReadSectionDataFromObjectFile (this, dynsym_data) > 0 && + dynstr_section->ReadSectionDataFromObjectFile (this, dynstr_data) > 0) + { + ParseSymbols (m_symtab_ap.get(), section_list, m_section_headers[dynsym_idx], dynsym_data, dynstr_data); + } + } + } + } + } + } + return m_symtab_ap.get(); +} + +// +////---------------------------------------------------------------------- +//// GetNListSymtab +////---------------------------------------------------------------------- +//bool +//ELF32RuntimeFileParser::GetNListSymtab(BinaryDataRef& stabs_data, BinaryDataRef& stabstr_data, bool locals_only, uint32_t& value_size) +//{ +// value_size = 4; // Size in bytes of the nlist n_value member +// return GetSectionInfo(GetSectionIndexByName(".stab"), NULL, NULL, NULL, NULL, NULL, NULL, &stabs_data, NULL) && +// GetSectionInfo(GetSectionIndexByName(".stabstr"), NULL, NULL, NULL, NULL, NULL, NULL, &stabstr_data, NULL); +//} +// +//===----------------------------------------------------------------------===// +// Dump +// +// Dump the specifics of the runtime file container (such as any headers +// segments, sections, etc). +//---------------------------------------------------------------------- +void +ObjectFileELF::Dump(Stream *s) +{ + DumpELFHeader(s, m_header); + s->EOL(); + DumpELFProgramHeaders(s); + s->EOL(); + DumpELFSectionHeaders(s); + s->EOL(); + SectionList *section_list = GetSectionList(); + if (section_list) + section_list->Dump(s, NULL, true); + Symtab *symtab = GetSymtab(); + if (symtab) + symtab->Dump(s, NULL); + s->EOL(); +} + +//---------------------------------------------------------------------- +// DumpELFHeader +// +// Dump the ELF header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFHeader(Stream *s, const Elf32_Ehdr& header) +{ + + s->PutCString ("ELF Header\n"); + s->Printf ("e_ident[EI_MAG0 ] = 0x%2.2x\n", header.e_ident[EI_MAG0]); + s->Printf ("e_ident[EI_MAG1 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG1], header.e_ident[EI_MAG1]); + s->Printf ("e_ident[EI_MAG2 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG2], header.e_ident[EI_MAG2]); + s->Printf ("e_ident[EI_MAG3 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG3], header.e_ident[EI_MAG3]); + s->Printf ("e_ident[EI_CLASS ] = 0x%2.2x\n", header.e_ident[EI_CLASS]); + s->Printf ("e_ident[EI_DATA ] = 0x%2.2x ", header.e_ident[EI_DATA]); + DumpELFHeader_e_ident_EI_DATA(s, header.e_ident[EI_DATA]); + s->Printf ("\ne_ident[EI_VERSION] = 0x%2.2x\n", header.e_ident[EI_VERSION]); + s->Printf ("e_ident[EI_PAD ] = 0x%2.2x\n", header.e_ident[EI_PAD]); + + s->Printf("e_type = 0x%4.4x ", header.e_type); + DumpELFHeader_e_type(s, header.e_type); + s->Printf("\ne_machine = 0x%4.4x\n", header.e_machine); + s->Printf("e_version = 0x%8.8x\n", header.e_version); + s->Printf("e_entry = 0x%8.8x\n", header.e_entry); + s->Printf("e_phoff = 0x%8.8x\n", header.e_phoff); + s->Printf("e_shoff = 0x%8.8x\n", header.e_shoff); + s->Printf("e_flags = 0x%8.8x\n", header.e_flags); + s->Printf("e_ehsize = 0x%4.4x\n", header.e_ehsize); + s->Printf("e_phentsize = 0x%4.4x\n", header.e_phentsize); + s->Printf("e_phnum = 0x%4.4x\n", header.e_phnum); + s->Printf("e_shentsize = 0x%4.4x\n", header.e_shentsize); + s->Printf("e_shnum = 0x%4.4x\n", header.e_shnum); + s->Printf("e_shstrndx = 0x%4.4x\n", header.e_shstrndx); +} + +//---------------------------------------------------------------------- +// DumpELFHeader_e_type +// +// Dump an token value for the ELF header member e_type +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFHeader_e_type(Stream *s, uint16_t e_type) +{ + switch (e_type) + { + case ET_NONE: *s << "ET_NONE"; break; + case ET_REL: *s << "ET_REL"; break; + case ET_EXEC: *s << "ET_EXEC"; break; + case ET_DYN: *s << "ET_DYN"; break; + case ET_CORE: *s << "ET_CORE"; break; + default: + break; + } +} + +//---------------------------------------------------------------------- +// DumpELFHeader_e_ident_EI_DATA +// +// Dump an token value for the ELF header member e_ident[EI_DATA] +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFHeader_e_ident_EI_DATA(Stream *s, uint16_t ei_data) +{ + switch (ei_data) + { + case ELFDATANONE: *s << "ELFDATANONE"; break; + case ELFDATA2LSB: *s << "ELFDATA2LSB - Little Endian"; break; + case ELFDATA2MSB: *s << "ELFDATA2MSB - Big Endian"; break; + default: + break; + } +} + + +//---------------------------------------------------------------------- +// DumpELFProgramHeader +// +// Dump a single ELF program header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFProgramHeader(Stream *s, const Elf32_Phdr& ph) +{ + DumpELFProgramHeader_p_type(s, ph.p_type); + s->Printf(" %8.8x %8.8x %8.8x %8.8x %8.8x %8.8x (", ph.p_offset, ph.p_vaddr, ph.p_paddr, ph.p_filesz, ph.p_memsz, ph.p_flags); + DumpELFProgramHeader_p_flags(s, ph.p_flags); + s->Printf(") %8.8x", ph.p_align); +} + +//---------------------------------------------------------------------- +// DumpELFProgramHeader_p_type +// +// Dump an token value for the ELF program header member p_type which +// describes the type of the program header +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFProgramHeader_p_type(Stream *s, Elf32_Word p_type) +{ + const int kStrWidth = 10; + switch (p_type) + { + CASE_AND_STREAM(s, PT_NULL , kStrWidth); + CASE_AND_STREAM(s, PT_LOAD , kStrWidth); + CASE_AND_STREAM(s, PT_DYNAMIC , kStrWidth); + CASE_AND_STREAM(s, PT_INTERP , kStrWidth); + CASE_AND_STREAM(s, PT_NOTE , kStrWidth); + CASE_AND_STREAM(s, PT_SHLIB , kStrWidth); + CASE_AND_STREAM(s, PT_PHDR , kStrWidth); + default: + s->Printf("0x%8.8x%*s", p_type, kStrWidth - 10, ""); + break; + } +} + + +//---------------------------------------------------------------------- +// DumpELFProgramHeader_p_flags +// +// Dump an token value for the ELF program header member p_flags +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFProgramHeader_p_flags(Stream *s, Elf32_Word p_flags) +{ + *s << ((p_flags & PF_X) ? "PF_X" : " ") + << (((p_flags & PF_X) && (p_flags & PF_W)) ? '+' : ' ') + << ((p_flags & PF_W) ? "PF_W" : " ") + << (((p_flags & PF_W) && (p_flags & PF_R)) ? '+' : ' ') + << ((p_flags & PF_R) ? "PF_R" : " "); +} + +//---------------------------------------------------------------------- +// DumpELFProgramHeaders +// +// Dump all of the ELF program header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFProgramHeaders(Stream *s) +{ + if (ParseProgramHeaders()) + { + s->PutCString("Program Headers\n"); + s->PutCString("IDX p_type p_offset p_vaddr p_paddr p_filesz p_memsz p_flags p_align\n"); + s->PutCString("==== ---------- -------- -------- -------- -------- -------- ------------------------- --------\n"); + + uint32_t idx = 0; + ProgramHeaderCollConstIter pos; + + for (pos = m_program_headers.begin(); pos != m_program_headers.end(); ++pos, ++idx) + { + s->Printf ("[%2u] ", idx); + ObjectFileELF::DumpELFProgramHeader(s, *pos); + s->EOL(); + } + } +} + + +//---------------------------------------------------------------------- +// DumpELFSectionHeader +// +// Dump a single ELF section header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFSectionHeader(Stream *s, const Elf32_Shdr& sh) +{ + s->Printf ("%8.8x ", sh.sh_name); + DumpELFSectionHeader_sh_type(s, sh.sh_type); + s->Printf (" %8.8x (", sh.sh_flags); + DumpELFSectionHeader_sh_flags(s, sh.sh_flags); + s->Printf (") %8.8x %8.8x %8.8x %8.8x %8.8x %8.8x %8.8x", + sh.sh_addr, sh.sh_offset, sh.sh_size, sh.sh_link, sh.sh_info, sh.sh_addralign, sh.sh_entsize); +} + +//---------------------------------------------------------------------- +// DumpELFSectionHeader_sh_type +// +// Dump an token value for the ELF section header member sh_type which +// describes the type of the section +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFSectionHeader_sh_type(Stream *s, Elf32_Word sh_type) +{ + const int kStrWidth = 12; + switch (sh_type) + { + CASE_AND_STREAM(s, SHT_NULL , kStrWidth); + CASE_AND_STREAM(s, SHT_PROGBITS , kStrWidth); + CASE_AND_STREAM(s, SHT_SYMTAB , kStrWidth); + CASE_AND_STREAM(s, SHT_STRTAB , kStrWidth); + CASE_AND_STREAM(s, SHT_RELA , kStrWidth); + CASE_AND_STREAM(s, SHT_HASH , kStrWidth); + CASE_AND_STREAM(s, SHT_DYNAMIC , kStrWidth); + CASE_AND_STREAM(s, SHT_NOTE , kStrWidth); + CASE_AND_STREAM(s, SHT_NOBITS , kStrWidth); + CASE_AND_STREAM(s, SHT_REL , kStrWidth); + CASE_AND_STREAM(s, SHT_SHLIB , kStrWidth); + CASE_AND_STREAM(s, SHT_DYNSYM , kStrWidth); + CASE_AND_STREAM(s, SHT_LOPROC , kStrWidth); + CASE_AND_STREAM(s, SHT_HIPROC , kStrWidth); + CASE_AND_STREAM(s, SHT_LOUSER , kStrWidth); + CASE_AND_STREAM(s, SHT_HIUSER , kStrWidth); + default: + s->Printf("0x%8.8x%*s", sh_type, kStrWidth - 10, ""); + break; + } +} + + +//---------------------------------------------------------------------- +// DumpELFSectionHeader_sh_flags +// +// Dump an token value for the ELF section header member sh_flags +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFSectionHeader_sh_flags(Stream *s, Elf32_Word sh_flags) +{ + *s << ((sh_flags & SHF_WRITE) ? "WRITE" : " ") + << (((sh_flags & SHF_WRITE) && (sh_flags & SHF_ALLOC)) ? '+' : ' ') + << ((sh_flags & SHF_ALLOC) ? "ALLOC" : " ") + << (((sh_flags & SHF_ALLOC) && (sh_flags & SHF_EXECINSTR)) ? '+' : ' ') + << ((sh_flags & SHF_EXECINSTR) ? "EXECINSTR" : " "); +} +//---------------------------------------------------------------------- +// DumpELFSectionHeaders +// +// Dump all of the ELF section header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFSectionHeaders(Stream *s) +{ + if (ParseSectionHeaders() && GetSectionHeaderStringTable()) + { + s->PutCString("Section Headers\n"); + s->PutCString("IDX name type flags addr offset size link info addralgn entsize Name\n"); + s->PutCString("==== -------- ------------ -------------------------------- -------- -------- -------- -------- -------- -------- -------- ====================\n"); + + uint32_t idx = 0; + SectionHeaderCollConstIter pos; + + for (pos = m_section_headers.begin(); pos != m_section_headers.end(); ++pos, ++idx) + { + s->Printf ("[%2u] ", idx); + ObjectFileELF::DumpELFSectionHeader(s, *pos); + const char* section_name = m_shstr_data.PeekCStr(pos->sh_name); + if (section_name) + *s << ' ' << section_name << "\n"; + } + } +} + +static uint32_t +ELFMachineToMachCPU(Elf32_Half machine) +{ + switch (machine) + { + case EM_SPARC: return CPU_TYPE_SPARC; + case EM_386: return CPU_TYPE_I386; + case EM_68K: return CPU_TYPE_MC680x0; + case EM_88K: return CPU_TYPE_MC88000; + case EM_860: return CPU_TYPE_I860; + case EM_MIPS: return 8; // commented out in mach/machine.h + case EM_PPC: return CPU_TYPE_POWERPC; + case EM_PPC64: return CPU_TYPE_POWERPC64; + case EM_ARM: return 12; // commented out in mach/machine.h + } + return 0; +} + +bool +ObjectFileELF::GetTargetTriple (ConstString &target_triple) +{ + static ConstString g_target_triple; + + if (g_target_triple) + { + target_triple = g_target_triple; + } + else + { + std::string triple; + switch (m_header.e_machine) + { + case EM_SPARC: triple.assign("sparc-"); break; + case EM_386: triple.assign("i386-"); break; + case EM_68K: triple.assign("68k-"); break; + case EM_88K: triple.assign("88k-"); break; + case EM_860: triple.assign("i860-"); break; + case EM_MIPS: triple.assign("mips-"); break; + case EM_PPC: triple.assign("powerpc-"); break; + case EM_PPC64: triple.assign("powerpc64-"); break; + case EM_ARM: triple.assign("arm-"); break; + } + // TODO: determine if there is a vendor in the ELF? Default to "apple" for now + triple += "apple-"; + // TODO: determine if there is an OS in the ELF? Default to "darwin" for now + triple += "darwin10"; + g_target_triple.SetCString(triple.c_str()); + target_triple = g_target_triple; + } + return !target_triple.IsEmpty(); +} + + +//bool +//ELF32RuntimeFileParser::GetArch(ArchSpec &arch) const +//{ +// arch.SetCPUType(ELFMachineToMachCPU(m_header.e_machine)); +// arch.SetCPUSubtype(ArchSpec::eAny); +// return true; +//} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +ObjectFileELF::GetPluginName() +{ + return "ObjectFileELF"; +} + +const char * +ObjectFileELF::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectFileELF::GetPluginVersion() +{ + return 1; +} + +void +ObjectFileELF::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +ObjectFileELF::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +ObjectFileELF::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + + diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h new file mode 100644 index 00000000000..5d5778c78fb --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h @@ -0,0 +1,197 @@ +//===-- ObjectFileELF.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_ObjectFileELF_h_ +#define liblldb_ObjectFileELF_h_ + +#include <stdint.h> +#include <vector> + +#include "lldb/lldb-private.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Symbol/ObjectFile.h" + +#include "elf.h" + +//---------------------------------------------------------------------- +// This class needs to be hidden as eventually belongs in a plugin that +// will export the ObjectFile protocol +//---------------------------------------------------------------------- +class ObjectFileELF : + public lldb_private::ObjectFile +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::ObjectFile * + CreateInstance (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec* file, + lldb::addr_t offset, + lldb::addr_t length); + static bool + MagicBytesMatch (lldb::DataBufferSP& dataSP); + + //------------------------------------------------------------------ + // Member Functions + //------------------------------------------------------------------ + ObjectFileELF (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec* file, + lldb::addr_t offset, + lldb::addr_t length); + + virtual + ~ObjectFileELF(); + + virtual bool + ParseHeader (); + + virtual lldb::ByteOrder + GetByteOrder () const; + + virtual size_t + GetAddressByteSize () const; + + virtual lldb_private::Symtab * + GetSymtab(); + + virtual lldb_private::SectionList * + GetSectionList(); + + virtual void + Dump (lldb_private::Stream *s); + + virtual bool + GetTargetTriple (lldb_private::ConstString &target_triple); + + virtual bool + GetUUID (lldb_private::UUID* uuid); + + virtual uint32_t + GetDependentModules(lldb_private::FileSpecList& files); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + + + +protected: + typedef std::vector<Elf32_Phdr> ProgramHeaderColl; + typedef ProgramHeaderColl::iterator ProgramHeaderCollIter; + typedef ProgramHeaderColl::const_iterator ProgramHeaderCollConstIter; + + typedef std::vector<Elf32_Shdr> SectionHeaderColl; + typedef SectionHeaderColl::iterator SectionHeaderCollIter; + typedef SectionHeaderColl::const_iterator SectionHeaderCollConstIter; + + Elf32_Ehdr m_header; + ProgramHeaderColl m_program_headers; + SectionHeaderColl m_section_headers; + mutable std::auto_ptr<lldb_private::SectionList> m_sections_ap; + mutable std::auto_ptr<lldb_private::Symtab> m_symtab_ap; + lldb_private::DataExtractor m_shstr_data; + + size_t + ParseSections (); + + size_t + ParseSymtab (bool minimize); + +private: + + // ELF header dump routines + static void + DumpELFHeader (lldb_private::Stream *s, + const Elf32_Ehdr& header); + + static void + DumpELFHeader_e_ident_EI_DATA (lldb_private::Stream *s, + uint16_t ei_data); + static void + DumpELFHeader_e_type (lldb_private::Stream *s, + uint16_t e_type); + + // ELF program header dump routines + void + DumpELFProgramHeaders (lldb_private::Stream *s); + + static void + DumpELFProgramHeader (lldb_private::Stream *s, + const Elf32_Phdr& ph); + + static void + DumpELFProgramHeader_p_type (lldb_private::Stream *s, + Elf32_Word p_type); + + static void + DumpELFProgramHeader_p_flags (lldb_private::Stream *s, + Elf32_Word p_flags); + + // ELF section header dump routines + void + DumpELFSectionHeaders (lldb_private::Stream *s); + + static void + DumpELFSectionHeader (lldb_private::Stream *s, + const Elf32_Shdr& sh); + + static void + DumpELFSectionHeader_sh_type (lldb_private::Stream *s, + Elf32_Word sh_type); + + static void + DumpELFSectionHeader_sh_flags (lldb_private::Stream *s, + Elf32_Word sh_flags); + + size_t + ParseProgramHeaders (); + + size_t + ParseSectionHeaders (); + + size_t + GetSectionHeaderStringTable (); + + uint32_t + GetSectionIndexByName (const char *name); +}; + +#endif // #ifndef liblldb_ObjectFileELF_h_ diff --git a/lldb/source/Plugins/ObjectFile/ELF/elf.h b/lldb/source/Plugins/ObjectFile/ELF/elf.h new file mode 100644 index 00000000000..9d08119c597 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/ELF/elf.h @@ -0,0 +1,240 @@ +//===-- elf.h ---------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __elf_h__ +#define __elf_h__ + +typedef uint16_t Elf32_Half; +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf32_Addr; +typedef uint32_t Elf32_Off; + + +#define EI_NIDENT 16 + +//---------------------------------------------------------------------- +// ELF Header +//---------------------------------------------------------------------- +typedef struct Elf32_Ehdr_Tag +{ + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +//---------------------------------------------------------------------- +// e_type +// +// This member identifies the object file type. +//---------------------------------------------------------------------- +#define ET_NONE 0 // No file type +#define ET_REL 1 // Relocatable file +#define ET_EXEC 2 // Executable file +#define ET_DYN 3 // Shared object file +#define ET_CORE 4 // Core file +#define ET_LOPROC 0xff00 // Processor-specific +#define ET_HIPROC 0xffff // Processor-specific + +//---------------------------------------------------------------------- +// e_machine +// +// Machine Type +//---------------------------------------------------------------------- +#define EM_NONE 0 // No machine +#define EM_M32 1 // AT&T WE 32100 +#define EM_SPARC 2 // SPARC +#define EM_386 3 // Intel 80386 +#define EM_68K 4 // Motorola 68000 +#define EM_88K 5 // Motorola 88000 +#define EM_860 7 // Intel 80860 +#define EM_MIPS 8 // MIPS RS3000 +#define EM_PPC 20 // PowerPC +#define EM_PPC64 21 // PowerPC64 +#define EM_ARM 40 // ARM + + +//---------------------------------------------------------------------- +// e_ident indexes +//---------------------------------------------------------------------- +#define EI_MAG0 0 // File identification +#define EI_MAG1 1 // File identification +#define EI_MAG2 2 // File identification +#define EI_MAG3 3 // File identification +#define EI_CLASS 4 // File class +#define EI_DATA 5 // Data encoding +#define EI_VERSION 6 // File version +#define EI_PAD 7 // Start of padding bytes + + +//---------------------------------------------------------------------- +// EI_DATA definitions +//---------------------------------------------------------------------- +#define ELFDATANONE 0 // Invalid data encoding +#define ELFDATA2LSB 1 // Little Endian +#define ELFDATA2MSB 2 // Big Endian + +//---------------------------------------------------------------------- +// Section Header +//---------------------------------------------------------------------- +typedef struct Elf32_Shdr_Tag +{ + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +//---------------------------------------------------------------------- +// Section Types (sh_type) +//---------------------------------------------------------------------- +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + +//---------------------------------------------------------------------- +// Special Section Indexes +//---------------------------------------------------------------------- +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_HIRESERVE 0xffff + +//---------------------------------------------------------------------- +// Section Attribute Flags (sh_flags) +//---------------------------------------------------------------------- +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 +#define SHF_MASKPROC 0xf0000000 + + +//---------------------------------------------------------------------- +// Symbol Table Entry Header +//---------------------------------------------------------------------- +typedef struct Elf32_Sym_Tag +{ + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + + +#define ELF32_ST_BIND(i) ((i)>>4) +#define ELF32_ST_TYPE(i) ((i)&0xf) +#define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf)) + +// ST_BIND +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 +#define STB_LOPROC 13 +#define STB_HIPROC 15 + +// ST_TYPE +#define STT_NOTYPE 0 +#define STT_OBJECT 1 +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_LOPROC 13 +#define STT_HIPROC 15 + + +//---------------------------------------------------------------------- +// Relocation Entries +//---------------------------------------------------------------------- +typedef struct Elf32_Rel_Tag +{ + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct Elf32_Rela_Tag +{ + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +#define ELF32_R_SYM(i) ((i)>>8) +#define ELF32_R_TYPE(i) ((unsignedchar)(i)) +#define ELF32_R_INFO(s,t) (((s)<<8)+(unsignedchar)(t)) + + +//---------------------------------------------------------------------- +// Program Headers +//---------------------------------------------------------------------- +typedef struct Elf32_Phdr_Tag +{ + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +//---------------------------------------------------------------------- +// Program Header Type (p_type) +//---------------------------------------------------------------------- +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff + +#define PF_X (1 << 0) // executable +#define PF_W (1 << 1) // writable +#define PF_R (1 << 2) // readable + + +#endif // __elf_h__ diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp new file mode 100644 index 00000000000..682a116d213 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -0,0 +1,1311 @@ +//===-- ObjectFileMachO.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjectFileMachO.h" + +#include <mach-o/nlist.h> +#include <mach-o/stab.h> + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/UUID.h" +#include "lldb/Symbol/ObjectFile.h" + +#ifndef S_DTRACE_DOF +// section contains DTrace Object Format +#define S_DTRACE_DOF 0xf +#endif + +#ifndef S_LAZY_DYLIB_SYMBOL_POINTERS +// section with only lazy symbol pointers to lazy loaded dylibs +#define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10 +#endif + +using namespace lldb; +using namespace lldb_private; + + +void +ObjectFileMachO::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +ObjectFileMachO::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +ObjectFileMachO::GetPluginNameStatic() +{ + return "object-file.mach-o"; +} + +const char * +ObjectFileMachO::GetPluginDescriptionStatic() +{ + return "Mach-o object file reader (32 and 64 bit)"; +} + + +ObjectFile * +ObjectFileMachO::CreateInstance (Module* module, DataBufferSP& dataSP, const FileSpec* file, addr_t offset, addr_t length) +{ + if (ObjectFileMachO::MagicBytesMatch(dataSP)) + { + std::auto_ptr<ObjectFile> objfile_ap(new ObjectFileMachO (module, dataSP, file, offset, length)); + if (objfile_ap.get() && objfile_ap->ParseHeader()) + return objfile_ap.release(); + } + return NULL; +} + + +static uint32_t +MachHeaderSizeFromMagic(uint32_t magic) +{ + switch (magic) + { + case MH_MAGIC: + case MH_CIGAM: + return sizeof(struct mach_header); + + case MH_MAGIC_64: + case MH_CIGAM_64: + return sizeof(struct mach_header_64); + break; + + default: + break; + } + return 0; +} + + +bool +ObjectFileMachO::MagicBytesMatch (DataBufferSP& dataSP) +{ + DataExtractor data(dataSP, eByteOrderHost, 4); + uint32_t offset = 0; + uint32_t magic = data.GetU32(&offset); + return MachHeaderSizeFromMagic(magic) != 0; +} + + +ObjectFileMachO::ObjectFileMachO(Module* module, DataBufferSP& dataSP, const FileSpec* file, addr_t offset, addr_t length) : + ObjectFile(module, file, offset, length, dataSP), + m_mutex (Mutex::eMutexTypeRecursive), + m_header(), + m_sections_ap(), + m_symtab_ap() +{ + ::bzero (&m_header, sizeof(m_header)); + ::bzero (&m_dysymtab, sizeof(m_dysymtab)); +} + + +ObjectFileMachO::~ObjectFileMachO() +{ +} + + +bool +ObjectFileMachO::ParseHeader () +{ + lldb_private::Mutex::Locker locker(m_mutex); + bool can_parse = false; + uint32_t offset = 0; + m_data.SetByteOrder (eByteOrderHost); + // Leave magic in the original byte order + m_header.magic = m_data.GetU32(&offset); + switch (m_header.magic) + { + case MH_MAGIC: + m_data.SetByteOrder (eByteOrderHost); + m_data.SetAddressByteSize(4); + can_parse = true; + break; + + case MH_MAGIC_64: + m_data.SetByteOrder (eByteOrderHost); + m_data.SetAddressByteSize(8); + can_parse = true; + break; + + case MH_CIGAM: + m_data.SetByteOrder(eByteOrderHost == eByteOrderBig ? eByteOrderLittle : eByteOrderBig); + m_data.SetAddressByteSize(4); + can_parse = true; + break; + + case MH_CIGAM_64: + m_data.SetByteOrder(eByteOrderHost == eByteOrderBig ? eByteOrderLittle : eByteOrderBig); + m_data.SetAddressByteSize(8); + can_parse = true; + break; + + default: + break; + } + + if (can_parse) + { + m_data.GetU32(&offset, &m_header.cputype, 6); + + ArchSpec mach_arch(m_header.cputype, m_header.cpusubtype); + if (mach_arch == m_module->GetArchitecture()) + { + // Read in all only the load command data + DataBufferSP data_sp(m_file.ReadFileContents(m_offset, m_header.sizeofcmds + MachHeaderSizeFromMagic(m_header.magic))); + m_data.SetData (data_sp); + return true; + } + } + else + { + memset(&m_header, 0, sizeof(struct mach_header)); + } + return false; +} + + +ByteOrder +ObjectFileMachO::GetByteOrder () const +{ + lldb_private::Mutex::Locker locker(m_mutex); + return m_data.GetByteOrder (); +} + + +size_t +ObjectFileMachO::GetAddressByteSize () const +{ + lldb_private::Mutex::Locker locker(m_mutex); + return m_data.GetAddressByteSize (); +} + + +Symtab * +ObjectFileMachO::GetSymtab() +{ + lldb_private::Mutex::Locker locker(m_mutex); + if (m_symtab_ap.get() == NULL) + { + m_symtab_ap.reset(new Symtab(this)); + ParseSymtab(false); + } + return m_symtab_ap.get(); +} + + +SectionList * +ObjectFileMachO::GetSectionList() +{ + lldb_private::Mutex::Locker locker(m_mutex); + if (m_sections_ap.get() == NULL) + { + m_sections_ap.reset(new SectionList()); + ParseSections(); + } + return m_sections_ap.get(); +} + + +size_t +ObjectFileMachO::ParseSections () +{ + lldb::user_id_t segID = 0; + lldb::user_id_t sectID = 0; + struct segment_command_64 load_cmd; + uint32_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t i; + //bool dump_sections = false; + for (i=0; i<m_header.ncmds; ++i) + { + const uint32_t load_cmd_offset = offset; + if (m_data.GetU32(&offset, &load_cmd, 2) == NULL) + break; + + if (load_cmd.cmd == LC_SEGMENT || load_cmd.cmd == LC_SEGMENT_64) + { + if (m_data.GetU8(&offset, (uint8_t*)load_cmd.segname, 16)) + { + load_cmd.vmaddr = m_data.GetAddress(&offset); + load_cmd.vmsize = m_data.GetAddress(&offset); + load_cmd.fileoff = m_data.GetAddress(&offset); + load_cmd.filesize = m_data.GetAddress(&offset); + if (m_data.GetU32(&offset, &load_cmd.maxprot, 4)) + { + // Keep a list of mach segments around in case we need to + // get at data that isn't stored in the abstracted Sections. + m_mach_segments.push_back (load_cmd); + + ConstString segment_name (load_cmd.segname, std::min<int>(strlen(load_cmd.segname), sizeof(load_cmd.segname))); + // Use a segment ID of the segment index shifted left by 8 so they + // never conflict with any of the sections. + SectionSP segment_sp; + if (segment_name) + { + segment_sp.reset(new Section (NULL, + GetModule(), // Module to which this section belongs + ++segID << 8, // Section ID is the 1 based segment index shifted right by 8 bits as not to collide with any of the 256 section IDs that are possible + segment_name, // Name of this section + eSectionTypeContainer, // This section is a container of other sections. + load_cmd.vmaddr, // File VM address == addresses as they are found in the object file + load_cmd.vmsize, // VM size in bytes of this section + load_cmd.fileoff, // Offset to the data for this section in the file + load_cmd.filesize, // Size in bytes of this section as found in the the file + load_cmd.flags)); // Flags for this section + + m_sections_ap->AddSection(segment_sp); + } + + struct section_64 sect64; + ::bzero (§64, sizeof(sect64)); + // Push a section into our mach sections for the section at + // index zero (NO_SECT) + m_mach_sections.push_back(sect64); + uint32_t segment_sect_idx; + const lldb::user_id_t first_segment_sectID = sectID + 1; + + + const uint32_t num_u32s = load_cmd.cmd == LC_SEGMENT ? 7 : 8; + for (segment_sect_idx=0; segment_sect_idx<load_cmd.nsects; ++segment_sect_idx) + { + if (m_data.GetU8(&offset, (uint8_t*)sect64.sectname, sizeof(sect64.sectname)) == NULL) + break; + if (m_data.GetU8(&offset, (uint8_t*)sect64.segname, sizeof(sect64.segname)) == NULL) + break; + sect64.addr = m_data.GetAddress(&offset); + sect64.size = m_data.GetAddress(&offset); + + if (m_data.GetU32(&offset, §64.offset, num_u32s) == NULL) + break; + + // Keep a list of mach sections around in case we need to + // get at data that isn't stored in the abstracted Sections. + m_mach_sections.push_back (sect64); + + ConstString section_name (sect64.sectname, std::min<size_t>(strlen(sect64.sectname), sizeof(sect64.sectname))); + if (!segment_name) + { + // We have a segment with no name so we need to conjure up + // segments that correspond to the section's segname if there + // isn't already such a section. If there is such a section, + // we resize the section so that it spans all sections. + // We also mark these sections as fake so address matches don't + // hit if they land in the gaps between the child sections. + segment_name.SetTrimmedCStringWithLength(sect64.segname, sizeof(sect64.segname)); + segment_sp = m_sections_ap->FindSectionByName (segment_name); + if (segment_sp.get()) + { + Section *segment = segment_sp.get(); + // Grow the section size as needed. + const lldb::addr_t sect64_min_addr = sect64.addr; + const lldb::addr_t sect64_max_addr = sect64_min_addr + sect64.size; + const lldb::addr_t curr_seg_byte_size = segment->GetByteSize(); + const lldb::addr_t curr_seg_min_addr = segment->GetFileAddress(); + const lldb::addr_t curr_seg_max_addr = curr_seg_min_addr + curr_seg_byte_size; + if (sect64_min_addr >= curr_seg_min_addr) + { + const lldb::addr_t new_seg_byte_size = sect64_max_addr - curr_seg_min_addr; + // Only grow the section size if needed + if (new_seg_byte_size > curr_seg_byte_size) + segment->SetByteSize (new_seg_byte_size); + } + else + { + // We need to change the base address of the segment and + // adjust the child section offsets for all existing children. + const lldb::addr_t slide_amount = sect64_min_addr - curr_seg_min_addr; + segment->Slide(slide_amount, false); + segment->GetChildren().Slide (-slide_amount, false); + segment->SetByteSize (curr_seg_max_addr - sect64_min_addr); + } + } + else + { + // Create a fake section for the section's named segment + segment_sp.reset(new Section(segment_sp.get(), // Parent section + GetModule(), // Module to which this section belongs + ++segID << 8, // Section ID is the 1 based segment index shifted right by 8 bits as not to collide with any of the 256 section IDs that are possible + segment_name, // Name of this section + eSectionTypeContainer, // This section is a container of other sections. + sect64.addr, // File VM address == addresses as they are found in the object file + sect64.size, // VM size in bytes of this section + sect64.offset, // Offset to the data for this section in the file + sect64.offset ? sect64.size : 0, // Size in bytes of this section as found in the the file + load_cmd.flags)); // Flags for this section + segment_sp->SetIsFake(true); + m_sections_ap->AddSection(segment_sp); + } + } + assert (segment_sp.get()); + + uint32_t mach_sect_type = sect64.flags & SECTION_TYPE; + static ConstString g_sect_name_objc_data ("__objc_data"); + static ConstString g_sect_name_objc_msgrefs ("__objc_msgrefs"); + static ConstString g_sect_name_objc_selrefs ("__objc_selrefs"); + static ConstString g_sect_name_objc_classrefs ("__objc_classrefs"); + static ConstString g_sect_name_objc_superrefs ("__objc_superrefs"); + static ConstString g_sect_name_objc_const ("__objc_const"); + static ConstString g_sect_name_objc_classlist ("__objc_classlist"); + static ConstString g_sect_name_cfstring ("__cfstring"); + SectionType sect_type = eSectionTypeOther; + + if (section_name == g_sect_name_objc_selrefs) + { + sect_type = eSectionTypeDataCStringPointers; + } + else if (section_name == g_sect_name_objc_msgrefs) + { + sect_type = eSectionTypeDataObjCMessageRefs; + } + else if (section_name == g_sect_name_objc_data || + section_name == g_sect_name_objc_classrefs || + section_name == g_sect_name_objc_superrefs || + section_name == g_sect_name_objc_const || + section_name == g_sect_name_objc_classlist) + { + sect_type = eSectionTypeDataPointers; + } + else if (section_name == g_sect_name_cfstring) + { + sect_type = eSectionTypeDataObjCCFStrings; + } + + if (sect_type == eSectionTypeOther) + { + switch (mach_sect_type) + { + // TODO: categorize sections by other flags for regular sections + case S_REGULAR: + + sect_type = eSectionTypeOther; + break; + case S_ZEROFILL: sect_type = eSectionTypeZeroFill; break; + case S_CSTRING_LITERALS: sect_type = eSectionTypeDataCString; break; // section with only literal C strings + case S_4BYTE_LITERALS: sect_type = eSectionTypeData4; break; // section with only 4 byte literals + case S_8BYTE_LITERALS: sect_type = eSectionTypeData8; break; // section with only 8 byte literals + case S_LITERAL_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only pointers to literals + case S_NON_LAZY_SYMBOL_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only non-lazy symbol pointers + case S_LAZY_SYMBOL_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only lazy symbol pointers + case S_SYMBOL_STUBS: sect_type = eSectionTypeCode; break; // section with only symbol stubs, byte size of stub in the reserved2 field + case S_MOD_INIT_FUNC_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only function pointers for initialization + case S_MOD_TERM_FUNC_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only function pointers for termination + case S_COALESCED: sect_type = eSectionTypeOther; break; + case S_GB_ZEROFILL: sect_type = eSectionTypeZeroFill; break; + case S_INTERPOSING: sect_type = eSectionTypeCode; break; // section with only pairs of function pointers for interposing + case S_16BYTE_LITERALS: sect_type = eSectionTypeData16; break; // section with only 16 byte literals + case S_DTRACE_DOF: sect_type = eSectionTypeDebug; break; + case S_LAZY_DYLIB_SYMBOL_POINTERS: sect_type = eSectionTypeDataPointers; break; + default: break; + } + } + + SectionSP section_sp(new Section(segment_sp.get(), + GetModule(), + ++sectID, + section_name, + sect_type, + sect64.addr - segment_sp->GetFileAddress(), + sect64.size, + sect64.offset, + sect64.offset == 0 ? 0 : sect64.size, + sect64.flags)); + segment_sp->GetChildren().AddSection(section_sp); + + if (segment_sp->IsFake()) + { + segment_sp.reset(); + segment_name.Clear(); + } + } + if (m_header.filetype == MH_DSYM) + { + if (first_segment_sectID <= sectID) + { + lldb::user_id_t sect_uid; + for (sect_uid = first_segment_sectID; sect_uid <= sectID; ++sect_uid) + { + SectionSP curr_section_sp(segment_sp->GetChildren().FindSectionByID (sect_uid)); + SectionSP next_section_sp; + if (sect_uid + 1 <= sectID) + next_section_sp = segment_sp->GetChildren().FindSectionByID (sect_uid+1); + + if (curr_section_sp.get()) + { + if (curr_section_sp->GetByteSize() == 0) + { + if (next_section_sp.get() != NULL) + curr_section_sp->SetByteSize ( next_section_sp->GetFileAddress() - curr_section_sp->GetFileAddress() ); + else + curr_section_sp->SetByteSize ( load_cmd.vmsize ); + } + } + } + } + } + } + } + } + else if (load_cmd.cmd == LC_DYSYMTAB) + { + m_dysymtab.cmd = load_cmd.cmd; + m_dysymtab.cmdsize = load_cmd.cmdsize; + m_data.GetU32 (&offset, &m_dysymtab.ilocalsym, (sizeof(m_dysymtab) / sizeof(uint32_t)) - 2); + } + + offset = load_cmd_offset + load_cmd.cmdsize; + } +// if (dump_sections) +// { +// StreamFile s(stdout); +// m_sections_ap->Dump(&s, true); +// } + return sectID; // Return the number of sections we registered with the module +} + +class MachSymtabSectionInfo +{ +public: + + MachSymtabSectionInfo (SectionList *section_list) : + m_section_list (section_list), + m_section_infos() + { + // Get the number of sections down to a depth of 1 to include + // all segments and their sections, but no other sections that + // may be added for debug map or + m_section_infos.resize(section_list->GetNumSections(1)); + } + + + Section * + GetSection (uint8_t n_sect, addr_t file_addr) + { + if (n_sect == 0) + return NULL; + if (n_sect < m_section_infos.size()) + { + if (m_section_infos[n_sect].section == NULL) + { + Section *section = m_section_list->FindSectionByID (n_sect).get(); + m_section_infos[n_sect].section = section; + assert (section != NULL); + m_section_infos[n_sect].vm_range.SetBaseAddress (section->GetFileAddress()); + m_section_infos[n_sect].vm_range.SetByteSize (section->GetByteSize()); + } + if (m_section_infos[n_sect].vm_range.Contains(file_addr)) + return m_section_infos[n_sect].section; + } + return m_section_list->FindSectionContainingFileAddress(file_addr).get(); + } + +protected: + struct SectionInfo + { + SectionInfo () : + vm_range(), + section (NULL) + { + } + + VMRange vm_range; + Section *section; + }; + SectionList *m_section_list; + std::vector<SectionInfo> m_section_infos; +}; + + + +size_t +ObjectFileMachO::ParseSymtab (bool minimize) +{ + Timer scoped_timer(__PRETTY_FUNCTION__, + "ObjectFileMachO::ParseSymtab () module = %s", + m_file.GetFilename().AsCString("")); + struct symtab_command symtab_load_command; + uint32_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t i; + for (i=0; i<m_header.ncmds; ++i) + { + const uint32_t cmd_offset = offset; + // Read in the load command and load command size + if (m_data.GetU32(&offset, &symtab_load_command, 2) == NULL) + break; + // Watch for the symbol table load command + if (symtab_load_command.cmd == LC_SYMTAB) + { + // Read in the rest of the symtab load command + if (m_data.GetU32(&offset, &symtab_load_command.symoff, 4)) + { + Symtab *symtab = m_symtab_ap.get(); + SectionList *section_list = GetSectionList(); + assert(section_list); + const size_t addr_size = m_data.GetAddressByteSize(); + const ByteOrder endian = m_data.GetByteOrder(); + bool bit_width_32 = addr_size == 4; + const size_t nlist_size = bit_width_32 ? sizeof(struct nlist) : sizeof(struct nlist_64); + + DataBufferSP symtab_data_sp(m_file.ReadFileContents(m_offset + symtab_load_command.symoff, symtab_load_command.nsyms * nlist_size)); + DataBufferSP strtab_data_sp(m_file.ReadFileContents(m_offset + symtab_load_command.stroff, symtab_load_command.strsize)); + + const char *strtab_data = (const char *)strtab_data_sp->GetBytes(); +// DataExtractor symtab_data(symtab_data_sp, endian, addr_size); +// DataExtractor strtab_data(strtab_data_sp, endian, addr_size); + + static ConstString g_segment_name_TEXT ("__TEXT"); + static ConstString g_segment_name_DATA ("__DATA"); + static ConstString g_segment_name_OBJC ("__OBJC"); + static ConstString g_section_name_eh_frame ("__eh_frame"); + SectionSP text_section_sp(section_list->FindSectionByName(g_segment_name_TEXT)); + SectionSP data_section_sp(section_list->FindSectionByName(g_segment_name_DATA)); + SectionSP objc_section_sp(section_list->FindSectionByName(g_segment_name_OBJC)); + SectionSP eh_frame_section_sp; + if (text_section_sp.get()) + eh_frame_section_sp = text_section_sp->GetChildren().FindSectionByName (g_section_name_eh_frame); + else + eh_frame_section_sp = section_list->FindSectionByName (g_section_name_eh_frame); + + uint8_t TEXT_eh_frame_sectID = eh_frame_section_sp.get() ? eh_frame_section_sp->GetID() : NO_SECT; + //uint32_t symtab_offset = 0; + const uint8_t* nlist_data = symtab_data_sp->GetBytes(); + assert (symtab_data_sp->GetByteSize()/nlist_size >= symtab_load_command.nsyms); + + + if (endian != eByteOrderHost) + { + // ... + assert (!"UNIMPLEMENTED: Swap all nlist entries"); + } + uint32_t N_SO_index = UINT_MAX; + + MachSymtabSectionInfo section_info (section_list); + std::vector<uint32_t> N_FUN_indexes; + std::vector<uint32_t> N_NSYM_indexes; + std::vector<uint32_t> N_INCL_indexes; + std::vector<uint32_t> N_BRAC_indexes; + std::vector<uint32_t> N_COMM_indexes; + uint32_t nlist_idx = 0; + Symbol *symbol_ptr = NULL; + + uint32_t sym_idx = 0; + Symbol *sym = symtab->Resize (symtab_load_command.nsyms + m_dysymtab.nindirectsyms); + uint32_t num_syms = symtab->GetNumSymbols(); + + //symtab->Reserve (symtab_load_command.nsyms + m_dysymtab.nindirectsyms); + for (nlist_idx = 0; nlist_idx < symtab_load_command.nsyms; ++nlist_idx) + { + struct nlist_64 nlist; + if (bit_width_32) + { + struct nlist* nlist32_ptr = (struct nlist*)(nlist_data + (nlist_idx * nlist_size)); + nlist.n_un.n_strx = nlist32_ptr->n_un.n_strx; + nlist.n_type = nlist32_ptr->n_type; + nlist.n_sect = nlist32_ptr->n_sect; + nlist.n_desc = nlist32_ptr->n_desc; + nlist.n_value = nlist32_ptr->n_value; + } + else + { + nlist = *((struct nlist_64*)(nlist_data + (nlist_idx * nlist_size))); + } + + SymbolType type = eSymbolTypeInvalid; + const char* symbol_name = &strtab_data[nlist.n_un.n_strx]; + if (symbol_name[0] == '\0') + symbol_name = NULL; + Section* symbol_section = NULL; + bool add_nlist = true; + bool is_debug = ((nlist.n_type & N_STAB) != 0); + + assert (sym_idx < num_syms); + + sym[sym_idx].SetDebug (is_debug); + + if (is_debug) + { + switch (nlist.n_type) + { + case N_GSYM: // global symbol: name,,NO_SECT,type,0 + // Sometimes the N_GSYM value contains the address. + if (nlist.n_value != 0) + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeGlobal; + break; + + case N_FNAME: // procedure name (f77 kludge): name,,NO_SECT,0,0 + type = eSymbolTypeFunction; + break; + + case N_FUN: // procedure: name,,n_sect,linenumber,address + if (symbol_name) + { + type = eSymbolTypeFunction; + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + N_FUN_indexes.push_back(sym_idx); + } + else + { + type = eSymbolTypeFunctionEnd; + + if ( !N_FUN_indexes.empty() ) + { + // Copy the size of the function into the original STAB entry so we don't have + // to hunt for it later + symtab->SymbolAtIndex(N_FUN_indexes.back())->SetByteSize(nlist.n_value); + N_FUN_indexes.pop_back(); + // We dont' really need the end function STAB as it contains the size which + // we already placed with the original symbol, so don't add it if we want a + // minimal symbol table + if (minimize) + add_nlist = false; + } + } + break; + + case N_STSYM: // static symbol: name,,n_sect,type,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeStatic; + break; + + case N_LCSYM: // .lcomm symbol: name,,n_sect,type,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeCommonBlock; + break; + + case N_BNSYM: + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + if (minimize) + { + // Skip these if we want minimal symbol tables + add_nlist = false; + } + else + { + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + N_NSYM_indexes.push_back(sym_idx); + type = eSymbolTypeScopeBegin; + } + break; + + case N_ENSYM: + // Set the size of the N_BNSYM to the terminating index of this N_ENSYM + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + if (minimize) + { + // Skip these if we want minimal symbol tables + add_nlist = false; + } + else + { + if ( !N_NSYM_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_NSYM_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_NSYM_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + } + break; + + + case N_OPT: // emitted with gcc2_compiled and in gcc source + type = eSymbolTypeCompiler; + break; + + case N_RSYM: // register sym: name,,NO_SECT,type,register + type = eSymbolTypeVariable; + break; + + case N_SLINE: // src line: 0,,n_sect,linenumber,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeLineEntry; + break; + + case N_SSYM: // structure elt: name,,NO_SECT,type,struct_offset + type = eSymbolTypeVariableType; + break; + + case N_SO: + type = eSymbolTypeSourceFile; + if (symbol_name == NULL) + { + if (N_SO_index == UINT_MAX) + { + // Skip the extra blank N_SO entries that happen when the entire + // path is contained in the second consecutive N_SO STAB. + if (minimize) + add_nlist = false; + } + else + { + // Set the size of the N_SO to the terminating index of this N_SO + // so that we can always skip the entire N_SO if we need to navigate + // more quickly at the source level when parsing STABS + symbol_ptr = symtab->SymbolAtIndex(N_SO_index); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + } + N_NSYM_indexes.clear(); + N_INCL_indexes.clear(); + N_BRAC_indexes.clear(); + N_COMM_indexes.clear(); + N_FUN_indexes.clear(); + N_SO_index = UINT_MAX; + } + else if (symbol_name[0] == '/') + { + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + N_SO_index = sym_idx; + } + break; + + case N_OSO: // object file name: name,,0,0,st_mtime + type = eSymbolTypeObjectFile; + break; + + case N_LSYM: // local sym: name,,NO_SECT,type,offset + type = eSymbolTypeLocal; + break; + + //---------------------------------------------------------------------- + // INCL scopes + //---------------------------------------------------------------------- + case N_BINCL: // include file beginning: name,,NO_SECT,0,sum + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + N_INCL_indexes.push_back(sym_idx); + type = eSymbolTypeScopeBegin; + break; + case N_EINCL: // include file end: name,,NO_SECT,0,0 + + // Set the size of the N_BINCL to the terminating index of this N_EINCL + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + if ( !N_INCL_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_INCL_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_INCL_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + break; + + case N_SOL: // #included file name: name,,n_sect,0,address + type = eSymbolTypeHeaderFile; + break; + + case N_PARAMS: // compiler parameters: name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_VERSION: // compiler version: name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_OLEVEL: // compiler -O level: name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_PSYM: // parameter: name,,NO_SECT,type,offset + type = eSymbolTypeVariable; + break; + + case N_ENTRY: // alternate entry: name,,n_sect,linenumber,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeLineEntry; + break; + + //---------------------------------------------------------------------- + // Left and Right Braces + //---------------------------------------------------------------------- + case N_LBRAC: // left bracket: 0,,NO_SECT,nesting level,address + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + N_BRAC_indexes.push_back(sym_idx); + type = eSymbolTypeScopeBegin; + break; + + case N_RBRAC: // right bracket: 0,,NO_SECT,nesting level,address + // Set the size of the N_LBRAC to the terminating index of this N_RBRAC + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + if ( !N_BRAC_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_BRAC_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_BRAC_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + break; + + case N_EXCL: // deleted include file: name,,NO_SECT,0,sum + type = eSymbolTypeHeaderFile; + break; + + //---------------------------------------------------------------------- + // COMM scopes + //---------------------------------------------------------------------- + case N_BCOMM: // begin common: name,,NO_SECT,0,0 + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + type = eSymbolTypeScopeBegin; + N_COMM_indexes.push_back(sym_idx); + break; + + case N_ECOML: // end common (local name): 0,,n_sect,0,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + // Fall through + + case N_ECOMM: // end common: name,,n_sect,0,0 + // Set the size of the N_BCOMM to the terminating index of this N_ECOMM/N_ECOML + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + if ( !N_COMM_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_COMM_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_COMM_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + break; + + case N_LENG: // second stab entry with length information + type = eSymbolTypeAdditional; + break; + + default: break; + } + } + else + { + //uint8_t n_pext = N_PEXT & nlist.n_type; + uint8_t n_type = N_TYPE & nlist.n_type; + sym[sym_idx].SetExternal((N_EXT & nlist.n_type) != 0); + + if (symbol_name && ::strstr (symbol_name, ".objc") == symbol_name) + { + type = eSymbolTypeRuntime; + } + else + { + switch (n_type) + { + case N_INDR: // Fall through + case N_PBUD: // Fall through + case N_UNDF: + type = eSymbolTypeExtern; + break; + + case N_ABS: + type = eSymbolTypeAbsolute; + break; + + case N_SECT: + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + + assert(symbol_section != NULL); + if (TEXT_eh_frame_sectID == nlist.n_sect) + { + type = eSymbolTypeException; + } + else + { + uint32_t section_type = symbol_section->GetAllFlagBits() & SECTION_TYPE; + + switch (section_type) + { + case S_REGULAR: break; // regular section + //case S_ZEROFILL: type = eSymbolTypeData; break; // zero fill on demand section + case S_CSTRING_LITERALS: type = eSymbolTypeData; break; // section with only literal C strings + case S_4BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 4 byte literals + case S_8BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 8 byte literals + case S_LITERAL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only pointers to literals + case S_NON_LAZY_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only non-lazy symbol pointers + case S_LAZY_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only lazy symbol pointers + case S_SYMBOL_STUBS: type = eSymbolTypeTrampoline; break; // section with only symbol stubs, byte size of stub in the reserved2 field + case S_MOD_INIT_FUNC_POINTERS: type = eSymbolTypeCode; break; // section with only function pointers for initialization + case S_MOD_TERM_FUNC_POINTERS: type = eSymbolTypeCode; break; // section with only function pointers for termination + //case S_COALESCED: type = eSymbolType; break; // section contains symbols that are to be coalesced + //case S_GB_ZEROFILL: type = eSymbolTypeData; break; // zero fill on demand section (that can be larger than 4 gigabytes) + case S_INTERPOSING: type = eSymbolTypeTrampoline; break; // section with only pairs of function pointers for interposing + case S_16BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 16 byte literals + case S_DTRACE_DOF: type = eSymbolTypeInstrumentation; break; + case S_LAZY_DYLIB_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; + default: break; + } + + if (type == eSymbolTypeInvalid) + { + const char *symbol_sect_name = symbol_section->GetName().AsCString(); + if (symbol_section->IsDescendant (text_section_sp.get())) + { + if (symbol_section->IsClear(S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE | S_ATTR_SOME_INSTRUCTIONS)) + type = eSymbolTypeData; + else + type = eSymbolTypeCode; + } + else + if (symbol_section->IsDescendant(data_section_sp.get())) + { + if (symbol_sect_name && ::strstr (symbol_sect_name, "__objc") == symbol_sect_name) + { + type = eSymbolTypeRuntime; + } + else + if (symbol_sect_name && ::strstr (symbol_sect_name, "__gcc_except_tab") == symbol_sect_name) + { + type = eSymbolTypeException; + } + else + { + type = eSymbolTypeData; + } + } + else + if (symbol_sect_name && ::strstr (symbol_sect_name, "__IMPORT") == symbol_sect_name) + { + type = eSymbolTypeTrampoline; + } + else + if (symbol_section->IsDescendant(objc_section_sp.get())) + { + type = eSymbolTypeRuntime; + } + } + } + break; + } + } + } + + if (add_nlist) + { + bool symbol_name_is_mangled = false; + if (symbol_name && symbol_name[0] == '_') + { + symbol_name_is_mangled = symbol_name[1] == '_'; + symbol_name++; // Skip the leading underscore + } + uint64_t symbol_value = nlist.n_value; + if (symbol_section != NULL) + symbol_value -= symbol_section->GetFileAddress(); + + sym[sym_idx].SetID (nlist_idx); + sym[sym_idx].SetType (type); + if (symbol_name) + sym[sym_idx].GetMangled().SetValue(symbol_name, symbol_name_is_mangled); + sym[sym_idx].GetAddressRangeRef().GetBaseAddress().SetSection (symbol_section); + sym[sym_idx].GetAddressRangeRef().GetBaseAddress().SetOffset (symbol_value); + sym[sym_idx].SetFlags (nlist.n_type << 16 | nlist.n_desc); + + ++sym_idx; + } + else + { + sym[sym_idx].Clear(); + } + + } + + + // STAB N_GSYM entries end up having a symbol type eSymbolTypeGlobal and when the symbol value + // is zero, the address of the global ends up being in a non-STAB entry. Try and fix up all + // such entries by figuring out what the address for the global is by looking up this non-STAB + // entry and copying the value into the debug symbol's value to save us the hassle in the + // debug symbol parser. + + Symbol *global_symbol = NULL; + for (nlist_idx = 0; + nlist_idx < symtab_load_command.nsyms && (global_symbol = symtab->FindSymbolWithType(eSymbolTypeGlobal, nlist_idx)) != NULL; + nlist_idx++) + { + if (global_symbol->GetValue().GetFileAddress() == 0) + { + std::vector<uint32_t> indexes; + if (symtab->AppendSymbolIndexesWithName(global_symbol->GetMangled().GetName(), indexes) > 0) + { + std::vector<uint32_t>::const_iterator pos; + std::vector<uint32_t>::const_iterator end = indexes.end(); + for (pos = indexes.begin(); pos != end; ++pos) + { + symbol_ptr = symtab->SymbolAtIndex(*pos); + if (symbol_ptr != global_symbol && symbol_ptr->IsDebug() == false) + { + global_symbol->SetValue(symbol_ptr->GetValue()); + break; + } + } + } + } + } + // Now synthesize indirect symbols + if (m_dysymtab.nindirectsyms != 0) + { + DataBufferSP indirect_symbol_indexes_sp(m_file.ReadFileContents(m_offset + m_dysymtab.indirectsymoff, m_dysymtab.nindirectsyms * 4)); + + if (indirect_symbol_indexes_sp && indirect_symbol_indexes_sp->GetByteSize()) + { + DataExtractor indirect_symbol_index_data (indirect_symbol_indexes_sp, m_data.GetByteOrder(), m_data.GetAddressByteSize()); + + for (uint32_t sect_idx = 1; sect_idx < m_mach_sections.size(); ++sect_idx) + { + if ((m_mach_sections[sect_idx].flags & SECTION_TYPE) == S_SYMBOL_STUBS) + { + uint32_t symbol_stub_byte_size = m_mach_sections[sect_idx].reserved2; + if (symbol_stub_byte_size == 0) + continue; + + const uint32_t num_symbol_stubs = m_mach_sections[sect_idx].size / symbol_stub_byte_size; + + if (num_symbol_stubs == 0) + continue; + + const uint32_t symbol_stub_index_offset = m_mach_sections[sect_idx].reserved1; + uint32_t stub_sym_id = symtab_load_command.nsyms; + for (uint32_t stub_idx = 0; stub_idx < num_symbol_stubs; ++stub_idx) + { + const uint32_t symbol_stub_index = symbol_stub_index_offset + stub_idx; + const lldb::addr_t symbol_stub_addr = m_mach_sections[sect_idx].addr + (stub_idx * symbol_stub_byte_size); + uint32_t symbol_stub_offset = symbol_stub_index * 4; + if (indirect_symbol_index_data.ValidOffsetForDataOfSize(symbol_stub_offset, 4)) + { + const uint32_t symbol_index = indirect_symbol_index_data.GetU32 (&symbol_stub_offset); + + Symbol *stub_symbol = symtab->SymbolAtIndex(symbol_index); + if (stub_symbol) + { + Address so_addr(symbol_stub_addr, section_list); + + if (stub_symbol->GetType() == eSymbolTypeExtern) + { + // Change the external symbol into a trampoline that makes sense + // These symbols were N_UNDF N_EXT, and are useless to us, so we + // can re-use them so we don't have to make up a synthetic symbol + // for no good reason. + stub_symbol->SetType (eSymbolTypeTrampoline); + stub_symbol->SetExternal (false); + stub_symbol->GetAddressRangeRef().GetBaseAddress() = so_addr; + stub_symbol->GetAddressRangeRef().SetByteSize (symbol_stub_byte_size); + } + else + { + // Make a synthetic symbol to describe the trampoline stub + if (sym_idx >= num_syms) + { + sym = symtab->Resize (num_syms + 16); + num_syms = symtab->GetNumSymbols(); + } + sym[sym_idx].SetID (stub_sym_id++); + sym[sym_idx].GetMangled() = stub_symbol->GetMangled(); + sym[sym_idx].SetType (eSymbolTypeTrampoline); + sym[sym_idx].SetIsSynthetic (true); + sym[sym_idx].GetAddressRangeRef().GetBaseAddress() = so_addr; + sym[sym_idx].GetAddressRangeRef().SetByteSize (symbol_stub_byte_size); + ++sym_idx; + } + } + } + } + } + } + } + } + + if (sym_idx != symtab->GetNumSymbols()) + symtab->Resize (sym_idx); + + return symtab->GetNumSymbols(); + } + } + offset = cmd_offset + symtab_load_command.cmdsize; + } + return 0; +} + + +void +ObjectFileMachO::Dump (Stream *s) +{ + lldb_private::Mutex::Locker locker(m_mutex); + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + if (m_header.magic == MH_MAGIC_64 || m_header.magic == MH_CIGAM_64) + s->PutCString("ObjectFileMachO64"); + else + s->PutCString("ObjectFileMachO32"); + + ArchSpec header_arch(m_header.cputype, m_header.cpusubtype); + + *s << ", file = '" << m_file << "', arch = " << header_arch.AsCString() << "\n"; + + if (m_sections_ap.get()) + m_sections_ap->Dump(s, NULL, true); + + if (m_symtab_ap.get()) + m_symtab_ap->Dump(s, NULL); +} + + +bool +ObjectFileMachO::GetUUID (UUID* uuid) +{ + lldb_private::Mutex::Locker locker(m_mutex); + struct uuid_command load_cmd; + uint32_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t i; + for (i=0; i<m_header.ncmds; ++i) + { + const uint32_t cmd_offset = offset; + if (m_data.GetU32(&offset, &load_cmd, 2) == NULL) + break; + + if (load_cmd.cmd == LC_UUID) + { + const uint8_t *uuid_bytes = m_data.PeekData(offset, 16); + if (uuid_bytes) + { + uuid->SetBytes (uuid_bytes); + return true; + } + return false; + } + offset = cmd_offset + load_cmd.cmdsize; + } + return false; +} + + +uint32_t +ObjectFileMachO::GetDependentModules (FileSpecList& files) +{ + lldb_private::Mutex::Locker locker(m_mutex); + struct load_command load_cmd; + uint32_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t count = 0; + uint32_t i; + for (i=0; i<m_header.ncmds; ++i) + { + const uint32_t cmd_offset = offset; + if (m_data.GetU32(&offset, &load_cmd, 2) == NULL) + break; + + switch (load_cmd.cmd) + { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_DYLINKER: + case LC_LOADFVMLIB: + { + uint32_t name_offset = cmd_offset + m_data.GetU32(&offset); + const char *path = m_data.PeekCStr(name_offset); + // Skip any path that starts with '@' since these are usually: + // @executable_path/.../file + // @rpath/.../file + if (path && path[0] != '@') + { + FileSpec file_spec(path); + if (files.AppendIfUnique(file_spec)) + count++; + } + } + break; + + default: + break; + } + offset = cmd_offset + load_cmd.cmdsize; + } + return count; +} + +bool +ObjectFileMachO::GetTargetTriple (ConstString &target_triple) +{ + lldb_private::Mutex::Locker locker(m_mutex); + std::string triple(GetModule()->GetArchitecture().AsCString()); + triple += "-apple-darwin"; + target_triple.SetCString(triple.c_str()); + if (target_triple) + return true; + return false; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +ObjectFileMachO::GetPluginName() +{ + return "ObjectFileMachO"; +} + +const char * +ObjectFileMachO::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectFileMachO::GetPluginVersion() +{ + return 1; +} + +void +ObjectFileMachO::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +ObjectFileMachO::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +ObjectFileMachO::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + + + diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h new file mode 100644 index 00000000000..3ffeb242b7c --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h @@ -0,0 +1,131 @@ +//===-- ObjectFileMachO.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_ObjectFileMachO_h_ +#define liblldb_ObjectFileMachO_h_ + +#include <mach-o/loader.h> +#include "lldb/Core/FileSpec.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/ObjectFile.h" + +//---------------------------------------------------------------------- +// This class needs to be hidden as eventually belongs in a plugin that +// will export the ObjectFile protocol +//---------------------------------------------------------------------- +class ObjectFileMachO : + public lldb_private::ObjectFile +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static ObjectFile * + CreateInstance (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec* file, + lldb::addr_t offset, + lldb::addr_t length); + + static bool + MagicBytesMatch (lldb::DataBufferSP& dataSP); + + //------------------------------------------------------------------ + // Member Functions + //------------------------------------------------------------------ + ObjectFileMachO (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec* file, + lldb::addr_t offset, + lldb::addr_t length); + + virtual + ~ObjectFileMachO(); + + virtual bool + ParseHeader (); + + virtual lldb::ByteOrder + GetByteOrder () const; + + virtual size_t + GetAddressByteSize () const; + + virtual lldb_private::Symtab * + GetSymtab(); + + virtual lldb_private::SectionList * + GetSectionList(); + + virtual void + Dump (lldb_private::Stream *s); + + virtual bool + GetTargetTriple (lldb_private::ConstString &target_triple); + + virtual bool + GetUUID (lldb_private::UUID* uuid); + + virtual uint32_t + GetDependentModules (lldb_private::FileSpecList& files); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + + + +protected: + mutable lldb_private::Mutex m_mutex; + struct mach_header m_header; + mutable std::auto_ptr<lldb_private::SectionList> m_sections_ap; + mutable std::auto_ptr<lldb_private::Symtab> m_symtab_ap; + + struct dysymtab_command m_dysymtab; + std::vector<struct segment_command_64> m_mach_segments; + std::vector<struct section_64> m_mach_sections; + + size_t + ParseSections (); + + size_t + ParseSymtab (bool minimize); + +}; + +#endif // liblldb_ObjectFileMachO_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/scripts/cc-swig b/lldb/source/Plugins/Process/MacOSX-User/scripts/cc-swig new file mode 100644 index 00000000000..0bb089a653e --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/scripts/cc-swig @@ -0,0 +1,47 @@ +#!/usr/bin/perl + +use File::Basename; + +sub execute_command +{ + print join(' ', @_), "\n"; + if (scalar(@_) > 0) { + system(@_); + } else { + system($_[0]); + } +} + +my $infile = $ENV{SCRIPT_INPUT_FILE_1}; +my($in_basename, $in_dirname, $in_extension) = fileparse($infile, qr/\.[^.]*/); +my $outdir = "$ENV{DERIVED_FILE_DIR}"; +my $perl_wrap_c = "$outdir/${in_basename}_perl_wrap.c"; +mkdir "$ENV{OBJECT_FILE_DIR}"; +my $perl_wrap_o = "$ENV{OBJECT_FILE_DIR}/${in_basename}_perl_wrap.o"; +my $perl_module = "$outdir/${in_basename}.pm"; +my $header_paths = "-I'../../../../../debugcore/source' -I'../../../../../DebugBase'"; +my $framework_opts = "-F'$ENV{CONFIGURATION_BUILD_DIR}' "; +execute_command("/usr/bin/swig -shadow -perl5 -DHAS_BOOL $header_paths -outdir '$outdir' -o '$perl_wrap_c' '$infile'"); + +# Get any needed perl options for the next compile +my $ccopts = `perl -MExtUtils::Embed -e ccopts`; +my $libperl_dir = undef; +if ($ccopts =~ /-I(\/System.*CORE)/) +{ + $libperl_dir = $1; + print "libperl directory: '$libperl_dir'\n"; +} + +execute_command("cd '$ENV{OBJECT_FILE_DIR}' && ln -s '$libperl_dir/libperl.dylib'"); + + +# Strip out the default architectures it gave us, we will add them back with +# the $arch_opts below +$ccopts =~ s/-arch [a-z_0-9]+//g; + +# Get a list of our build architectures +my $arch_opts = "-arch " . join(' -arch ', split('\s+', $ENV{ARCHS})); + +execute_command("gcc -c -Dbool=char $arch_opts $ccopts $header_paths $framework_opts -I'$ENV{PROJECT_DIR}/source' '$perl_wrap_c' -o '$perl_wrap_o'"); + +execute_command("cp '$perl_module' '$ENV{CONFIGURATION_BUILD_DIR}/$ENV{SHARED_SUPPORT_FOLDER_PATH}'");
\ No newline at end of file diff --git a/lldb/source/Plugins/Process/MacOSX-User/scripts/config.pl b/lldb/source/Plugins/Process/MacOSX-User/scripts/config.pl new file mode 100644 index 00000000000..a6cf6ce2396 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/scripts/config.pl @@ -0,0 +1,71 @@ +#!/usr/bin/perl + +use strict; +my $config_file = "$ENV{SCRIPT_OUTPUT_FILE_0}"; + +# Define the tests we need to run during this configuration +my @config_tests = ( + { + NAME => "HAVE_64_BIT_MACH_EXCEPTIONS", + TEST => "-e '$ENV{SDKROOT}/usr/include/mach/mach_exc.defs'", + COMMENT => "// Defined if we can use 64 bit mach exceptions", + FAIL => "#undef HAVE_64_BIT_MACH_EXCEPTIONS\ +#define mach_exception_data_t exception_data_t\ +#define mach_exception_data_type_t exception_data_type_t\ +#define mach_exc_server exc_server\ +#define MACH_EXCEPTION_CODES 0\n", + SUCCESS => "#define HAVE_64_BIT_MACH_EXCEPTIONS 1\n", + } +); + +#---------------------------------------------------------------------- +# Open the config file +#---------------------------------------------------------------------- +open(CONFIG, "> $config_file") || die "Couldn't open '$config_file' for writing: $!\n"; +print CONFIG "/*" . "-" x 72 . "\n"; +print CONFIG "// This file is auto generated by a config.pl, do not edit by hand!\n"; +print CONFIG "//" . "-" x 72 . "\n"; +print CONFIG "// COMMAND LINE\n"; +print CONFIG "// " . join(' ', @ARGV) . "\n"; +print CONFIG "//" . "-" x 72 . "\n"; +print CONFIG "// ENVIRONMENT\n"; +my $key; +my $val; +while (($key, $val) = each %ENV) +{ + printf CONFIG "// %s = %s\n", $key, $val; +} +print CONFIG "//" . "-" x 72 . "\n"; +print CONFIG "// SETTINGS\n"; +print CONFIG "// config_file: '$config_file'\n"; +print CONFIG "//" . "-" x 72 . "\n"; +print CONFIG "*/\n\n"; +print CONFIG "#ifndef liblldb_PDConfig_h_\n"; +print CONFIG "#define liblldb_PDConfig_h_\n"; + + +#---------------------------------------------------------------------- +# Run the tests +#---------------------------------------------------------------------- +foreach my $test_href (@config_tests) +{ + if (exists $test_href->{COMMENT}) { + print CONFIG "\n$test_href->{COMMENT}\n"; + } else { + print CONFIG "\n// $test_href->{NAME}\n"; + } + + my $test_result = eval "$test_href->{TEST}"; + if ($test_result != 0) + { + print CONFIG "$test_href->{SUCCESS}\n"; + } + else + { + print CONFIG "$test_href->{FAIL}\n"; + } +} + +print CONFIG "#endif // #ifndef liblldb_PDConfig_h_\n"; +close(CONFIG); + diff --git a/lldb/source/Plugins/Process/MacOSX-User/scripts/test-ProcessDebug.pl b/lldb/source/Plugins/Process/MacOSX-User/scripts/test-ProcessDebug.pl new file mode 100755 index 00000000000..96b3115c912 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/scripts/test-ProcessDebug.pl @@ -0,0 +1,409 @@ +#!/usr/bin/perl + +use strict; +use Cwd 'abs_path'; +our $home = $ENV{HOME} || die "ERROR: Couldn't deduce your home directory...\n"; + +our @inc_paths = ( + './include', +); + +my $inc_paths_added = 0; +foreach my $inc_path (@inc_paths) +{ + if (-e $inc_path) + { + push (@INC, abs_path($inc_path)); + $inc_paths_added++; + } +} + +if ($inc_paths_added == 0) +{ + die "Please compile the Release version of lldb\n"; +} + +require lldb; + +# my $state = lldb::eStateAttaching; + +use constant UINT32_MAX => 4294967295; + +#---------------------------------------------------------------------- +# Interactive Commands +#---------------------------------------------------------------------- +our %commands = ( + break => { + name => 'break', # in case an alias is used to get to this command + description => "Sets a breakpoint.", + usage => ["break ADDR"], + function => \&command_set_breakpoint, + runs_target => 0, + }, + delete => { + name => 'delete', # in case an alias is used to get to this command + description => "Deletes one or more breakpoints by ID.\ +If no breakpoint IDs are given all breakpoints will be deleted.\ +If one or more IDs are given, only those breakpoints will be deleted.", + usage => ["delete [ID1 ID2 ...]"], + function => \&command_clear_breakpoint, + runs_target => 0, + }, + continue => { + name => 'continue', # in case an alias is used to get to this command + description => "Continues target execution.", + usage => ["continue [ADDR]"], + function => \&command_continue, + runs_target => 1 + }, + step => { + name => 'step', # in case an alias is used to get to this command + description => "Single steps one instruction.", + usage => ["step"], + function => \&command_step, + runs_target => 1 + }, + info => { + name => 'info', # in case an alias is used to get to this command + description => "Gets info on a variety of things.", + usage => ["info reg", "info thread", "info threads"], + function => \&command_info, + runs_target => 0 + }, + help => { + name => 'help', # in case an alias is used to get to this command + description => "Displays a list of all commands, or help for a specific command.", + usage => ["help", "help CMD"], + function => \&command_help, + runs_target => 0 + } +); + +#---------------------------------------------------------------------- +# Command aliases +#---------------------------------------------------------------------- +our %aliases = ( + b => $commands{break}, + c => $commands{continue}, + s => $commands{step}, + d => $commands{delete}, + h => $commands{help} +); + +our $opt_g = 0; # Enable verbose debug logging +our $opt_v = 0; # Verbose mode +my $prev_command_href = undef; +my $stdio = '/dev/stdin'; +my $launch = 0; +my @env = (); +my @break_ids; + +#---------------------------------------------------------------------- +# Given a command string, return the command hash reference for it, or +# undef if it doesn't exist. +#---------------------------------------------------------------------- +sub get_command_hash_ref +{ + my $cmd = shift; + my $cmd_href = undef; + if (length($cmd) == 0) { $cmd_href = $prev_command_href; } + elsif (exists $aliases{$cmd}) { $cmd_href = $aliases{$cmd}; } + elsif (exists $commands{$cmd}) { $cmd_href = $commands{$cmd}; } + defined $cmd_href and $prev_command_href = $cmd_href; + return $cmd_href; +} + +#---------------------------------------------------------------------- +# Set a breakpoint +#---------------------------------------------------------------------- +sub command_set_breakpoint +{ + my $pid = shift; + my $tid = shift; + $opt_g and print "command_set_breakpoint (pid = $pid, locations = @_)\n"; + foreach my $location (@_) + { + my $success = 0; + my $address = hex($location); + if ($address != 0) + { + my $break_id = lldb::PDBreakpointSet ($pid, $address, 1, 0); + if ($break_id != $lldb::PD_INVALID_BREAK_ID) + { + printf("Breakpoint %i is set.\n", $break_id); + push(@break_ids, $break_id); + $success = 1; + } + } + $success or print("error: failed to set breakpoint at $location.\n"); + } + return 1; +} + +#---------------------------------------------------------------------- +# Clear a breakpoint +#---------------------------------------------------------------------- +sub command_clear_breakpoint +{ + my $pid = shift; + my $tid = shift; + if (@_) + { + my $break_id; + my @cleared_break_ids; + my @new_break_ids; + $opt_g and print "command_clear_breakpoint (pid = $pid, break_ids = @_)\n"; + foreach $break_id (@_) + { + if (lldb::PDBreakpointClear ($pid, $break_id)) + { + printf("Breakpoint %i has been cleared.\n", $break_id); + push (@cleared_break_ids, $break_id); + } + else + { + printf("error: failed to clear breakpoint %i.\n", $break_id); + } + } + + foreach my $old_break_id (@break_ids) + { + my $found_break_id = 0; + foreach $break_id (@cleared_break_ids) + { + if ($old_break_id == $break_id) + { + $found_break_id = 1; + } + } + $found_break_id or push (@new_break_ids, $old_break_id); + } + @break_ids = @new_break_ids; + } + else + { + # Nothing specified, clear all breakpoints + return command_clear_breakpoint($pid, $tid, @break_ids); + } + return 1; +} +#---------------------------------------------------------------------- +# Continue program execution +#---------------------------------------------------------------------- +sub command_continue +{ + my $pid = shift; + my $tid = shift; + $opt_g and print "command_continue (pid = $pid)\n"; + if ($pid != $lldb::PD_INVALID_PROCESS_ID) + { + $opt_v and printf("Resuming pid %d...\n", $pid); + return lldb::PDProcessResume ($pid); + } + return 0; +} + +sub command_step +{ + my $pid = shift; + my $tid = shift; + $opt_g and print "command_step (pid = $pid, tid = $tid)\n"; + if ($pid != $lldb::PD_INVALID_PROCESS_ID) + { + $opt_v and printf("Single stepping pid %d tid = %4.4x...\n", $pid, $tid); + return lldb::PDThreadResume ($pid, $tid, 1); + } + return 0; +} + +sub command_info +{ + my $pid = shift; + my $tid = shift; + $opt_g and print "command_step (pid = $pid, tid = $tid)\n"; + if ($pid != $lldb::PD_INVALID_PROCESS_ID) + { + if (@_) + { + my $info_cmd = shift; + if ($info_cmd eq 'reg') + { + + } + elsif ($info_cmd eq 'thread') + { + # info on the current thread + printf("thread 0x%4.4x %s\n", $tid, lldb::PDThreadGetInfo($pid, $tid)); + } + elsif ($info_cmd eq 'threads') + { + my $num_threads = lldb::PDProcessGetNumThreads( $pid ); + for my $thread_num (1..$num_threads) + { + my $curr_tid = lldb::PDProcessGetThreadAtIndex ( $pid, $thread_num - 1 ); + printf("%c%u - thread 0x%4.4x %s\n", $curr_tid == $tid ? '*' : ' ', $thread_num, $curr_tid, lldb::PDThreadGetInfo($pid, $curr_tid)); + } + } + } + } + return 1; +} +#---------------------------------------------------------------------- +# Get help on all commands, or a specific list of commands +#---------------------------------------------------------------------- +sub command_help +{ + my $pid = shift; + my $tid = shift; + if (@_) + { + $opt_g and print "command_continue (pid = $pid, commands = @_)\n"; + foreach my $cmd (@_) + { + my $cmd_href = get_command_hash_ref($cmd); + if ($cmd_href) + { + print '#', '-' x 72, "\n# $cmd_href->{name}\n", '#', '-' x 72, "\n"; + my $usage_aref = $cmd_href->{usage}; + if (@{$usage_aref}) + { + print " USAGE\n"; + foreach my $usage (@{$usage_aref}) { + print " $usage\n"; + } + print "\n"; + } + print " DESCRIPTION\n $cmd_href->{description}\n\n"; + } + else + { + print " invalid command: '$cmd'\n\n"; + } + } + } + else + { + return command_help($pid, sort keys %commands); + } + return 1; +} + + +#lldb::PDLogSetLogMask ($lldb::PD_LOG_ALL); +#lldb::PDLogSetLogFile ('/dev/stdout'); + +print "running: ", join(' ', @ARGV), "\n"; + +my $pid = lldb::PDProcessLaunch ($ARGV[0], \@ARGV, \@env, "i386", '/dev/stdin', '/dev/stdout', '/dev/stderr', $launch, '', 0); +my $pid_state; +while ($pid) +{ + $opt_g and printf("PDProcessWaitForEvents (%d, 0x%4.4x, SET, 1)\n", $pid, $lldb::PD_ALL_EVENTS); + my $events = lldb::PDProcessWaitForEvents ($pid, $lldb::PD_ALL_EVENTS, 1, 1); + if ($events) + { + $opt_g and printf ("Got event: 0x%8.8x\n", $events); + + if ($events & $lldb::PD_EVENT_IMAGES_CHANGED) + { + $opt_g and printf("pid %d images changed...\n", $pid); + } + + if ($events & $lldb::PD_EVENT_STDIO) + { + $opt_g and printf("pid %d has stdio...\n", $pid); + } + + if ($events & $lldb::PD_EVENT_ASYNC_INTERRUPT) + { + $opt_g and printf("pid %d got async interrupt...\n", $pid); + } + + if ($events & $lldb::PD_EVENT_RUNNING) + { + $pid_state = lldb::PDProcessGetState ($pid); + $opt_v and printf( "pid %d state: %s.\n", $pid, lldb::PDStateAsString ($pid_state) ); + } + + if ($events & $lldb::PD_EVENT_STOPPED) + { + $pid_state = lldb::PDProcessGetState ($pid); + $opt_v and printf( "pid %d state: %s.\n", $pid, lldb::PDStateAsString ($pid_state) ); + + if ($pid_state == $lldb::eStateUnloaded || + $pid_state == $lldb::eStateAttaching || + $pid_state == $lldb::eStateLaunching ) + { + + } + elsif ( $pid_state == $lldb::eStateStopped ) + { + my $tid = lldb::PDProcessGetCurrentThread ( $pid ); + my $pc = lldb::PDThreadGetRegisterHexValueByName($pid, $tid, $lldb::PD_REGISTER_SET_ALL, "eip", 0); + $pc != 0 and printf("pc = 0x%8.8x ", $pc); + # my $sp = lldb::PDThreadGetRegisterHexValueByName($pid, $tid, $lldb::PD_REGISTER_SET_ALL, "esp", 0); + # $sp != 0 and printf("sp = 0x%8.8x ", $sp); + # my $fp = lldb::PDThreadGetRegisterHexValueByName($pid, $tid, $lldb::PD_REGISTER_SET_ALL, "ebp", 0); + # $sp != 0 and printf("fp = 0x%8.8x ", $fp); + # print "\n"; + my $done = 0; + my $input; + while (!$done) + { + print '(pdbg) '; + + chomp($input = <STDIN>); + my @argv = split(/\s+/, $input); + my $cmd = @argv ? shift @argv : undef; + my $cmd_href = get_command_hash_ref ($cmd); + if ($cmd_href) + { + # Print the expanded alias if one was used + if ($opt_v and $cmd_href->{name} ne $cmd) + { + print "$cmd_href->{name} @argv\n"; + } + + # Call the command's callback function to make things happen + if ($cmd_href->{function}($pid, $tid, @argv)) + { + $done = $cmd_href->{runs_target}; + } + } + else + { + print "invalid command: '$cmd'\nType 'help' for a list of all commands.\nType 'help CMD' for help on a specific commmand.\n"; + } + } + } + elsif ( $pid_state == $lldb::eStateRunning || + $pid_state == $lldb::eStateStepping ) + { + + } + elsif ( $pid_state == $lldb::eStateCrashed || + $pid_state == $lldb::eStateDetached || + $pid_state == $lldb::eStateExited ) + { + $pid = 0; + } + elsif ( $pid_state == $lldb::eStateSuspended ) + { + } + else + { + } + } + + if ($pid) + { + $opt_g and printf("PDProcessResetEvents(%d, 0x%8.8x)\n", $pid, $events); + lldb::PDProcessResetEvents($pid, $events); + } + } +} + +if ($pid != $lldb::PD_INVALID_PROCESS_ID) +{ + lldb::PDProcessDetach ($pid); +} diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.cpp new file mode 100644 index 00000000000..7dc8d2ce652 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.cpp @@ -0,0 +1,575 @@ +//===-- MachException.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <errno.h> +#include <sys/types.h> +#include <sys/ptrace.h> + +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Host.h" + +#include "MachException.h" +#include "ProcessMacOSXLog.h" + +using namespace lldb_private; + +// Routine mach_exception_raise +extern "C" +kern_return_t catch_mach_exception_raise +( + mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt +); + +extern "C" +kern_return_t catch_mach_exception_raise_state +( + mach_port_t exception_port, + exception_type_t exception, + const mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int *flavor, + const thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t *new_stateCnt +); + +// Routine mach_exception_raise_state_identity +extern "C" +kern_return_t catch_mach_exception_raise_state_identity +( + mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int *flavor, + thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t *new_stateCnt +); + +extern "C" boolean_t mach_exc_server( + mach_msg_header_t *InHeadP, + mach_msg_header_t *OutHeadP); + +// Any access to the g_message variable should be done by locking the +// g_message_mutex first, using the g_message variable, then unlocking +// the g_message_mutex. See MachException::Message::CatchExceptionRaise() +// for sample code. + +static MachException::Data *g_message = NULL; +//static pthread_mutex_t g_message_mutex = PTHREAD_MUTEX_INITIALIZER; + + +extern "C" +kern_return_t +catch_mach_exception_raise_state +( + mach_port_t exc_port, + exception_type_t exc_type, + const mach_exception_data_t exc_data, + mach_msg_type_number_t exc_data_count, + int * flavor, + const thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t * new_stateCnt +) +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + if (log) + { + log->Printf("::%s ( exc_port = 0x%4.4x, exc_type = %d ( %s ), exc_data = " MACH_EXCEPTION_DATA_FMT_HEX ", exc_data_count = %d)", + __FUNCTION__, + exc_port, + exc_type, MachException::Name(exc_type), + exc_data, + exc_data_count); + } + return KERN_FAILURE; +} + +extern "C" +kern_return_t +catch_mach_exception_raise_state_identity +( + mach_port_t exc_port, + mach_port_t thread_port, + mach_port_t task_port, + exception_type_t exc_type, + mach_exception_data_t exc_data, + mach_msg_type_number_t exc_data_count, + int * flavor, + thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t *new_stateCnt +) +{ + kern_return_t kret; + Log * log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + if (log) + { + log->Printf("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { " MACH_EXCEPTION_DATA_FMT_HEX ", " MACH_EXCEPTION_DATA_FMT_HEX " })", + __FUNCTION__, + exc_port, + thread_port, + task_port, + exc_type, MachException::Name(exc_type), + exc_data_count, + exc_data_count > 0 ? exc_data[0] : 0xBADDBADD, + exc_data_count > 1 ? exc_data[1] : 0xBADDBADD); + } + kret = mach_port_deallocate (mach_task_self (), task_port); + kret = mach_port_deallocate (mach_task_self (), thread_port); + + return KERN_FAILURE; +} + +extern "C" +kern_return_t +catch_mach_exception_raise +( + mach_port_t exc_port, + mach_port_t thread_port, + mach_port_t task_port, + exception_type_t exc_type, + mach_exception_data_t exc_data, + mach_msg_type_number_t exc_data_count) +{ + Log * log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + if (log) + { + log->Printf("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { " MACH_EXCEPTION_DATA_FMT_HEX ", " MACH_EXCEPTION_DATA_FMT_HEX " })", + __FUNCTION__, + exc_port, + thread_port, + task_port, + exc_type, MachException::Name(exc_type), + exc_data_count, + exc_data_count > 0 ? exc_data[0] : 0xBADDBADD, + exc_data_count > 1 ? exc_data[1] : 0xBADDBADD); + } + + g_message->task_port = task_port; + g_message->thread_port = thread_port; + g_message->exc_type = exc_type; + g_message->exc_data.resize(exc_data_count); + ::memcpy (&g_message->exc_data[0], exc_data, g_message->exc_data.size() * sizeof (mach_exception_data_type_t)); + return KERN_SUCCESS; +} + + +void +MachException::Message::PutToLog(Log *log) const +{ + if (log) + { + log->Printf(" exc_msg { bits = 0x%8.8lx size = 0x%8.8lx remote-port = 0x%8.8lx local-port = 0x%8.8lx reserved = 0x%8.8lx id = 0x%8.8lx } ", + exc_msg.hdr.msgh_bits, + exc_msg.hdr.msgh_size, + exc_msg.hdr.msgh_remote_port, + exc_msg.hdr.msgh_local_port, + exc_msg.hdr.msgh_reserved, + exc_msg.hdr.msgh_id); + + log->Printf( "reply_msg { bits = 0x%8.8lx size = 0x%8.8lx remote-port = 0x%8.8lx local-port = 0x%8.8lx reserved = 0x%8.8lx id = 0x%8.8lx }", + reply_msg.hdr.msgh_bits, + reply_msg.hdr.msgh_size, + reply_msg.hdr.msgh_remote_port, + reply_msg.hdr.msgh_local_port, + reply_msg.hdr.msgh_reserved, + reply_msg.hdr.msgh_id); + state.PutToLog(log); + } +} + +bool +MachException::Data::GetStopInfo(Thread::StopInfo *stop_info) const +{ + // Zero out the structure. + stop_info->Clear(); + + // Make sure we have a valid exception before we return anything valid + if (exc_type == 0) + return true; + // We always stop with a mach exceptions + const size_t exc_data_count = exc_data.size(); + stop_info->SetStopReasonWithException(exc_type, exc_data_count); + + // Fill in a text description + const char * exc_name = MachException::Name(exc_type); + StreamString sstr; + if (exc_name) + sstr.PutCString(exc_name); + else + sstr.Printf ("%i", exc_type); + + int signal = SoftSignal(); + if (signal > 0) + { + const char *sig_str = Host::GetSignalAsCString(signal); + if (sig_str) + sstr.Printf (" EXC_SOFT_SIGNAL(%s)", sig_str); + else + sstr.Printf (" EXC_SOFT_SIGNAL(%i)", signal); + } + else + { + // No special disassembly for exception data, just + sstr.Printf (" data[%zu] = {", exc_data_count); + + for (size_t idx = 0; idx < exc_data_count; ++idx) + sstr.Printf (MACH_EXCEPTION_DATA_FMT_MINHEX "%s", exc_data[idx], ((idx + 1 == exc_data_count) ? "" : ",")); + + sstr.PutChar('}'); + } + + stop_info->SetStopDescription (sstr.GetData()); + + // Copy the exception data + size_t i; + for (i=0; i<exc_data_count; i++) + stop_info->SetExceptionDataAtIndex(i, exc_data[i]); + + return true; +} + + +void +MachException::Data::DumpStopReason() const +{ + Log * log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(); + if (log) + { + int signal = SoftSignal(); + if (signal > 0) + { + const char *signal_str = Host::GetSignalAsCString(signal); + if (signal_str) + log->Printf ("signal(%s)", signal_str); + else + log->Printf ("signal(%i)", signal); + return; + } + log->Printf ("%s", Name(exc_type)); + } +} + +kern_return_t +MachException::Message::Receive(mach_port_t port, mach_msg_option_t options, mach_msg_timeout_t timeout, mach_port_t notify_port) +{ + Error err; + Log * log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + mach_msg_timeout_t mach_msg_timeout = options & MACH_RCV_TIMEOUT ? timeout : 0; + if (log && ((options & MACH_RCV_TIMEOUT) == 0)) + { + // Dump this log message if we have no timeout in case it never returns + log->Printf ("::mach_msg ( msg->{bits = %#x, size = %u remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)", + exc_msg.hdr.msgh_bits, + exc_msg.hdr.msgh_size, + exc_msg.hdr.msgh_remote_port, + exc_msg.hdr.msgh_local_port, + exc_msg.hdr.msgh_reserved, + exc_msg.hdr.msgh_id, + options, + 0, + sizeof (exc_msg.data), + port, + mach_msg_timeout, + notify_port); + } + + err = ::mach_msg (&exc_msg.hdr, + options, // options + 0, // Send size + sizeof (exc_msg.data), // Receive size + port, // exception port to watch for exception on + mach_msg_timeout, // timeout in msec (obeyed only if MACH_RCV_TIMEOUT is ORed into the options parameter) + notify_port); + + // Dump any errors we get + if (log && err.GetError() != MACH_RCV_TIMED_OUT) + { + log->Error("::mach_msg ( msg->{bits = %#x, size = %u remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)", + exc_msg.hdr.msgh_bits, + exc_msg.hdr.msgh_size, + exc_msg.hdr.msgh_remote_port, + exc_msg.hdr.msgh_local_port, + exc_msg.hdr.msgh_reserved, + exc_msg.hdr.msgh_id, + options, + 0, + sizeof (exc_msg.data), + port, + mach_msg_timeout, + notify_port); + } + return err.GetError(); +} + +bool +MachException::Message::CatchExceptionRaise() +{ + bool success = false; + // locker will keep a mutex locked until it goes out of scope +// Mutex::Locker locker(&g_message_mutex); + // log->Printf ("calling mach_exc_server"); + g_message = &state; + // The exc_server function is the MIG generated server handling function + // to handle messages from the kernel relating to the occurrence of an + // exception in a thread. Such messages are delivered to the exception port + // set via thread_set_exception_ports or task_set_exception_ports. When an + // exception occurs in a thread, the thread sends an exception message to + // its exception port, blocking in the kernel waiting for the receipt of a + // reply. The exc_server function performs all necessary argument handling + // for this kernel message and calls catch_exception_raise, + // catch_exception_raise_state or catch_exception_raise_state_identity, + // which should handle the exception. If the called routine returns + // KERN_SUCCESS, a reply message will be sent, allowing the thread to + // continue from the point of the exception; otherwise, no reply message + // is sent and the called routine must have dealt with the exception + // thread directly. + if (mach_exc_server (&exc_msg.hdr, &reply_msg.hdr)) + { + success = true; + } + else + { + Log * log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + if (log) + log->Printf ("mach_exc_server returned zero..."); + } + g_message = NULL; + return success; +} + + + +kern_return_t +MachException::Message::Reply(task_t task, pid_t pid, int signal) +{ + // Reply to the exception... + Error err; + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(); + if (log) + log->Printf("MachException::Message::Reply (task = 0x%4.4x, pid = %i, signal = %i)", task, pid, signal); + + // If we had a soft signal, we need to update the thread first so it can + // continue without signaling + int soft_signal = state.SoftSignal(); + int state_pid = LLDB_INVALID_PROCESS_ID; + if (task == state.task_port) + { + // This is our task, so we can update the signal to send to it + state_pid = pid; + } + else + { + err = ::pid_for_task(state.task_port, &state_pid); + } + + if (signal == LLDB_INVALID_SIGNAL_NUMBER) + signal = 0; + + if (log) + log->Printf("MachException::Message::Reply () updating thread signal to %i (original soft_signal = %i)", signal, soft_signal); + + if (state_pid != LLDB_INVALID_PROCESS_ID) + { + errno = 0; + if (::ptrace (PT_THUPDATE, state_pid, (caddr_t)state.thread_port, signal) != 0) + { + if (soft_signal != LLDB_INVALID_SIGNAL_NUMBER) + // We know we currently can't forward signals for threads that didn't stop in EXC_SOFT_SIGNAL... + // So only report it as an error if we should have been able to do it. + err.SetErrorToErrno(); + else + err.Clear(); + } + else + err.Clear(); + + if (log && log->GetMask().IsSet(PD_LOG_EXCEPTIONS) || err.Fail()) + err.PutToLog(log, "::ptrace (request = PT_THUPDATE, pid = %i, tid = 0x%4.4x, signal = %i)", state_pid, state.thread_port, signal); + } + + err = ::mach_msg ( &reply_msg.hdr, + MACH_SEND_MSG | MACH_SEND_INTERRUPT, + reply_msg.hdr.msgh_size, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + if (log) + log->LogIf (PD_LOG_EXCEPTIONS, "::mach_msg ( msg->{bits = %#x, size = %u, remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x) = 0x%8.8x", + reply_msg.hdr.msgh_bits, + reply_msg.hdr.msgh_size, + reply_msg.hdr.msgh_remote_port, + reply_msg.hdr.msgh_local_port, + reply_msg.hdr.msgh_reserved, + reply_msg.hdr.msgh_id, + MACH_SEND_MSG | MACH_SEND_INTERRUPT, + reply_msg.hdr.msgh_size, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL, + err.GetError()); + + + if (err.Fail()) + { + if (err.GetError() == MACH_SEND_INTERRUPTED) + { + err.PutToLog(log, "::mach_msg() - send interrupted"); + } + else + { + if (state.task_port == task) + { + err.PutToLog(log, "::mach_msg() - failed (task)"); + abort (); + } + else + { + err.PutToLog(log, "::mach_msg() - failed (child of task)"); + } + } + } + + return err.GetError(); +} + + +void +MachException::Data::PutToLog(Log *log) const +{ + if (log == NULL) + return; + + const char *exc_type_name = MachException::Name(exc_type); + + log->Printf (" state { task_port = 0x%4.4x, thread_port = 0x%4.4x, exc_type = %i (%s) ...", task_port, thread_port, exc_type, exc_type_name ? exc_type_name : "???"); + + const size_t exc_data_count = exc_data.size(); + // Dump any special exception data contents + int soft_signal = SoftSignal(); + if (soft_signal > 0) + { + const char *sig_str = Host::GetSignalAsCString(soft_signal); + log->Printf (" exc_data: EXC_SOFT_SIGNAL (%i (%s))", soft_signal, sig_str ? sig_str : "unknown signal"); + } + else + { + // No special disassembly for this data, just dump the data + size_t idx; + for (idx = 0; idx < exc_data_count; ++idx) + { + log->Printf(" exc_data[%u]: " MACH_EXCEPTION_DATA_FMT_HEX, idx, exc_data[idx]); + } + } +} + + +MachException::PortInfo::PortInfo() : + count(0) +{ + ::bzero (masks, sizeof(masks)); + ::bzero (ports, sizeof(ports)); + ::bzero (behaviors, sizeof(behaviors)); + ::bzero (flavors, sizeof(flavors)); +} + + +kern_return_t +MachException::PortInfo::Save (task_t task) +{ + count = EXC_TYPES_COUNT; + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_EXCEPTIONS); + if (log) + log->Printf ("MachException::PortInfo::Save (task = 0x%4.4x)", task); + Error err; + if (log) + log->Printf("::task_get_exception_ports (task=0x%4.4x, mask=0x%x, maskCnt<=>%u, ports, behaviors, flavors)...", task, EXC_MASK_ALL, count); + err = ::task_get_exception_ports (task, EXC_MASK_ALL, masks, &count, ports, behaviors, flavors); + if (log || err.Fail()) + err.PutToLog(log, "::task_get_exception_ports (task=0x%4.4x, mask=0x%x, maskCnt<=>%u, ports, behaviors, flavors)", task, EXC_MASK_ALL, count); + if (log) + { + mach_msg_type_number_t i; + log->Printf("Index Mask Port Behavior Flavor", masks[i], ports[i], behaviors[i], flavors[i]); + log->Printf("===== -------- -------- -------- --------"); + for (i=0; i<count; ++i) + log->Printf("[%3u] %8.8x %8.8x %8.8x %8.8x", i, masks[i], ports[i], behaviors[i], flavors[i]); + } + if (err.Fail()) + count = 0; + return err.GetError(); +} + +kern_return_t +MachException::PortInfo::Restore (task_t task) +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_EXCEPTIONS); + if (log && log->GetMask().IsSet(PD_LOG_VERBOSE)) + log->Printf("MachException::PortInfo::Restore (task = 0x%4.4x)", task); + uint32_t i = 0; + Error err; + if (count > 0) + { + for (i = 0; i < count; i++) + { + err = ::task_set_exception_ports (task, masks[i], ports[i], behaviors[i], flavors[i]); + if (log || err.Fail()) + err.PutToLog(log, "::task_set_exception_ports ( task = 0x%4.4x, exception_mask = 0x%8.8x, new_port = 0x%4.4x, behavior = 0x%8.8x, new_flavor = 0x%8.8x )", task, masks[i], ports[i], behaviors[i], flavors[i]); + + if (err.Fail()) + break; + } + } + count = 0; + return err.GetError(); +} + +const char * +MachException::Name(exception_type_t exc_type) +{ + switch (exc_type) + { + case EXC_BAD_ACCESS: return "EXC_BAD_ACCESS"; + case EXC_BAD_INSTRUCTION: return "EXC_BAD_INSTRUCTION"; + case EXC_ARITHMETIC: return "EXC_ARITHMETIC"; + case EXC_EMULATION: return "EXC_EMULATION"; + case EXC_SOFTWARE: return "EXC_SOFTWARE"; + case EXC_BREAKPOINT: return "EXC_BREAKPOINT"; + case EXC_SYSCALL: return "EXC_SYSCALL"; + case EXC_MACH_SYSCALL: return "EXC_MACH_SYSCALL"; + case EXC_RPC_ALERT: return "EXC_RPC_ALERT"; +#ifdef EXC_CRASH + case EXC_CRASH: return "EXC_CRASH"; +#endif + default: + break; + } + return NULL; +} + + + diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.h new file mode 100644 index 00000000000..1f3aeb07b0a --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.h @@ -0,0 +1,148 @@ +//===-- MachException.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_MachException_h_ +#define liblldb_MachException_h_ + +#include <mach/mach.h> +#include <vector> +#include "lldb/lldb-private.h" +#include "lldb/Target/Thread.h" +// TODO: Get the config script to run to this plug-in +//#include "PDConfig.h" +#define HAVE_64_BIT_MACH_EXCEPTIONS // REMOVE THIS WHEN PDConfig.h is included above +#ifdef HAVE_64_BIT_MACH_EXCEPTIONS + +#define MACH_EXCEPTION_DATA_FMT_DEC "%lld" +#define MACH_EXCEPTION_DATA_FMT_HEX "0x%16.16llx" +#define MACH_EXCEPTION_DATA_FMT_MINHEX "0x%llx" + +#else + +#define MACH_EXCEPTION_DATA_FMT_DEC "%d" +#define MACH_EXCEPTION_DATA_FMT_HEX "0x%8.8x" +#define MACH_EXCEPTION_DATA_FMT_MINHEX "0x%x" + +#endif + +class MachProcess; + +typedef union MachMessageTag +{ + mach_msg_header_t hdr; + char data[1024]; +} MachMessage; + + +class MachException +{ +public: + + struct PortInfo + { + exception_mask_t masks[EXC_TYPES_COUNT]; + mach_port_t ports[EXC_TYPES_COUNT]; + exception_behavior_t behaviors[EXC_TYPES_COUNT]; + thread_state_flavor_t flavors[EXC_TYPES_COUNT]; + mach_msg_type_number_t count; + + PortInfo(); + kern_return_t Save(task_t task); + kern_return_t Restore(task_t task); + }; + + struct Data + { + task_t task_port; + lldb::tid_t thread_port; + exception_type_t exc_type; + std::vector<mach_exception_data_type_t> exc_data; + Data() : + task_port(TASK_NULL), + thread_port(THREAD_NULL), + exc_type(0), + exc_data() + { + } + + void Clear() + { + task_port = TASK_NULL; + thread_port = THREAD_NULL; + exc_type = 0; + exc_data.clear(); + } + bool IsValid() const + { + return task_port != TASK_NULL && + thread_port != THREAD_NULL && + exc_type != 0; + } + // Return the SoftSignal for this MachException data, or zero if there is none + int SoftSignal() const + { + if (exc_type == EXC_SOFTWARE && exc_data.size() == 2 && exc_data[0] == EXC_SOFT_SIGNAL) + return exc_data[1]; + return LLDB_INVALID_SIGNAL_NUMBER; + } + bool IsBreakpoint() const + { + return (exc_type == EXC_BREAKPOINT) || ((exc_type == EXC_SOFTWARE) && exc_data[0] == 1); + } + void PutToLog(lldb_private::Log *log) const; + void DumpStopReason() const; + bool GetStopInfo(lldb_private::Thread::StopInfo *stop_info) const; + }; + + struct Message + { + MachMessage exc_msg; + MachMessage reply_msg; + Data state; + + Message() : + exc_msg(), + reply_msg(), + state() + { + memset(&exc_msg, 0, sizeof(exc_msg)); + memset(&reply_msg, 0, sizeof(reply_msg)); + } + bool CatchExceptionRaise(); + void PutToLog(lldb_private::Log *log) const; + kern_return_t Reply (task_t task, pid_t pid, int signal); + kern_return_t Receive( mach_port_t receive_port, + mach_msg_option_t options, + mach_msg_timeout_t timeout, + mach_port_t notify_port = MACH_PORT_NULL); + + typedef std::vector<Message> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + }; + + enum + { + e_actionForward, // Forward signal to inferior process + e_actionStop, // Stop when this signal is received + }; + struct Action + { + task_t task_port; // Set to TASK_NULL for any TASK + lldb::tid_t thread_port; // Set to THREAD_NULL for any thread + exception_type_t exc_mask; // Mach exception mask to watch for + std::vector<mach_exception_data_type_t> exc_data_mask; // Mask to apply to exception data, or empty to ignore exc_data value for exception + std::vector<mach_exception_data_type_t> exc_data_value; // Value to compare to exception data after masking, or empty to ignore exc_data value for exception + uint8_t flags; // Action flags describing what to do with the exception + }; + static const char *Name(exception_type_t exc_type); +}; + +#endif diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.cpp new file mode 100644 index 00000000000..1a0c3c48997 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.cpp @@ -0,0 +1,674 @@ +//===-- MachTask.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MachTask.h" + +// C Includes +// C++ Includes + +// Other libraries and framework includes +#if defined (__arm__) + +#include <CoreFoundation/CoreFoundation.h> +#include <SpringBoardServices/SpringBoardServer.h> +#include <SpringBoardServices/SBSWatchdogAssertion.h> + +#endif + +#include "lldb/Host/Host.h" +#include "lldb/Core/DataExtractor.h" + +// Project includes +#include "ProcessMacOSX.h" +#include "ProcessMacOSXLog.h" + + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// MachTask constructor +//---------------------------------------------------------------------- +MachTask::MachTask(ProcessMacOSX *process) : + m_process (process), + m_task (TASK_NULL), + m_vm_memory (), + m_exception_thread (0), + m_exception_port (MACH_PORT_NULL), + m_exc_port_info() +{ + memset(&m_exc_port_info, 0, sizeof(m_exc_port_info)); + +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +MachTask::~MachTask() +{ + Clear(); +} + + +//---------------------------------------------------------------------- +// MachTask::Suspend +//---------------------------------------------------------------------- +kern_return_t +MachTask::Suspend() +{ + Error err; + task_t task = GetTaskPort(); + err = ::task_suspend (task); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_TASK); + if (log || err.Fail()) + err.PutToLog(log, "::task_suspend ( target_task = 0x%4.4x )", task); + return err.GetError(); +} + + +//---------------------------------------------------------------------- +// MachTask::Resume +//---------------------------------------------------------------------- +kern_return_t +MachTask::Resume() +{ + Error err; + task_t task = GetTaskPort(); + err = ::task_resume (task); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_TASK); + if (log || err.Fail()) + err.PutToLog(log, "::task_resume ( target_task = 0x%4.4x )", task); + return err.GetError(); +} + +int32_t +MachTask::GetSuspendCount () const +{ + struct task_basic_info task_info; + if (BasicInfo(&task_info) == KERN_SUCCESS) + return task_info.suspend_count; + return -1; +} + +//---------------------------------------------------------------------- +// MachTask::ExceptionPort +//---------------------------------------------------------------------- +mach_port_t +MachTask::ExceptionPort() const +{ + return m_exception_port; +} + +//---------------------------------------------------------------------- +// MachTask::ExceptionPortIsValid +//---------------------------------------------------------------------- +bool +MachTask::ExceptionPortIsValid() const +{ + return MACH_PORT_VALID(m_exception_port); +} + + +//---------------------------------------------------------------------- +// MachTask::Clear +//---------------------------------------------------------------------- +void +MachTask::Clear() +{ + // Do any cleanup needed for this task + m_task = TASK_NULL; + m_exception_thread = 0; + m_exception_port = MACH_PORT_NULL; + +} + + +//---------------------------------------------------------------------- +// MachTask::SaveExceptionPortInfo +//---------------------------------------------------------------------- +kern_return_t +MachTask::SaveExceptionPortInfo() +{ + return m_exc_port_info.Save(GetTaskPort()); +} + +//---------------------------------------------------------------------- +// MachTask::RestoreExceptionPortInfo +//---------------------------------------------------------------------- +kern_return_t +MachTask::RestoreExceptionPortInfo() +{ + return m_exc_port_info.Restore(GetTaskPort()); +} + + +//---------------------------------------------------------------------- +// MachTask::ReadMemory +//---------------------------------------------------------------------- +size_t +MachTask::ReadMemory (lldb::addr_t addr, void *buf, size_t size, Error& error) +{ + size_t n = 0; + task_t task = GetTaskPort(); + if (task != TASK_NULL) + { + n = m_vm_memory.Read(task, addr, buf, size, error); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY); + if (log) + { + log->Printf ("MachTask::ReadMemory ( addr = 0x%16.16llx, size = %zu, buf = %8.8p) => %u bytes read", (uint64_t)addr, size, buf, n); + if (log->GetMask().IsSet(PD_LOG_MEMORY_DATA_LONG) || (log->GetMask().IsSet(PD_LOG_MEMORY_DATA_SHORT) && size <= 8)) + { + DataExtractor data((uint8_t*)buf, n, eByteOrderHost, 4); + data.PutToLog(log, 0, n, addr, 16, DataExtractor::TypeUInt8); + } + } + } + return n; +} + + +//---------------------------------------------------------------------- +// MachTask::WriteMemory +//---------------------------------------------------------------------- +size_t +MachTask::WriteMemory (lldb::addr_t addr, const void *buf, size_t size, Error& error) +{ + size_t n = 0; + task_t task = GetTaskPort(); + if (task != TASK_NULL) + { + n = m_vm_memory.Write(task, addr, buf, size, error); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY); + if (log) + { + log->Printf ("MachTask::WriteMemory ( addr = 0x%16.16llx, size = %zu, buf = %8.8p) => %u bytes written", (uint64_t)addr, size, buf, n); + if (log->GetMask().IsSet(PD_LOG_MEMORY_DATA_LONG) || (log->GetMask().IsSet(PD_LOG_MEMORY_DATA_SHORT) && size <= 8)) + { + DataExtractor data((uint8_t*)buf, n, eByteOrderHost, 4); + data.PutToLog(log, 0, n, addr, 16, DataExtractor::TypeUInt8); + } + } + } + return n; +} + +//---------------------------------------------------------------------- +// MachTask::AllocateMemory +//---------------------------------------------------------------------- +lldb::addr_t +MachTask::AllocateMemory (size_t size, uint32_t permissions, Error& error) +{ + // FIXME: vm_allocate allocates a page at a time, so we should use + // host_page_size to get the host page size and then parcel out the + // page we get back until it is filled. + // FIXME: Add log messages. + + kern_return_t kret; + mach_vm_address_t addr; + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY); + + kret = ::mach_vm_allocate (GetTaskPort(), &addr, size, TRUE); + if (kret == KERN_SUCCESS) + { + // Set the protections: + vm_prot_t mach_prot = 0; + if (permissions & lldb::ePermissionsReadable) + mach_prot |= VM_PROT_READ; + if (permissions & lldb::ePermissionsWritable) + mach_prot |= VM_PROT_WRITE; + if (permissions & lldb::ePermissionsExecutable) + mach_prot |= VM_PROT_EXECUTE; + + kret = ::mach_vm_protect (GetTaskPort(), addr, size, 0, mach_prot); + if (kret == KERN_SUCCESS) + { + if (log) + log->Printf("Allocated memory at addr = 0x%16.16llx, size = %zu, prot = 0x%x)", (uint64_t) addr, size, mach_prot); + m_allocations.insert (std::make_pair(addr, size)); + return (lldb::addr_t) addr; + } + else + { + if (log) + log->Printf("Failed to set protections on memory at addr = 0x%16.16llx, size = %zu), prot = 0x%x", (uint64_t) addr, size, mach_prot); + kret = ::mach_vm_deallocate (GetTaskPort(), addr, size); + return LLDB_INVALID_ADDRESS; + } + } + else + { + if (log) + log->Printf("Failed to set allocate memory: size = %zu)", size); + return LLDB_INVALID_ADDRESS; + } +} + +//---------------------------------------------------------------------- +// MachTask::DeallocateMemory +//---------------------------------------------------------------------- +Error +MachTask::DeallocateMemory (lldb::addr_t ptr) +{ + Error error; + // We have to stash away sizes for the allocations... + allocation_collection::iterator pos, end = m_allocations.end(); + for (pos = m_allocations.begin(); pos != end; pos++) + { + if ((*pos).first == ptr) + { + m_allocations.erase (pos); + error = ::mach_vm_deallocate (GetTaskPort(), (vm_address_t) ptr, (*pos).second); + return error; + } + } + error.SetErrorStringWithFormat("no memory allocated at 0x%llx", (uint64_t)ptr); + return error; +} + +//---------------------------------------------------------------------- +// MachTask::TaskPortForProcessID +//---------------------------------------------------------------------- +task_t +MachTask::GetTaskPortForProcessID (Error &err) +{ + err.Clear(); + if (m_task == TASK_NULL && m_process != NULL) + m_task = MachTask::GetTaskPortForProcessID(m_process->GetID(), err); + return m_task; +} + +//---------------------------------------------------------------------- +// MachTask::TaskPortForProcessID +//---------------------------------------------------------------------- +task_t +MachTask::GetTaskPortForProcessID (lldb::pid_t pid, Error &err) +{ + task_t task = TASK_NULL; + if (pid != LLDB_INVALID_PROCESS_ID) + { + mach_port_t task_self = mach_task_self (); + err = ::task_for_pid ( task_self, pid, &task); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_TASK); + if (log || err.Fail()) + { + err.PutToLog(log, "::task_for_pid ( target_tport = 0x%4.4x, pid = %d, task => 0x%4.4x ) %u/%u %u/%u", task_self, pid, task, getuid(), geteuid(), getgid(), getegid()); + } + } + return task; +} + + +//---------------------------------------------------------------------- +// MachTask::BasicInfo +//---------------------------------------------------------------------- +kern_return_t +MachTask::BasicInfo(struct task_basic_info *info) const +{ + return BasicInfo (GetTaskPort(), info); +} + +//---------------------------------------------------------------------- +// MachTask::BasicInfo +//---------------------------------------------------------------------- +kern_return_t +MachTask::BasicInfo(task_t task, struct task_basic_info *info) +{ + if (info == NULL) + return KERN_INVALID_ARGUMENT; + + Error err; + mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT; + err = ::task_info (task, TASK_BASIC_INFO, (task_info_t)info, &count); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_TASK); + if (log || err.Fail()) + err.PutToLog(log, "::task_info ( target_task = 0x%4.4x, flavor = TASK_BASIC_INFO, task_info_out => %p, task_info_outCnt => %u )", task, info, count); + if (log && log->GetMask().IsSet(PD_LOG_VERBOSE) && err.Success()) + { + float user = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f; + float system = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f; + log->Printf ("task_basic_info = { suspend_count = %i, virtual_size = 0x%8.8x, resident_size = 0x%8.8x, user_time = %f, system_time = %f }", + info->suspend_count, info->virtual_size, info->resident_size, user, system); + } + return err.GetError(); +} + + +//---------------------------------------------------------------------- +// MachTask::IsValid +// +// Returns true if a task is a valid task port for a current process. +//---------------------------------------------------------------------- +bool +MachTask::IsValid () const +{ + return MachTask::IsValid(GetTaskPort()); +} + +//---------------------------------------------------------------------- +// MachTask::IsValid +// +// Returns true if a task is a valid task port for a current process. +//---------------------------------------------------------------------- +bool +MachTask::IsValid (task_t task) +{ + if (task != TASK_NULL) + { + struct task_basic_info task_info; + return BasicInfo(task, &task_info) == KERN_SUCCESS; + } + return false; +} + + +bool +MachTask::StartExceptionThread(Error &err) +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_EXCEPTIONS); + + if (log) + log->Printf ("MachTask::%s ( )", __FUNCTION__); + task_t task = GetTaskPortForProcessID(err); + if (MachTask::IsValid(task)) + { + // Got the mach port for the current process + mach_port_t task_self = mach_task_self (); + + // Allocate an exception port that we will use to track our child process + err = ::mach_port_allocate (task_self, MACH_PORT_RIGHT_RECEIVE, &m_exception_port); + if (log || err.Fail()) + err.PutToLog(log, "::mach_port_allocate (task_self=0x%4.4x, MACH_PORT_RIGHT_RECEIVE, &m_exception_port => 0x%4.4x)", + task_self, m_exception_port); + if (err.Fail()) + return false; + + // Add the ability to send messages on the new exception port + err = ::mach_port_insert_right (task_self, m_exception_port, m_exception_port, MACH_MSG_TYPE_MAKE_SEND); + if (log || err.Fail()) + err.PutToLog(log, "::mach_port_insert_right (task_self=0x%4.4x, m_exception_port=0x%4.4x, m_exception_port=0x%4.4x, MACH_MSG_TYPE_MAKE_SEND)", + task_self, m_exception_port, m_exception_port); + if (err.Fail()) + return false; + + // Save the original state of the exception ports for our child process + err = SaveExceptionPortInfo(); + + // Set the ability to get all exceptions on this port + err = ::task_set_exception_ports (task, EXC_MASK_ALL, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE); + if (log || err.Fail()) + err.PutToLog(log, "::task_set_exception_ports (task, EXC_MASK_ALL, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE)"); + if (err.Fail()) + return false; + + // Create the exception thread + char thread_name[256]; + ::snprintf (thread_name, sizeof(thread_name), "<lldb.process.process-macosx.mach-exception-%d>", m_process->GetID()); + m_exception_thread = Host::ThreadCreate (thread_name, MachTask::ExceptionThread, this, &err); + + return err.Success(); + } + return false; +} + +kern_return_t +MachTask::ShutDownExceptionThread() +{ + Error err; + + if (m_exception_thread == NULL) + return KERN_SUCCESS; + + err = RestoreExceptionPortInfo(); + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_EXCEPTIONS); + + // NULL our our exception port and let our exception thread exit + mach_port_t exception_port = m_exception_port; + m_exception_port = NULL; + + Host::ThreadCancel (m_exception_thread, &err); + if (log || err.Fail()) + err.PutToLog(log, "Host::ThreadCancel ( thread = %p )", m_exception_thread); + + Host::ThreadJoin (m_exception_thread, NULL, &err); + if (log || err.Fail()) + err.PutToLog(log, "Host::ThreadJoin ( thread = %p, result_ptr = NULL)", m_exception_thread); + + // Deallocate our exception port that we used to track our child process + mach_port_t task_self = mach_task_self (); + err = ::mach_port_deallocate (task_self, exception_port); + if (log || err.Fail()) + err.PutToLog(log, "::mach_port_deallocate ( task = 0x%4.4x, name = 0x%4.4x )", task_self, exception_port); + exception_port = NULL; + + Clear(); + return err.GetError(); +} + + +void * +MachTask::ExceptionThread (void *arg) +{ + if (arg == NULL) + return NULL; + + MachTask *mach_task = (MachTask*) arg; + ProcessMacOSX *mach_proc = mach_task->Process(); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_EXCEPTIONS); + if (log) + log->Printf ("MachTask::%s (arg = %p) thread starting...", __FUNCTION__, arg); + + // We keep a count of the number of consecutive exceptions received so + // we know to grab all exceptions without a timeout. We do this to get a + // bunch of related exceptions on our exception port so we can process + // then together. When we have multiple threads, we can get an exception + // per thread and they will come in consecutively. The main loop in this + // thread can stop periodically if needed to service things related to this + // process. + // flag set in the options, so we will wait forever for an exception on + // our exception port. After we get one exception, we then will use the + // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current + // exceptions for our process. After we have received the last pending + // exception, we will get a timeout which enables us to then notify + // our main thread that we have an exception bundle avaiable. We then wait + // for the main thread to tell this exception thread to start trying to get + // exceptions messages again and we start again with a mach_msg read with + // infinite timeout. + uint32_t num_exceptions_received = 0; + Error err; + task_t task = mach_task->GetTaskPort(); + mach_msg_timeout_t periodic_timeout = 1000; + +#if defined (__arm__) + mach_msg_timeout_t watchdog_elapsed = 0; + mach_msg_timeout_t watchdog_timeout = 60 * 1000; + lldb::pid_t pid = mach_proc->GetID(); + CFReleaser<SBSWatchdogAssertionRef> watchdog; + + if (mach_proc->ProcessUsingSpringBoard()) + { + // Request a renewal for every 60 seconds if we attached using SpringBoard + watchdog.reset(::SBSWatchdogAssertionCreateForPID(NULL, pid, 60)); + if (log) + log->Printf ("::SBSWatchdogAssertionCreateForPID (NULL, %4.4x, 60 ) => %p", pid, watchdog.get()); + + if (watchdog.get()) + { + ::SBSWatchdogAssertionRenew (watchdog.get()); + + CFTimeInterval watchdogRenewalInterval = ::SBSWatchdogAssertionGetRenewalInterval (watchdog.get()); + if (log) + log->Printf ("::SBSWatchdogAssertionGetRenewalInterval ( %p ) => %g seconds", watchdog.get(), watchdogRenewalInterval); + if (watchdogRenewalInterval > 0.0) + { + watchdog_timeout = (mach_msg_timeout_t)watchdogRenewalInterval * 1000; + if (watchdog_timeout > 3000) + watchdog_timeout -= 1000; // Give us a second to renew our timeout + else if (watchdog_timeout > 1000) + watchdog_timeout -= 250; // Give us a quarter of a second to renew our timeout + } + } + if (periodic_timeout == 0 || periodic_timeout > watchdog_timeout) + periodic_timeout = watchdog_timeout; + } +#endif // #if defined (__arm__) + + while (mach_task->ExceptionPortIsValid()) + { + //::pthread_testcancel (); + + MachException::Message exception_message; + + + if (num_exceptions_received > 0) + { + // No timeout, just receive as many exceptions as we can since we already have one and we want + // to get all currently available exceptions for this task + err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, 0); + } + else if (periodic_timeout > 0) + { + // We need to stop periodically in this loop, so try and get a mach message with a valid timeout (ms) + err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, periodic_timeout); + } + else + { + // We don't need to parse all current exceptions or stop periodically, + // just wait for an exception forever. + err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0); + } + + if (err.GetError() == MACH_RCV_INTERRUPTED) + { + // If we have no task port we should exit this thread + if (!mach_task->ExceptionPortIsValid()) + { + if (log) + log->Printf ("thread cancelled..."); + break; + } + + // Make sure our task is still valid + if (MachTask::IsValid(task)) + { + // Task is still ok + if (log) + log->Printf ("interrupted, but task still valid, continuing..."); + continue; + } + else + { + if (log) + log->Printf ("task has exited..."); + mach_proc->SetPrivateState (eStateExited); + // Our task has died, exit the thread. + break; + } + } + else if (err.GetError() == MACH_RCV_TIMED_OUT) + { + if (num_exceptions_received > 0) + { + // We were receiving all current exceptions with a timeout of zero + // it is time to go back to our normal looping mode + num_exceptions_received = 0; + + // Notify our main thread we have a complete exception message + // bundle available. + mach_proc->ExceptionMessageBundleComplete(); + + // in case we use a timeout value when getting exceptions... + // Make sure our task is still valid + if (MachTask::IsValid(task)) + { + // Task is still ok + if (log) + log->Printf ("got a timeout, continuing..."); + continue; + } + else + { + if (log) + log->Printf ("task has exited..."); + mach_proc->SetPrivateState (eStateExited); + // Our task has died, exit the thread. + break; + } + continue; + } + +#if defined (__arm__) + if (watchdog.get()) + { + watchdog_elapsed += periodic_timeout; + if (watchdog_elapsed >= watchdog_timeout) + { + LogIf(PD_LOG_TASK, "SBSWatchdogAssertionRenew ( %p )", watchdog.get()); + ::SBSWatchdogAssertionRenew (watchdog.get()); + watchdog_elapsed = 0; + } + } +#endif + } + else if (err.GetError() != KERN_SUCCESS) + { + if (log) + log->Printf ("got some other error, do something about it??? nah, continuing for now..."); + // TODO: notify of error? + } + else + { + if (exception_message.CatchExceptionRaise()) + { + ++num_exceptions_received; + mach_proc->ExceptionMessageReceived(exception_message); + } + } + } + +#if defined (__arm__) + if (watchdog.get()) + { + // TODO: change SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel when we + // all are up and running on systems that support it. The SBS framework has a #define + // that will forward SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel for now + // so it should still build either way. + LogIf(PD_LOG_TASK, "::SBSWatchdogAssertionRelease(%p)", watchdog.get()); + ::SBSWatchdogAssertionRelease (watchdog.get()); + } +#endif // #if defined (__arm__) + + if (log) + log->Printf ("MachTask::%s (arg = %p) thread exiting...", __FUNCTION__, arg); + return NULL; +} + +lldb::addr_t +MachTask::GetDYLDAllImageInfosAddress () +{ +#ifdef TASK_DYLD_INFO + task_dyld_info_data_t dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + Error err; + // The actual task shouldn't matter for the DYLD info, so lets just use ours + kern_return_t kret = ::task_info (GetTaskPortForProcessID (err), TASK_DYLD_INFO, (task_info_t)&dyld_info, &count); + if (kret == KERN_SUCCESS) + { + // We now have the address of the all image infos structure + return dyld_info.all_image_info_addr; + } +#endif + return LLDB_INVALID_ADDRESS; +} + + + + + diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.h new file mode 100644 index 00000000000..228cb7c516b --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.h @@ -0,0 +1,138 @@ +//===-- MachTask.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __MachTask_h__ +#define __MachTask_h__ + +// C Includes +// C++ Includes +#include <map> +// Other libraries and framework includes +#include <mach/mach.h> +#include <mach/mach_vm.h> +#include <sys/socket.h> + +// Project includes +#include "MachException.h" +#include "MachVMMemory.h" + +class ProcessMacOSX; + +class MachTask +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + MachTask (ProcessMacOSX *process); + + virtual + ~MachTask (); + + void + Clear (); + + kern_return_t + Suspend (); + + kern_return_t + Resume (); + + int32_t + GetSuspendCount () const; + + size_t + ReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error& error); + + size_t + WriteMemory (lldb::addr_t addr, const void *buf, size_t size, lldb_private::Error& error); + + lldb::addr_t + AllocateMemory (size_t size, uint32_t permissions, lldb_private::Error& error); + + lldb_private::Error + DeallocateMemory (lldb::addr_t addr); + + mach_port_t + ExceptionPort () const; + + bool + ExceptionPortIsValid () const; + + kern_return_t + SaveExceptionPortInfo (); + + kern_return_t + RestoreExceptionPortInfo (); + + kern_return_t + ShutDownExceptionThread (); + + bool + StartExceptionThread (lldb_private::Error &err); + + lldb::addr_t + GetDYLDAllImageInfosAddress (); + + kern_return_t + BasicInfo (struct task_basic_info *info) const; + + static kern_return_t + BasicInfo (task_t task, struct task_basic_info *info); + + bool + IsValid () const; + + static bool + IsValid (task_t task); + + static void * + ExceptionThread (void *arg); + + task_t + GetTaskPort () const + { + return m_task; + } + + task_t + GetTaskPortForProcessID (lldb_private::Error &err); + + static task_t + GetTaskPortForProcessID (lldb::pid_t pid, lldb_private::Error &err); + + ProcessMacOSX * + Process () + { + return m_process; + } + + const ProcessMacOSX * + Process () const + { + return m_process; + } + +protected: + ProcessMacOSX * m_process; // The mach process that owns this MachTask + task_t m_task; + MachVMMemory m_vm_memory; // Special mach memory reading class that will take care of watching for page and region boundaries + MachException::PortInfo m_exc_port_info; // Saved settings for all exception ports + lldb::thread_t m_exception_thread; // Thread ID for the exception thread in case we need it + mach_port_t m_exception_port; // Exception port on which we will receive child exceptions + + // Maybe sort this by address and use find? + typedef std::map<vm_address_t,size_t> allocation_collection; + allocation_collection m_allocations; + +private: + DISALLOW_COPY_AND_ASSIGN (MachTask); +}; + +#endif // __MachTask_h__ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext.h new file mode 100644 index 00000000000..3166c2802f3 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext.h @@ -0,0 +1,48 @@ +//===-- MachThreadContext.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_MachThreadContext_h_ +#define liblldb_MachThreadContext_h_ + +#include <vector> + +#include "MachException.h" + +class ThreadMacOSX; + +class MachThreadContext +{ +public: + MachThreadContext (ThreadMacOSX &thread) : + m_thread (thread) + { + } + + virtual ~MachThreadContext() + { + } + + virtual lldb_private::RegisterContext * + CreateRegisterContext (lldb_private::StackFrame *frame) const = 0; + + virtual void InitializeInstance() = 0; + virtual void ThreadWillResume () = 0; + virtual bool ShouldStop () = 0; + virtual void RefreshStateAfterStop() = 0; + virtual bool NotifyException (MachException::Data& exc) { return false; } + virtual bool StepNotComplete () { return false; } + virtual size_t GetStackFrameData(lldb_private::StackFrame *frame, std::vector<std::pair<lldb::addr_t, lldb::addr_t> >& fp_pc_pairs) { return 0; } +// virtual const uint8_t * SoftwareBreakpointOpcode (size_t byte_size) = 0; + +protected: + ThreadMacOSX &m_thread; + +}; + +#endif // #ifndef liblldb_MachThreadContext_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.cpp new file mode 100644 index 00000000000..4f6b17c81a2 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.cpp @@ -0,0 +1,1884 @@ +//===-- MachThreadContext_arm.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MachThreadContext_arm.h" + +#include <sys/sysctl.h> + +#include "ProcessMacOSX.h" +#include "ProcessMacOSXLog.h" +#include "ThreadMacOSX.h" + +using namespace lldb_private; + +//#define DNB_ARCH_MACH_ARM_DEBUG_SW_STEP 1 + +static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 }; +static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE }; + +// ARM constants used during decoding +#define REG_RD 0 +#define LDM_REGLIST 1 +#define PC_REG 15 +#define PC_REGLIST_BIT 0x8000 + +// ARM conditions +#define COND_EQ 0x0 +#define COND_NE 0x1 +#define COND_CS 0x2 +#define COND_HS 0x2 +#define COND_CC 0x3 +#define COND_LO 0x3 +#define COND_MI 0x4 +#define COND_PL 0x5 +#define COND_VS 0x6 +#define COND_VC 0x7 +#define COND_HI 0x8 +#define COND_LS 0x9 +#define COND_GE 0xA +#define COND_LT 0xB +#define COND_GT 0xC +#define COND_LE 0xD +#define COND_AL 0xE +#define COND_UNCOND 0xF + +#define MASK_CPSR_T (1u << 5) +#define MASK_CPSR_J (1u << 24) + +#define MNEMONIC_STRING_SIZE 32 +#define OPERAND_STRING_SIZE 128 + +using namespace lldb; +using namespace lldb_private; + +MachThreadContext_arm::MachThreadContext_arm(ThreadMacOSX &thread) : + MachThreadContext(thread), + m_hw_single_chained_step_addr(LLDB_INVALID_ADDRESS), + m_bvr0_reg (LLDB_INVALID_REGNUM), + m_bcr0_reg (LLDB_INVALID_REGNUM), + m_bvr0_save (0), + m_bcr0_save (0) +{ +} + +MachThreadContext_arm::~MachThreadContext_arm() +{ +} + +RegisterContext * +MachThreadContext_arm::CreateRegisterContext (StackFrame *frame) const +{ + return new RegisterContextMach_arm(m_thread, frame); +} + +// Instance init function +void +MachThreadContext_arm::InitializeInstance() +{ + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + assert (reg_ctx != NULL); + const RegisterInfo * reg_info; + reg_info = reg_ctx->GetRegisterInfoByName ("bvr0"); + if (reg_info) + m_bvr0_reg = reg_info->reg; + + reg_info = reg_ctx->GetRegisterInfoByName ("bcr0"); + if (reg_info) + m_bcr0_reg = reg_info->reg; +} + + + +uint32_t +MachThreadContext_arm::GetCPUType() +{ + return CPU_TYPE_ARM; +} + +void +MachThreadContext_arm::ThreadWillResume() +{ + // Do we need to step this thread? If so, let the mach thread tell us so. + if (m_thread.GetState() == eStateStepping) + { + bool step_handled = false; + // This is the primary thread, let the arch do anything it needs + if (m_thread.GetRegisterContext()->NumSupportedHardwareBreakpoints() > 0) + { +#if defined (DNB_ARCH_MACH_ARM_DEBUG_SW_STEP) + bool half_step = m_hw_single_chained_step_addr != LLDB_INVALID_ADDRESS; +#endif + step_handled = EnableHardwareSingleStep(true) == KERN_SUCCESS; +#if defined (DNB_ARCH_MACH_ARM_DEBUG_SW_STEP) + if (!half_step) + step_handled = false; +#endif + } + +#if defined (ENABLE_ARM_SINGLE_STEP) + if (!step_handled) + { + SetSingleStepSoftwareBreakpoints(); + } +#endif + } +} + +bool +MachThreadContext_arm::ShouldStop () +{ + return true; +} + +void +MachThreadContext_arm::RefreshStateAfterStop () +{ + EnableHardwareSingleStep (false) == KERN_SUCCESS; +} + +#if defined (ENABLE_ARM_SINGLE_STEP) + +bool +MachThreadContext_arm::ShouldStop () +{ + return true; +} + +bool +MachThreadContext_arm::RefreshStateAfterStop () +{ + success = EnableHardwareSingleStep(false) == KERN_SUCCESS; + + bool success = true; + + m_state.InvalidateRegisterSet (GPRRegSet); + m_state.InvalidateRegisterSet (VFPRegSet); + m_state.InvalidateRegisterSet (EXCRegSet); + + // Are we stepping a single instruction? + if (ReadGPRRegisters(true) == KERN_SUCCESS) + { + // We are single stepping, was this the primary thread? + if (m_thread.GetState() == eStateStepping) + { +#if defined (DNB_ARCH_MACH_ARM_DEBUG_SW_STEP) + success = EnableHardwareSingleStep(false) == KERN_SUCCESS; + // Hardware single step must work if we are going to test software + // single step functionality + assert(success); + if (m_hw_single_chained_step_addr == LLDB_INVALID_ADDRESS && m_sw_single_step_next_pc != LLDB_INVALID_ADDRESS) + { + uint32_t sw_step_next_pc = m_sw_single_step_next_pc & 0xFFFFFFFEu; + bool sw_step_next_pc_is_thumb = (m_sw_single_step_next_pc & 1) != 0; + bool actual_next_pc_is_thumb = (m_state.gpr.__cpsr & 0x20) != 0; + if (m_state.gpr.r[15] != sw_step_next_pc) + { + LogError("curr pc = 0x%8.8x - calculated single step target PC was incorrect: 0x%8.8x != 0x%8.8x", m_state.gpr.r[15], sw_step_next_pc, m_state.gpr.r[15]); + exit(1); + } + if (actual_next_pc_is_thumb != sw_step_next_pc_is_thumb) + { + LogError("curr pc = 0x%8.8x - calculated single step calculated mode mismatch: sw single mode = %s != %s", + m_state.gpr.r[15], + actual_next_pc_is_thumb ? "Thumb" : "ARM", + sw_step_next_pc_is_thumb ? "Thumb" : "ARM"); + exit(1); + } + m_sw_single_step_next_pc = LLDB_INVALID_ADDRESS; + } +#else +#if defined (ENABLE_ARM_SINGLE_STEP) + // Are we software single stepping? + if (LLDB_BREAK_ID_IS_VALID(m_sw_single_step_break_id) || m_sw_single_step_itblock_break_count) + { + // Remove any software single stepping breakpoints that we have set + + // Do we have a normal software single step breakpoint? + if (LLDB_BREAK_ID_IS_VALID(m_sw_single_step_break_id)) + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: removing software single step breakpoint (breakID=%d)", __FUNCTION__, m_sw_single_step_break_id); + success = m_thread.Process()->DisableBreakpoint(m_sw_single_step_break_id, true); + m_sw_single_step_break_id = LLDB_INVALID_BREAK_ID; + } + + // Do we have any Thumb IT breakpoints? + if (m_sw_single_step_itblock_break_count > 0) + { + // See if we hit one of our Thumb IT breakpoints? + DNBBreakpoint *step_bp = m_thread.Process()->Breakpoints().FindByAddress(m_state.gpr.r[15]); + + if (step_bp) + { + // We did hit our breakpoint, tell the breakpoint it was + // hit so that it can run its callback routine and fixup + // the PC. + ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: IT software single step breakpoint hit (breakID=%u)", __FUNCTION__, step_bp->GetID()); + step_bp->BreakpointHit(m_thread.Process()->ProcessID(), m_thread.GetID()); + } + + // Remove all Thumb IT breakpoints + for (int i = 0; i < m_sw_single_step_itblock_break_count; i++) + { + if (LLDB_BREAK_ID_IS_VALID(m_sw_single_step_itblock_break_id[i])) + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: removing IT software single step breakpoint (breakID=%d)", __FUNCTION__, m_sw_single_step_itblock_break_id[i]); + success = m_thread.Process()->DisableBreakpoint(m_sw_single_step_itblock_break_id[i], true); + m_sw_single_step_itblock_break_id[i] = LLDB_INVALID_BREAK_ID; + } + } + m_sw_single_step_itblock_break_count = 0; + + // Decode instructions up to the current PC to ensure the internal decoder state is valid for the IT block + // The decoder has to decode each instruction in the IT block even if it is not executed so that + // the fields are correctly updated + DecodeITBlockInstructions(m_state.gpr.r[15]); + } + + } + else +#endif + success = EnableHardwareSingleStep(false) == KERN_SUCCESS; +#endif + } + else + { + // The MachThread will automatically restore the suspend count + // in ShouldStop (), so we don't need to do anything here if + // we weren't the primary thread the last time + } + } + return; +} + + + +bool +MachThreadContext_arm::StepNotComplete () +{ + if (m_hw_single_chained_step_addr != LLDB_INVALID_ADDRESS) + { + kern_return_t kret = KERN_INVALID_ARGUMENT; + kret = ReadGPRRegisters(false); + if (kret == KERN_SUCCESS) + { + if (m_state.gpr.r[15] == m_hw_single_chained_step_addr) + { + //ProcessMacOSXLog::LogIf(PD_LOG_STEP, "Need to step some more at 0x%8.8x", m_hw_single_chained_step_addr); + return true; + } + } + } + + m_hw_single_chained_step_addr = LLDB_INVALID_ADDRESS; + return false; +} + + +void +MachThreadContext_arm::DecodeITBlockInstructions(lldb::addr_t curr_pc) + +{ + uint16_t opcode16; + uint32_t opcode32; + lldb::addr_t next_pc_in_itblock; + lldb::addr_t pc_in_itblock = m_last_decode_pc; + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: last_decode_pc=0x%8.8x", __FUNCTION__, m_last_decode_pc); + + // Decode IT block instruction from the instruction following the m_last_decoded_instruction at + // PC m_last_decode_pc upto and including the instruction at curr_pc + if (m_thread.Process()->Task().ReadMemory(pc_in_itblock, 2, &opcode16) == 2) + { + opcode32 = opcode16; + pc_in_itblock += 2; + // Check for 32 bit thumb opcode and read the upper 16 bits if needed + if (((opcode32 & 0xE000) == 0xE000) && opcode32 & 0x1800) + { + // Adjust 'next_pc_in_itblock' to point to the default next Thumb instruction for + // a 32 bit Thumb opcode + // Read bits 31:16 of a 32 bit Thumb opcode + if (m_thread.Process()->Task().ReadMemory(pc_in_itblock, 2, &opcode16) == 2) + { + pc_in_itblock += 2; + // 32 bit thumb opcode + opcode32 = (opcode32 << 16) | opcode16; + } + else + { + LogError("%s: Unable to read opcode bits 31:16 for a 32 bit thumb opcode at pc=0x%8.8lx", __FUNCTION__, pc_in_itblock); + } + } + } + else + { + LogError("%s: Error reading 16-bit Thumb instruction at pc=0x%8.8x", __FUNCTION__, pc_in_itblock); + } + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: pc_in_itblock=0x%8.8x, curr_pc=0x%8.8x", __FUNCTION__, pc_in_itblock, curr_pc); + + next_pc_in_itblock = pc_in_itblock; + while (next_pc_in_itblock <= curr_pc) + { + arm_error_t decodeError; + + m_last_decode_pc = pc_in_itblock; + decodeError = DecodeInstructionUsingDisassembler(pc_in_itblock, m_state.gpr.__cpsr, &m_last_decode_arm, &m_last_decode_thumb, &next_pc_in_itblock); + + pc_in_itblock = next_pc_in_itblock; + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: next_pc_in_itblock=0x%8.8x", __FUNCTION__, next_pc_in_itblock); + } +} + +#endif + +// Set the single step bit in the processor status register. +kern_return_t +MachThreadContext_arm::EnableHardwareSingleStep (bool enable) +{ + Error err; + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_STEP); + + if (log) log->Printf("%s( enable = %d )", __FUNCTION__, enable); + + if (m_bvr0_reg == LLDB_INVALID_REGNUM || m_bcr0_reg == LLDB_INVALID_REGNUM) + return KERN_INVALID_ARGUMENT; + + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + uint32_t bvr = 0; + uint32_t bcr = 0; + + const uint32_t i = 0; + if (enable) + { + m_hw_single_chained_step_addr = LLDB_INVALID_ADDRESS; + + // Save our previous state + m_bvr0_save = reg_ctx->ReadRegisterAsUnsigned(m_bvr0_reg, 0); + m_bcr0_save = reg_ctx->ReadRegisterAsUnsigned(m_bcr0_reg, 0); + lldb::addr_t pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS); + lldb::addr_t cpsr = reg_ctx->GetFlags(0); + if (pc == LLDB_INVALID_ADDRESS) + return KERN_INVALID_ARGUMENT; + + // Set a breakpoint that will stop when the PC doesn't match the current one! + bvr = pc & 0xFFFFFFFCu; // Set the current PC as the breakpoint address + bcr = BCR_M_IMVA_MISMATCH | // Stop on address mismatch + S_USER | // Stop only in user mode + BCR_ENABLE; // Enable this breakpoint + if (cpsr & 0x20) + { + // Thumb breakpoint + if (pc & 2) + bcr |= BAS_IMVA_2_3; + else + bcr |= BAS_IMVA_0_1; + + uint16_t opcode; + Error error; + if (sizeof(opcode) == m_thread.GetProcess().ReadMemory(pc, &opcode, sizeof(opcode), error)) + { + if (((opcode & 0xE000) == 0xE000) && opcode & 0x1800) + { + // 32 bit thumb opcode... + if (pc & 2) + { + // We can't take care of a 32 bit thumb instruction single step + // with just IVA mismatching. We will need to chain an extra + // hardware single step in order to complete this single step... + m_hw_single_chained_step_addr = pc + 2; + } + else + { + // Extend the number of bits to ignore for the mismatch + bcr |= BAS_IMVA_ALL; + } + } + } + } + else + { + // ARM breakpoint + bcr |= BAS_IMVA_ALL; // Stop when any address bits change + } + + ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: BVR%u=0x%8.8x BCR%u=0x%8.8x", __FUNCTION__, i, bvr, i, bcr); + + m_bvr0_save = reg_ctx->ReadRegisterAsUnsigned(m_bvr0_reg, 0); + m_bcr0_save = reg_ctx->ReadRegisterAsUnsigned(m_bcr0_reg, 0); + +// for (uint32_t j=i+1; j<16; ++j) +// { +// // Disable all others +// m_state.dbg.bvr[j] = 0; +// m_state.dbg.bcr[j] = 0; +// } + } + else + { + // Just restore the state we had before we did single stepping + bvr = m_bvr0_save; + bcr = m_bcr0_save; + } + + if (reg_ctx->WriteRegisterFromUnsigned(m_bvr0_reg, bvr) && + reg_ctx->WriteRegisterFromUnsigned(m_bcr0_reg, bcr)) + return KERN_SUCCESS; + + return KERN_INVALID_ARGUMENT; +} + +#if defined (ENABLE_ARM_SINGLE_STEP) + +// return 1 if bit "BIT" is set in "value" +static inline uint32_t bit(uint32_t value, uint32_t bit) +{ + return (value >> bit) & 1u; +} + +// return the bitfield "value[msbit:lsbit]". +static inline uint32_t bits(uint32_t value, uint32_t msbit, uint32_t lsbit) +{ + assert(msbit >= lsbit); + uint32_t shift_left = sizeof(value) * 8 - 1 - msbit; + value <<= shift_left; // shift anything above the msbit off of the unsigned edge + value >>= shift_left + lsbit; // shift it back again down to the lsbit (including undoing any shift from above) + return value; // return our result +} + +bool +MachThreadContext_arm::ConditionPassed(uint8_t condition, uint32_t cpsr) +{ + uint32_t cpsr_n = bit(cpsr, 31); // Negative condition code flag + uint32_t cpsr_z = bit(cpsr, 30); // Zero condition code flag + uint32_t cpsr_c = bit(cpsr, 29); // Carry condition code flag + uint32_t cpsr_v = bit(cpsr, 28); // Overflow condition code flag + + switch (condition) { + case COND_EQ: // (0x0) + if (cpsr_z == 1) return true; + break; + case COND_NE: // (0x1) + if (cpsr_z == 0) return true; + break; + case COND_CS: // (0x2) + if (cpsr_c == 1) return true; + break; + case COND_CC: // (0x3) + if (cpsr_c == 0) return true; + break; + case COND_MI: // (0x4) + if (cpsr_n == 1) return true; + break; + case COND_PL: // (0x5) + if (cpsr_n == 0) return true; + break; + case COND_VS: // (0x6) + if (cpsr_v == 1) return true; + break; + case COND_VC: // (0x7) + if (cpsr_v == 0) return true; + break; + case COND_HI: // (0x8) + if ((cpsr_c == 1) && (cpsr_z == 0)) return true; + break; + case COND_LS: // (0x9) + if ((cpsr_c == 0) || (cpsr_z == 1)) return true; + break; + case COND_GE: // (0xA) + if (cpsr_n == cpsr_v) return true; + break; + case COND_LT: // (0xB) + if (cpsr_n != cpsr_v) return true; + break; + case COND_GT: // (0xC) + if ((cpsr_z == 0) && (cpsr_n == cpsr_v)) return true; + break; + case COND_LE: // (0xD) + if ((cpsr_z == 1) || (cpsr_n != cpsr_v)) return true; + break; + default: + return true; + break; + } + + return false; +} + +bool +MachThreadContext_arm::ComputeNextPC(lldb::addr_t currentPC, arm_decoded_instruction_t decodedInstruction, bool currentPCIsThumb, lldb::addr_t *targetPC) +{ + lldb::addr_t myTargetPC, addressWherePCLives; + lldb::pid_t mypid; + + uint32_t cpsr_c = bit(m_state.gpr.cpsr, 29); // Carry condition code flag + + uint32_t firstOperand=0, secondOperand=0, shiftAmount=0, secondOperandAfterShift=0, immediateValue=0; + uint32_t halfwords=0, baseAddress=0, immediateOffset=0, addressOffsetFromRegister=0, addressOffsetFromRegisterAfterShift; + uint32_t baseAddressIndex=LLDB_INVALID_INDEX32; + uint32_t firstOperandIndex=LLDB_INVALID_INDEX32; + uint32_t secondOperandIndex=LLDB_INVALID_INDEX32; + uint32_t addressOffsetFromRegisterIndex=LLDB_INVALID_INDEX32; + uint32_t shiftRegisterIndex=LLDB_INVALID_INDEX32; + uint16_t registerList16, registerList16NoPC; + uint8_t registerList8; + uint32_t numRegistersToLoad=0; + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: instruction->code=%d", __FUNCTION__, decodedInstruction.instruction->code); + + // Get the following in this switch statement: + // - firstOperand, secondOperand, immediateValue, shiftAmount: For arithmetic, logical and move instructions + // - baseAddress, immediateOffset, shiftAmount: For LDR + // - numRegistersToLoad: For LDM and POP instructions + switch (decodedInstruction.instruction->code) + { + // Arithmetic operations that can change the PC + case ARM_INST_ADC: + case ARM_INST_ADCS: + case ARM_INST_ADD: + case ARM_INST_ADDS: + case ARM_INST_AND: + case ARM_INST_ANDS: + case ARM_INST_ASR: + case ARM_INST_ASRS: + case ARM_INST_BIC: + case ARM_INST_BICS: + case ARM_INST_EOR: + case ARM_INST_EORS: + case ARM_INST_ORR: + case ARM_INST_ORRS: + case ARM_INST_RSB: + case ARM_INST_RSBS: + case ARM_INST_RSC: + case ARM_INST_RSCS: + case ARM_INST_SBC: + case ARM_INST_SBCS: + case ARM_INST_SUB: + case ARM_INST_SUBS: + switch (decodedInstruction.addressMode) + { + case ARM_ADDR_DATA_IMM: + if (decodedInstruction.numOperands != 3) + { + LogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get firstOperand register value (at index=1) + firstOperandIndex = decodedInstruction.op[1].value; // first operand register index + firstOperand = m_state.gpr.r[firstOperandIndex]; + + // Get immediateValue (at index=2) + immediateValue = decodedInstruction.op[2].value; + + break; + + case ARM_ADDR_DATA_REG: + if (decodedInstruction.numOperands != 3) + { + LogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get firstOperand register value (at index=1) + firstOperandIndex = decodedInstruction.op[1].value; // first operand register index + firstOperand = m_state.gpr.r[firstOperandIndex]; + + // Get secondOperand register value (at index=2) + secondOperandIndex = decodedInstruction.op[2].value; // second operand register index + secondOperand = m_state.gpr.r[secondOperandIndex]; + + break; + + case ARM_ADDR_DATA_SCALED_IMM: + if (decodedInstruction.numOperands != 4) + { + LogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get firstOperand register value (at index=1) + firstOperandIndex = decodedInstruction.op[1].value; // first operand register index + firstOperand = m_state.gpr.r[firstOperandIndex]; + + // Get secondOperand register value (at index=2) + secondOperandIndex = decodedInstruction.op[2].value; // second operand register index + secondOperand = m_state.gpr.r[secondOperandIndex]; + + // Get shiftAmount as immediate value (at index=3) + shiftAmount = decodedInstruction.op[3].value; + + break; + + + case ARM_ADDR_DATA_SCALED_REG: + if (decodedInstruction.numOperands != 4) + { + LogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get firstOperand register value (at index=1) + firstOperandIndex = decodedInstruction.op[1].value; // first operand register index + firstOperand = m_state.gpr.r[firstOperandIndex]; + + // Get secondOperand register value (at index=2) + secondOperandIndex = decodedInstruction.op[2].value; // second operand register index + secondOperand = m_state.gpr.r[secondOperandIndex]; + + // Get shiftAmount from register (at index=3) + shiftRegisterIndex = decodedInstruction.op[3].value; // second operand register index + shiftAmount = m_state.gpr.r[shiftRegisterIndex]; + + break; + + case THUMB_ADDR_HR_HR: + if (decodedInstruction.numOperands != 2) + { + LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get firstOperand register value (at index=0) + firstOperandIndex = decodedInstruction.op[0].value; // first operand register index + firstOperand = m_state.gpr.r[firstOperandIndex]; + + // Get secondOperand register value (at index=1) + secondOperandIndex = decodedInstruction.op[1].value; // second operand register index + secondOperand = m_state.gpr.r[secondOperandIndex]; + + break; + + default: + break; + } + break; + + // Logical shifts and move operations that can change the PC + case ARM_INST_LSL: + case ARM_INST_LSLS: + case ARM_INST_LSR: + case ARM_INST_LSRS: + case ARM_INST_MOV: + case ARM_INST_MOVS: + case ARM_INST_MVN: + case ARM_INST_MVNS: + case ARM_INST_ROR: + case ARM_INST_RORS: + case ARM_INST_RRX: + case ARM_INST_RRXS: + // In these cases, the firstOperand is always 0, as if it does not exist + switch (decodedInstruction.addressMode) + { + case ARM_ADDR_DATA_IMM: + if (decodedInstruction.numOperands != 2) + { + LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get immediateValue (at index=1) + immediateValue = decodedInstruction.op[1].value; + + break; + + case ARM_ADDR_DATA_REG: + if (decodedInstruction.numOperands != 2) + { + LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get secondOperand register value (at index=1) + secondOperandIndex = decodedInstruction.op[1].value; // second operand register index + secondOperand = m_state.gpr.r[secondOperandIndex]; + + break; + + case ARM_ADDR_DATA_SCALED_IMM: + if (decodedInstruction.numOperands != 3) + { + LogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get secondOperand register value (at index=1) + secondOperandIndex = decodedInstruction.op[2].value; // second operand register index + secondOperand = m_state.gpr.r[secondOperandIndex]; + + // Get shiftAmount as immediate value (at index=2) + shiftAmount = decodedInstruction.op[2].value; + + break; + + + case ARM_ADDR_DATA_SCALED_REG: + if (decodedInstruction.numOperands != 3) + { + LogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get secondOperand register value (at index=1) + secondOperandIndex = decodedInstruction.op[1].value; // second operand register index + secondOperand = m_state.gpr.r[secondOperandIndex]; + + // Get shiftAmount from register (at index=2) + shiftRegisterIndex = decodedInstruction.op[2].value; // second operand register index + shiftAmount = m_state.gpr.r[shiftRegisterIndex]; + + break; + + case THUMB_ADDR_HR_HR: + if (decodedInstruction.numOperands != 2) + { + LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + if (decodedInstruction.op[0].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + + // Get secondOperand register value (at index=1) + secondOperandIndex = decodedInstruction.op[1].value; // second operand register index + secondOperand = m_state.gpr.r[secondOperandIndex]; + + break; + + default: + break; + } + + break; + + // Simple branches, used to hop around within a routine + case ARM_INST_B: + *targetPC = decodedInstruction.targetPC; // Known targetPC + return true; + break; + + // Branch-and-link, used to call ARM subroutines + case ARM_INST_BL: + *targetPC = decodedInstruction.targetPC; // Known targetPC + return true; + break; + + // Branch-and-link with exchange, used to call opposite-mode subroutines + case ARM_INST_BLX: + if ((decodedInstruction.addressMode == ARM_ADDR_BRANCH_IMM) || + (decodedInstruction.addressMode == THUMB_ADDR_UNCOND)) + { + *targetPC = decodedInstruction.targetPC; // Known targetPC + return true; + } + else // addressMode == ARM_ADDR_BRANCH_REG + { + // Unknown target unless we're branching to the PC itself, + // although this may not work properly with BLX + if (decodedInstruction.op[REG_RD].value == PC_REG) + { + // this should (almost) never happen + *targetPC = decodedInstruction.targetPC; // Known targetPC + return true; + } + + // Get the branch address and return + if (decodedInstruction.numOperands != 1) + { + LogError("Expected 1 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get branch address in register (at index=0) + *targetPC = m_state.gpr.r[decodedInstruction.op[0].value]; + return true; + } + break; + + // Branch with exchange, used to hop to opposite-mode code + // Branch to Jazelle code, used to execute Java; included here since it + // acts just like BX unless the Jazelle unit is active and JPC is + // already loaded into it. + case ARM_INST_BX: + case ARM_INST_BXJ: + // Unknown target unless we're branching to the PC itself, + // although this can never switch to Thumb mode and is + // therefore pretty much useless + if (decodedInstruction.op[REG_RD].value == PC_REG) + { + // this should (almost) never happen + *targetPC = decodedInstruction.targetPC; // Known targetPC + return true; + } + + // Get the branch address and return + if (decodedInstruction.numOperands != 1) + { + LogError("Expected 1 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get branch address in register (at index=0) + *targetPC = m_state.gpr.r[decodedInstruction.op[0].value]; + return true; + break; + + // Compare and branch on zero/non-zero (Thumb-16 only) + // Unusual condition check built into the instruction + case ARM_INST_CBZ: + case ARM_INST_CBNZ: + // Branch address is known at compile time + // Get the branch address and return + if (decodedInstruction.numOperands != 2) + { + LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get branch address as an immediate value (at index=1) + *targetPC = decodedInstruction.op[1].value; + return true; + break; + + // Load register can be used to load PC, usually with a function pointer + case ARM_INST_LDR: + if (decodedInstruction.op[REG_RD].value != PC_REG) + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + switch (decodedInstruction.addressMode) + { + case ARM_ADDR_LSWUB_IMM: + case ARM_ADDR_LSWUB_IMM_PRE: + case ARM_ADDR_LSWUB_IMM_POST: + if (decodedInstruction.numOperands != 3) + { + LogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get baseAddress from register (at index=1) + baseAddressIndex = decodedInstruction.op[1].value; + baseAddress = m_state.gpr.r[baseAddressIndex]; + + // Get immediateOffset (at index=2) + immediateOffset = decodedInstruction.op[2].value; + break; + + case ARM_ADDR_LSWUB_REG: + case ARM_ADDR_LSWUB_REG_PRE: + case ARM_ADDR_LSWUB_REG_POST: + if (decodedInstruction.numOperands != 3) + { + LogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get baseAddress from register (at index=1) + baseAddressIndex = decodedInstruction.op[1].value; + baseAddress = m_state.gpr.r[baseAddressIndex]; + + // Get immediateOffset from register (at index=2) + addressOffsetFromRegisterIndex = decodedInstruction.op[2].value; + addressOffsetFromRegister = m_state.gpr.r[addressOffsetFromRegisterIndex]; + + break; + + case ARM_ADDR_LSWUB_SCALED: + case ARM_ADDR_LSWUB_SCALED_PRE: + case ARM_ADDR_LSWUB_SCALED_POST: + if (decodedInstruction.numOperands != 4) + { + LogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get baseAddress from register (at index=1) + baseAddressIndex = decodedInstruction.op[1].value; + baseAddress = m_state.gpr.r[baseAddressIndex]; + + // Get immediateOffset from register (at index=2) + addressOffsetFromRegisterIndex = decodedInstruction.op[2].value; + addressOffsetFromRegister = m_state.gpr.r[addressOffsetFromRegisterIndex]; + + // Get shiftAmount (at index=3) + shiftAmount = decodedInstruction.op[3].value; + + break; + + default: + break; + } + break; + + // 32b load multiple operations can load the PC along with everything else, + // usually to return from a function call + case ARM_INST_LDMDA: + case ARM_INST_LDMDB: + case ARM_INST_LDMIA: + case ARM_INST_LDMIB: + if (decodedInstruction.op[LDM_REGLIST].value & PC_REGLIST_BIT) + { + if (decodedInstruction.numOperands != 2) + { + LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands); + return false; + } + + // Get baseAddress from register (at index=0) + baseAddressIndex = decodedInstruction.op[0].value; + baseAddress = m_state.gpr.r[baseAddressIndex]; + + // Get registerList from register (at index=1) + registerList16 = (uint16_t)decodedInstruction.op[1].value; + + // Count number of registers to load in the multiple register list excluding the PC + registerList16NoPC = registerList16&0x3FFF; // exclude the PC + numRegistersToLoad=0; + for (int i = 0; i < 16; i++) + { + if (registerList16NoPC & 0x1) numRegistersToLoad++; + registerList16NoPC = registerList16NoPC >> 1; + } + } + else + { + LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value); + return false; + } + break; + + // Normal 16-bit LD multiple can't touch R15, but POP can + case ARM_INST_POP: // Can also get the PC & updates SP + // Get baseAddress from SP (at index=0) + baseAddress = m_state.gpr.__sp; + + if (decodedInstruction.thumb16b) + { + // Get registerList from register (at index=0) + registerList8 = (uint8_t)decodedInstruction.op[0].value; + + // Count number of registers to load in the multiple register list + numRegistersToLoad=0; + for (int i = 0; i < 8; i++) + { + if (registerList8 & 0x1) numRegistersToLoad++; + registerList8 = registerList8 >> 1; + } + } + else + { + // Get registerList from register (at index=0) + registerList16 = (uint16_t)decodedInstruction.op[0].value; + + // Count number of registers to load in the multiple register list excluding the PC + registerList16NoPC = registerList16&0x3FFF; // exclude the PC + numRegistersToLoad=0; + for (int i = 0; i < 16; i++) + { + if (registerList16NoPC & 0x1) numRegistersToLoad++; + registerList16NoPC = registerList16NoPC >> 1; + } + } + break; + + // 16b TBB and TBH instructions load a jump address from a table + case ARM_INST_TBB: + case ARM_INST_TBH: + // Get baseAddress from register (at index=0) + baseAddressIndex = decodedInstruction.op[0].value; + baseAddress = m_state.gpr.r[baseAddressIndex]; + + // Get immediateOffset from register (at index=1) + addressOffsetFromRegisterIndex = decodedInstruction.op[1].value; + addressOffsetFromRegister = m_state.gpr.r[addressOffsetFromRegisterIndex]; + break; + + // ThumbEE branch-to-handler instructions: Jump to handlers at some offset + // from a special base pointer register (which is unknown at disassembly time) + case ARM_INST_HB: + case ARM_INST_HBP: +// TODO: ARM_INST_HB, ARM_INST_HBP + break; + + case ARM_INST_HBL: + case ARM_INST_HBLP: +// TODO: ARM_INST_HBL, ARM_INST_HBLP + break; + + // Breakpoint and software interrupt jump to interrupt handler (always ARM) + case ARM_INST_BKPT: + case ARM_INST_SMC: + case ARM_INST_SVC: + + // Return from exception, obviously modifies PC [interrupt only!] + case ARM_INST_RFEDA: + case ARM_INST_RFEDB: + case ARM_INST_RFEIA: + case ARM_INST_RFEIB: + + // Other instructions either can't change R15 or are "undefined" if you do, + // so no sane compiler should ever generate them & we don't care here. + // Also, R15 can only legally be used in a read-only manner for the + // various ARM addressing mode (to get PC-relative addressing of constants), + // but can NOT be used with any of the update modes. + default: + LogError("%s should not be called for instruction code %d!", __FUNCTION__, decodedInstruction.instruction->code); + return false; + break; + } + + // Adjust PC if PC is one of the input operands + if (baseAddressIndex == PC_REG) + { + if (currentPCIsThumb) + baseAddress += 4; + else + baseAddress += 8; + } + + if (firstOperandIndex == PC_REG) + { + if (currentPCIsThumb) + firstOperand += 4; + else + firstOperand += 8; + } + + if (secondOperandIndex == PC_REG) + { + if (currentPCIsThumb) + secondOperand += 4; + else + secondOperand += 8; + } + + if (addressOffsetFromRegisterIndex == PC_REG) + { + if (currentPCIsThumb) + addressOffsetFromRegister += 4; + else + addressOffsetFromRegister += 8; + } + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, + "%s: firstOperand=%8.8x, secondOperand=%8.8x, immediateValue = %d, shiftAmount = %d, baseAddress = %8.8x, addressOffsetFromRegister = %8.8x, immediateOffset = %d, numRegistersToLoad = %d", + __FUNCTION__, + firstOperand, + secondOperand, + immediateValue, + shiftAmount, + baseAddress, + addressOffsetFromRegister, + immediateOffset, + numRegistersToLoad); + + + // Calculate following values after applying shiftAmount: + // - immediateOffsetAfterShift, secondOperandAfterShift + + switch (decodedInstruction.scaleMode) + { + case ARM_SCALE_NONE: + addressOffsetFromRegisterAfterShift = addressOffsetFromRegister; + secondOperandAfterShift = secondOperand; + break; + + case ARM_SCALE_LSL: // Logical shift left + addressOffsetFromRegisterAfterShift = addressOffsetFromRegister << shiftAmount; + secondOperandAfterShift = secondOperand << shiftAmount; + break; + + case ARM_SCALE_LSR: // Logical shift right + addressOffsetFromRegisterAfterShift = addressOffsetFromRegister >> shiftAmount; + secondOperandAfterShift = secondOperand >> shiftAmount; + break; + + case ARM_SCALE_ASR: // Arithmetic shift right + asm("mov %0, %1, asr %2" : "=r" (addressOffsetFromRegisterAfterShift) : "r" (addressOffsetFromRegister), "r" (shiftAmount)); + asm("mov %0, %1, asr %2" : "=r" (secondOperandAfterShift) : "r" (secondOperand), "r" (shiftAmount)); + break; + + case ARM_SCALE_ROR: // Rotate right + asm("mov %0, %1, ror %2" : "=r" (addressOffsetFromRegisterAfterShift) : "r" (addressOffsetFromRegister), "r" (shiftAmount)); + asm("mov %0, %1, ror %2" : "=r" (secondOperandAfterShift) : "r" (secondOperand), "r" (shiftAmount)); + break; + + case ARM_SCALE_RRX: // Rotate right, pulling in carry (1-bit shift only) + asm("mov %0, %1, rrx" : "=r" (addressOffsetFromRegisterAfterShift) : "r" (addressOffsetFromRegister)); + asm("mov %0, %1, rrx" : "=r" (secondOperandAfterShift) : "r" (secondOperand)); + break; + } + + // Emulate instruction to calculate targetPC + // All branches are already handled in the first switch statement. A branch should not reach this switch + switch (decodedInstruction.instruction->code) + { + // Arithmetic operations that can change the PC + case ARM_INST_ADC: + case ARM_INST_ADCS: + // Add with Carry + *targetPC = firstOperand + (secondOperandAfterShift + immediateValue) + cpsr_c; + break; + + case ARM_INST_ADD: + case ARM_INST_ADDS: + *targetPC = firstOperand + (secondOperandAfterShift + immediateValue); + break; + + case ARM_INST_AND: + case ARM_INST_ANDS: + *targetPC = firstOperand & (secondOperandAfterShift + immediateValue); + break; + + case ARM_INST_ASR: + case ARM_INST_ASRS: + asm("mov %0, %1, asr %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + case ARM_INST_BIC: + case ARM_INST_BICS: + asm("bic %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + case ARM_INST_EOR: + case ARM_INST_EORS: + asm("eor %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + case ARM_INST_ORR: + case ARM_INST_ORRS: + asm("orr %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + case ARM_INST_RSB: + case ARM_INST_RSBS: + asm("rsb %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + case ARM_INST_RSC: + case ARM_INST_RSCS: + myTargetPC = secondOperandAfterShift - (firstOperand + !cpsr_c); + *targetPC = myTargetPC; + break; + + case ARM_INST_SBC: + case ARM_INST_SBCS: + asm("sbc %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue + !cpsr_c)); + *targetPC = myTargetPC; + break; + + case ARM_INST_SUB: + case ARM_INST_SUBS: + asm("sub %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue)); + *targetPC = myTargetPC; + break; + + // Logical shifts and move operations that can change the PC + case ARM_INST_LSL: + case ARM_INST_LSLS: + case ARM_INST_LSR: + case ARM_INST_LSRS: + case ARM_INST_MOV: + case ARM_INST_MOVS: + case ARM_INST_ROR: + case ARM_INST_RORS: + case ARM_INST_RRX: + case ARM_INST_RRXS: + myTargetPC = secondOperandAfterShift + immediateValue; + *targetPC = myTargetPC; + break; + + case ARM_INST_MVN: + case ARM_INST_MVNS: + myTargetPC = !(secondOperandAfterShift + immediateValue); + *targetPC = myTargetPC; + break; + + // Load register can be used to load PC, usually with a function pointer + case ARM_INST_LDR: + switch (decodedInstruction.addressMode) { + case ARM_ADDR_LSWUB_IMM_POST: + case ARM_ADDR_LSWUB_REG_POST: + case ARM_ADDR_LSWUB_SCALED_POST: + addressWherePCLives = baseAddress; + break; + + case ARM_ADDR_LSWUB_IMM: + case ARM_ADDR_LSWUB_REG: + case ARM_ADDR_LSWUB_SCALED: + case ARM_ADDR_LSWUB_IMM_PRE: + case ARM_ADDR_LSWUB_REG_PRE: + case ARM_ADDR_LSWUB_SCALED_PRE: + addressWherePCLives = baseAddress + (addressOffsetFromRegisterAfterShift + immediateOffset); + break; + + default: + break; + } + + mypid = m_thread.ProcessID(); + if (PDProcessMemoryRead(mypid, addressWherePCLives, sizeof(lldb::addr_t), &myTargetPC) != sizeof(lldb::addr_t)) + { + LogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives); + return false; + } + + *targetPC = myTargetPC; + break; + + // 32b load multiple operations can load the PC along with everything else, + // usually to return from a function call + case ARM_INST_LDMDA: + mypid = m_thread.ProcessID(); + addressWherePCLives = baseAddress; + if (PDProcessMemoryRead(mypid, addressWherePCLives, sizeof(lldb::addr_t), &myTargetPC) != sizeof(lldb::addr_t)) + { + LogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives); + return false; + } + + *targetPC = myTargetPC; + break; + + case ARM_INST_LDMDB: + mypid = m_thread.ProcessID(); + addressWherePCLives = baseAddress - 4; + if (PDProcessMemoryRead(mypid, addressWherePCLives, sizeof(lldb::addr_t), &myTargetPC) != sizeof(lldb::addr_t)) + { + LogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives); + return false; + } + + *targetPC = myTargetPC; + break; + + case ARM_INST_LDMIB: + mypid = m_thread.ProcessID(); + addressWherePCLives = baseAddress + numRegistersToLoad*4 + 4; + if (PDProcessMemoryRead(mypid, addressWherePCLives, sizeof(lldb::addr_t), &myTargetPC) != sizeof(lldb::addr_t)) + { + LogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives); + return false; + } + + *targetPC = myTargetPC; + break; + + case ARM_INST_LDMIA: // same as pop + // Normal 16-bit LD multiple can't touch R15, but POP can + case ARM_INST_POP: // Can also get the PC & updates SP + mypid = m_thread.ProcessID(); + addressWherePCLives = baseAddress + numRegistersToLoad*4; + if (PDProcessMemoryRead(mypid, addressWherePCLives, sizeof(lldb::addr_t), &myTargetPC) != sizeof(lldb::addr_t)) + { + LogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives); + return false; + } + + *targetPC = myTargetPC; + break; + + // 16b TBB and TBH instructions load a jump address from a table + case ARM_INST_TBB: + mypid = m_thread.ProcessID(); + addressWherePCLives = baseAddress + addressOffsetFromRegisterAfterShift; + if (PDProcessMemoryRead(mypid, addressWherePCLives, 1, &halfwords) != 1) + { + LogError("Could not read memory at %8.8x to get targetPC when processing the TBB instruction!", addressWherePCLives); + return false; + } + // add 4 to currentPC since we are in Thumb mode and then add 2*halfwords + *targetPC = (currentPC + 4) + 2*halfwords; + break; + + case ARM_INST_TBH: + mypid = m_thread.ProcessID(); + addressWherePCLives = ((baseAddress + (addressOffsetFromRegisterAfterShift << 1)) & ~0x1); + if (PDProcessMemoryRead(mypid, addressWherePCLives, 2, &halfwords) != 2) + { + LogError("Could not read memory at %8.8x to get targetPC when processing the TBH instruction!", addressWherePCLives); + return false; + } + // add 4 to currentPC since we are in Thumb mode and then add 2*halfwords + *targetPC = (currentPC + 4) + 2*halfwords; + break; + + // ThumbEE branch-to-handler instructions: Jump to handlers at some offset + // from a special base pointer register (which is unknown at disassembly time) + case ARM_INST_HB: + case ARM_INST_HBP: + // TODO: ARM_INST_HB, ARM_INST_HBP + break; + + case ARM_INST_HBL: + case ARM_INST_HBLP: + // TODO: ARM_INST_HBL, ARM_INST_HBLP + break; + + // Breakpoint and software interrupt jump to interrupt handler (always ARM) + case ARM_INST_BKPT: + case ARM_INST_SMC: + case ARM_INST_SVC: + // TODO: ARM_INST_BKPT, ARM_INST_SMC, ARM_INST_SVC + break; + + // Return from exception, obviously modifies PC [interrupt only!] + case ARM_INST_RFEDA: + case ARM_INST_RFEDB: + case ARM_INST_RFEIA: + case ARM_INST_RFEIB: + // TODO: ARM_INST_RFEDA, ARM_INST_RFEDB, ARM_INST_RFEIA, ARM_INST_RFEIB + break; + + // Other instructions either can't change R15 or are "undefined" if you do, + // so no sane compiler should ever generate them & we don't care here. + // Also, R15 can only legally be used in a read-only manner for the + // various ARM addressing mode (to get PC-relative addressing of constants), + // but can NOT be used with any of the update modes. + default: + LogError("%s should not be called for instruction code %d!", __FUNCTION__, decodedInstruction.instruction->code); + return false; + break; + } + + return true; +} + +void +MachThreadContext_arm::EvaluateNextInstructionForSoftwareBreakpointSetup(lldb::addr_t currentPC, uint32_t cpsr, bool currentPCIsThumb, lldb::addr_t *nextPC, bool *nextPCIsThumb) +{ + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "MachThreadContext_arm::EvaluateNextInstructionForSoftwareBreakpointSetup() called"); + + lldb::addr_t targetPC = LLDB_INVALID_ADDRESS; + uint32_t registerValue; + arm_error_t decodeError; + lldb::addr_t currentPCInITBlock, nextPCInITBlock; + int i; + bool last_decoded_instruction_executes = true; + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: default nextPC=0x%8.8x (%s)", __FUNCTION__, *nextPC, *nextPCIsThumb ? "Thumb" : "ARM"); + + // Update *nextPC and *nextPCIsThumb for special cases + if (m_last_decode_thumb.itBlockRemaining) // we are in an IT block + { + // Set the nextPC to the PC of the instruction which will execute in the IT block + // If none of the instruction execute in the IT block based on the condition flags, + // then point to the instruction immediately following the IT block + const int itBlockRemaining = m_last_decode_thumb.itBlockRemaining; + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: itBlockRemaining=%8.8x", __FUNCTION__, itBlockRemaining); + + // Determine the PC at which the next instruction resides + if (m_last_decode_arm.thumb16b) + currentPCInITBlock = currentPC + 2; + else + currentPCInITBlock = currentPC + 4; + + for (i = 0; i < itBlockRemaining; i++) + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: currentPCInITBlock=%8.8x", __FUNCTION__, currentPCInITBlock); + decodeError = DecodeInstructionUsingDisassembler(currentPCInITBlock, cpsr, &m_last_decode_arm, &m_last_decode_thumb, &nextPCInITBlock); + + if (decodeError != ARM_SUCCESS) + LogError("unable to disassemble instruction at 0x%8.8lx", currentPCInITBlock); + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: condition=%d", __FUNCTION__, m_last_decode_arm.condition); + if (ConditionPassed(m_last_decode_arm.condition, cpsr)) + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Condition codes matched for instruction %d", __FUNCTION__, i); + break; // break from the for loop + } + else + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Condition codes DID NOT matched for instruction %d", __FUNCTION__, i); + } + + // update currentPC and nextPCInITBlock + currentPCInITBlock = nextPCInITBlock; + } + + if (i == itBlockRemaining) // We came out of the IT block without executing any instructions + last_decoded_instruction_executes = false; + + *nextPC = currentPCInITBlock; + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: After IT block step-through: *nextPC=%8.8x", __FUNCTION__, *nextPC); + } + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, + "%s: cpsr = %8.8x, thumb16b = %d, thumb = %d, branch = %d, conditional = %d, knownTarget = %d, links = %d, canSwitchMode = %d, doesSwitchMode = %d", + __FUNCTION__, + cpsr, + m_last_decode_arm.thumb16b, + m_last_decode_arm.thumb, + m_last_decode_arm.branch, + m_last_decode_arm.conditional, + m_last_decode_arm.knownTarget, + m_last_decode_arm.links, + m_last_decode_arm.canSwitchMode, + m_last_decode_arm.doesSwitchMode); + + + if (last_decoded_instruction_executes && // Was this a conditional instruction that did execute? + m_last_decode_arm.branch && // Can this instruction change the PC? + (m_last_decode_arm.instruction->code != ARM_INST_SVC)) // If this instruction is not an SVC instruction + { + // Set targetPC. Compute if needed. + if (m_last_decode_arm.knownTarget) + { + // Fixed, known PC-relative + targetPC = m_last_decode_arm.targetPC; + } + else + { + // if targetPC is not known at compile time (PC-relative target), compute targetPC + if (!ComputeNextPC(currentPC, m_last_decode_arm, currentPCIsThumb, &targetPC)) + { + LogError("%s: Unable to compute targetPC for instruction at 0x%8.8lx", __FUNCTION__, currentPC); + targetPC = LLDB_INVALID_ADDRESS; + } + } + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: targetPC=0x%8.8x, cpsr=0x%8.8x, condition=0x%hhx", __FUNCTION__, targetPC, cpsr, m_last_decode_arm.condition); + + // Refine nextPC computation + if ((m_last_decode_arm.instruction->code == ARM_INST_CBZ) || + (m_last_decode_arm.instruction->code == ARM_INST_CBNZ)) + { + // Compare and branch on zero/non-zero (Thumb-16 only) + // Unusual condition check built into the instruction + registerValue = m_state.gpr.r[m_last_decode_arm.op[REG_RD].value]; + + if (m_last_decode_arm.instruction->code == ARM_INST_CBZ) + { + if (registerValue == 0) + *nextPC = targetPC; + } + else + { + if (registerValue != 0) + *nextPC = targetPC; + } + } + else if (m_last_decode_arm.conditional) // Is the change conditional on flag results? + { + if (ConditionPassed(m_last_decode_arm.condition, cpsr)) // conditions match + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Condition matched!", __FUNCTION__); + *nextPC = targetPC; + } + else + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Condition did not match!", __FUNCTION__); + } + } + else + { + *nextPC = targetPC; + } + + // Refine nextPCIsThumb computation + if (m_last_decode_arm.doesSwitchMode) + { + *nextPCIsThumb = !currentPCIsThumb; + } + else if (m_last_decode_arm.canSwitchMode) + { + // Legal to switch ARM <--> Thumb mode with this branch + // dependent on bit[0] of targetPC + *nextPCIsThumb = (*nextPC & 1u) != 0; + } + else + { + *nextPCIsThumb = currentPCIsThumb; + } + } + + ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: calculated nextPC=0x%8.8x (%s)", __FUNCTION__, *nextPC, *nextPCIsThumb ? "Thumb" : "ARM"); +} + + +arm_error_t +MachThreadContext_arm::DecodeInstructionUsingDisassembler(lldb::addr_t curr_pc, uint32_t curr_cpsr, arm_decoded_instruction_t *decodedInstruction, thumb_static_data_t *thumbStaticData, lldb::addr_t *next_pc) +{ + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: pc=0x%8.8x, cpsr=0x%8.8x", __FUNCTION__, curr_pc, curr_cpsr); + + const uint32_t isetstate_mask = MASK_CPSR_T | MASK_CPSR_J; + const uint32_t curr_isetstate = curr_cpsr & isetstate_mask; + uint32_t opcode32; + lldb::addr_t nextPC = curr_pc; + arm_error_t decodeReturnCode = ARM_SUCCESS; + + m_last_decode_pc = curr_pc; + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: last_decode_pc=0x%8.8x", __FUNCTION__, m_last_decode_pc); + + switch (curr_isetstate) { + case 0x0: // ARM Instruction + // Read the ARM opcode + if (m_thread.Process()->Task().ReadMemory(curr_pc, 4, &opcode32) != 4) + { + LogError("unable to read opcode bits 31:0 for an ARM opcode at 0x%8.8lx", curr_pc); + decodeReturnCode = ARM_ERROR; + } + else + { + nextPC += 4; + decodeReturnCode = ArmDisassembler((uint64_t)curr_pc, opcode32, false, decodedInstruction, NULL, 0, NULL, 0); + + if (decodeReturnCode != ARM_SUCCESS) + LogError("Unable to decode ARM instruction 0x%8.8x at 0x%8.8lx", opcode32, curr_pc); + } + break; + + case 0x20: // Thumb Instruction + uint16_t opcode16; + // Read the a 16 bit Thumb opcode + if (m_thread.Process()->Task().ReadMemory(curr_pc, 2, &opcode16) != 2) + { + LogError("unable to read opcode bits 15:0 for a thumb opcode at 0x%8.8lx", curr_pc); + decodeReturnCode = ARM_ERROR; + } + else + { + nextPC += 2; + opcode32 = opcode16; + + decodeReturnCode = ThumbDisassembler((uint64_t)curr_pc, opcode16, false, false, thumbStaticData, decodedInstruction, NULL, 0, NULL, 0); + + switch (decodeReturnCode) { + case ARM_SKIP: + // 32 bit thumb opcode + nextPC += 2; + if (m_thread.Process()->Task().ReadMemory(curr_pc+2, 2, &opcode16) != 2) + { + LogError("unable to read opcode bits 15:0 for a thumb opcode at 0x%8.8lx", curr_pc+2); + } + else + { + opcode32 = (opcode32 << 16) | opcode16; + + decodeReturnCode = ThumbDisassembler((uint64_t)(curr_pc+2), opcode16, false, false, thumbStaticData, decodedInstruction, NULL, 0, NULL, 0); + + if (decodeReturnCode != ARM_SUCCESS) + LogError("Unable to decode 2nd half of Thumb instruction 0x%8.4hx at 0x%8.8lx", opcode16, curr_pc+2); + break; + } + break; + + case ARM_SUCCESS: + // 16 bit thumb opcode; at this point we are done decoding the opcode + break; + + default: + LogError("Unable to decode Thumb instruction 0x%8.4hx at 0x%8.8lx", opcode16, curr_pc); + decodeReturnCode = ARM_ERROR; + break; + } + } + break; + + default: + break; + } + + if (next_pc) + *next_pc = nextPC; + + return decodeReturnCode; +} + +bool +MachThreadContext_arm::BreakpointHit(lldb::pid_t pid, lldb::tid_t tid, lldb::user_id_t breakID, void *baton) +{ + lldb::addr_t bkpt_pc = (lldb::addr_t)baton; + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s(pid = %i, tid = %4.4x, breakID = %u, baton = %p): Setting PC to 0x%8.8x", __FUNCTION__, pid, tid, breakID, baton, bkpt_pc); + return PDThreadSetRegisterValueByID(pid, tid, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, bkpt_pc); +} + +// Set the single step bit in the processor status register. +kern_return_t +MachThreadContext_arm::SetSingleStepSoftwareBreakpoints() +{ + Error err; + err = ReadGPRRegisters(false); + + if (err.Fail()) + { + err.Log("%s: failed to read the GPR registers", __FUNCTION__); + return err.GetError(); + } + + lldb::addr_t curr_pc = m_state.gpr.r[15]; + uint32_t curr_cpsr = m_state.gpr.__cpsr; + lldb::addr_t next_pc = curr_pc; + + bool curr_pc_is_thumb = (m_state.gpr.__cpsr & 0x20) != 0; + bool next_pc_is_thumb = curr_pc_is_thumb; + + uint32_t curr_itstate = ((curr_cpsr & 0x6000000) >> 25) | ((curr_cpsr & 0xFC00) >> 8); + bool inITBlock = (curr_itstate & 0xF) ? 1 : 0; + bool lastInITBlock = ((curr_itstate & 0xF) == 0x8) ? 1 : 0; + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: curr_pc=0x%8.8x (%s), curr_itstate=0x%x, inITBlock=%d, lastInITBlock=%d", __FUNCTION__, curr_pc, curr_pc_is_thumb ? "Thumb" : "ARM", curr_itstate, inITBlock, lastInITBlock); + + // If the instruction is not in the IT block, then decode using the Disassembler and compute next_pc + if (!inITBlock) + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Decoding an instruction NOT in the IT block", __FUNCTION__); + + arm_error_t decodeReturnCode = DecodeInstructionUsingDisassembler(curr_pc, curr_cpsr, &m_last_decode_arm, &m_last_decode_thumb, &next_pc); + + if (decodeReturnCode != ARM_SUCCESS) + { + err = KERN_INVALID_ARGUMENT; + LogError("MachThreadContext_arm::SetSingleStepSoftwareBreakpoints: Unable to disassemble instruction at 0x%8.8lx", curr_pc); + } + } + else + { + next_pc = curr_pc + ((m_last_decode_arm.thumb16b) ? 2 : 4); + } + + // Instruction is NOT in the IT block OR + if (!inITBlock) + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: normal instruction", __FUNCTION__); + EvaluateNextInstructionForSoftwareBreakpointSetup(curr_pc, m_state.gpr.__cpsr, curr_pc_is_thumb, &next_pc, &next_pc_is_thumb); + } + else if (inITBlock && !m_last_decode_arm.setsFlags) + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: IT instruction that doesn't set flags", __FUNCTION__); + EvaluateNextInstructionForSoftwareBreakpointSetup(curr_pc, m_state.gpr.__cpsr, curr_pc_is_thumb, &next_pc, &next_pc_is_thumb); + } + else if (lastInITBlock && m_last_decode_arm.branch) + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: IT instruction which last in the IT block and is a branch", __FUNCTION__); + EvaluateNextInstructionForSoftwareBreakpointSetup(curr_pc, m_state.gpr.__cpsr, curr_pc_is_thumb, &next_pc, &next_pc_is_thumb); + } + else + { + // Instruction is in IT block and can modify the CPSR flags + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: IT instruction that sets flags", __FUNCTION__); + + // NOTE: When this point of code is reached, the instruction at curr_pc has already been decoded + // inside the function ShouldStop (). Therefore m_last_decode_arm, m_last_decode_thumb + // reflect the decoded instruction at curr_pc + + // If we find an instruction inside the IT block which will set/modify the condition flags (NZCV bits in CPSR), + // we set breakpoints at all remaining instructions inside the IT block starting from the instruction immediately + // following this one AND a breakpoint at the instruction immediately following the IT block. We do this because + // we cannot determine the next_pc until the instruction at which we are currently stopped executes. Hence we + // insert (m_last_decode_thumb.itBlockRemaining+1) 16-bit Thumb breakpoints at consecutive memory locations + // starting at addrOfNextInstructionInITBlock. We record these breakpoints in class variable m_sw_single_step_itblock_break_id[], + // and also record the total number of IT breakpoints set in the variable 'm_sw_single_step_itblock_break_count'. + + // The instructions inside the IT block, which are replaced by the 16-bit Thumb breakpoints (opcode=0xDEFE) + // instructions, can be either Thumb-16 or Thumb-32. When a Thumb-32 instruction (say, inst#1) is replaced Thumb + // by a 16-bit breakpoint (OS only supports 16-bit breakpoints in Thumb mode and 32-bit breakpoints in ARM mode), the + // breakpoint for the next instruction (say instr#2) is saved in the upper half of this Thumb-32 (instr#1) + // instruction. Hence if the execution stops at Breakpoint2 corresponding to instr#2, the PC is offset by 16-bits. + // We therefore have to keep track of PC of each instruction in the IT block that is being replaced with the 16-bit + // Thumb breakpoint, to ensure that when the breakpoint is hit, the PC is adjusted to the correct value. We save + // the actual PC corresponding to each instruction in the IT block by associating a call back with each breakpoint + // we set and passing it as a baton. When the breakpoint hits and the callback routine is called, the routine + // adjusts the PC based on the baton that is passed to it. + + lldb::addr_t addrOfNextInstructionInITBlock, pcInITBlock, nextPCInITBlock, bpAddressInITBlock; + uint16_t opcode16; + uint32_t opcode32; + + addrOfNextInstructionInITBlock = (m_last_decode_arm.thumb16b) ? curr_pc + 2 : curr_pc + 4; + + pcInITBlock = addrOfNextInstructionInITBlock; + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: itBlockRemaining=%d", __FUNCTION__, m_last_decode_thumb.itBlockRemaining); + + m_sw_single_step_itblock_break_count = 0; + for (int i = 0; i <= m_last_decode_thumb.itBlockRemaining; i++) + { + if (LLDB_BREAK_ID_IS_VALID(m_sw_single_step_itblock_break_id[i])) + { + LogError("FunctionProfiler::SetSingleStepSoftwareBreakpoints(): Array m_sw_single_step_itblock_break_id should not contain any valid breakpoint IDs at this point. But found a valid breakID=%d at index=%d", m_sw_single_step_itblock_break_id[i], i); + } + else + { + nextPCInITBlock = pcInITBlock; + // Compute nextPCInITBlock based on opcode present at pcInITBlock + if (m_thread.Process()->Task().ReadMemory(pcInITBlock, 2, &opcode16) == 2) + { + opcode32 = opcode16; + nextPCInITBlock += 2; + + // Check for 32 bit thumb opcode and read the upper 16 bits if needed + if (((opcode32 & 0xE000) == 0xE000) && (opcode32 & 0x1800)) + { + // Adjust 'next_pc_in_itblock' to point to the default next Thumb instruction for + // a 32 bit Thumb opcode + // Read bits 31:16 of a 32 bit Thumb opcode + if (m_thread.Process()->Task().ReadMemory(pcInITBlock+2, 2, &opcode16) == 2) + { + // 32 bit thumb opcode + opcode32 = (opcode32 << 16) | opcode16; + nextPCInITBlock += 2; + } + else + { + LogError("FunctionProfiler::SetSingleStepSoftwareBreakpoints(): Unable to read opcode bits 31:16 for a 32 bit thumb opcode at pc=0x%8.8lx", nextPCInITBlock); + } + } + } + else + { + LogError("FunctionProfiler::SetSingleStepSoftwareBreakpoints(): Error reading 16-bit Thumb instruction at pc=0x%8.8x", nextPCInITBlock); + } + + + // Set breakpoint and associate a callback function with it + bpAddressInITBlock = addrOfNextInstructionInITBlock + 2*i; + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Setting IT breakpoint[%d] at address: 0x%8.8x", __FUNCTION__, i, bpAddressInITBlock); + + m_sw_single_step_itblock_break_id[i] = m_thread.Process()->CreateBreakpoint(bpAddressInITBlock, 2, false, m_thread.GetID()); + if (!LLDB_BREAK_ID_IS_VALID(m_sw_single_step_itblock_break_id[i])) + err = KERN_INVALID_ARGUMENT; + else + { + ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: Set IT breakpoint[%i]=%d set at 0x%8.8x for instruction at 0x%8.8x", __FUNCTION__, i, m_sw_single_step_itblock_break_id[i], bpAddressInITBlock, pcInITBlock); + + // Set the breakpoint callback for these special IT breakpoints + // so that if one of these breakpoints gets hit, it knows to + // update the PC to the original address of the conditional + // IT instruction. + PDBreakpointSetCallback(m_thread.ProcessID(), m_sw_single_step_itblock_break_id[i], MachThreadContext_arm::BreakpointHit, (void*)pcInITBlock); + m_sw_single_step_itblock_break_count++; + } + } + + pcInITBlock = nextPCInITBlock; + } + + ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Set %u IT software single breakpoints.", __FUNCTION__, m_sw_single_step_itblock_break_count); + + } + + ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: next_pc=0x%8.8x (%s)", __FUNCTION__, next_pc, next_pc_is_thumb ? "Thumb" : "ARM"); + + if (next_pc & 0x1) + { + assert(next_pc_is_thumb); + } + + if (next_pc_is_thumb) + { + next_pc &= ~0x1; + } + else + { + assert((next_pc & 0x3) == 0); + } + + if (!inITBlock || (inITBlock && !m_last_decode_arm.setsFlags) || (lastInITBlock && m_last_decode_arm.branch)) + { + err = KERN_SUCCESS; + +#if defined DNB_ARCH_MACH_ARM_DEBUG_SW_STEP + m_sw_single_step_next_pc = next_pc; + if (next_pc_is_thumb) + m_sw_single_step_next_pc |= 1; // Set bit zero if the next PC is expected to be Thumb +#else + const DNBBreakpoint *bp = m_thread.Process()->Breakpoints().FindByAddress(next_pc); + + if (bp == NULL) + { + m_sw_single_step_break_id = m_thread.Process()->CreateBreakpoint(next_pc, next_pc_is_thumb ? 2 : 4, false, m_thread.GetID()); + if (!LLDB_BREAK_ID_IS_VALID(m_sw_single_step_break_id)) + err = KERN_INVALID_ARGUMENT; + ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: software single step breakpoint with breakID=%d set at 0x%8.8x", __FUNCTION__, m_sw_single_step_break_id, next_pc); + } +#endif + } + + return err.GetError(); +} + +#endif + +MachThreadContext* +MachThreadContext_arm::Create (const ArchSpec &arch_spec, ThreadMacOSX &thread) +{ + return new MachThreadContext_arm(thread); +} + +void +MachThreadContext_arm::Initialize() +{ + ArchSpec arch_spec(CPU_TYPE_ARM, CPU_TYPE_ANY); + ProcessMacOSX::AddArchCreateCallback(arch_spec, MachThreadContext_arm::Create); +} diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.h new file mode 100644 index 00000000000..4ec72dfd0d4 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.h @@ -0,0 +1,63 @@ +//===-- MachThreadContext_arm.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_MachThreadContext_arm_h_ +#define liblldb_MachThreadContext_arm_h_ + +#include "MachThreadContext.h" +#include "RegisterContextMach_arm.h" + +class ThreadMacOSX; + +class MachThreadContext_arm : public MachThreadContext +{ +public: + enum { kMaxNumThumbITBreakpoints = 4 }; + + static MachThreadContext* + Create (const lldb_private::ArchSpec &arch_spec, ThreadMacOSX &thread); + + static void + Initialize(); + + MachThreadContext_arm(ThreadMacOSX &thread); + + virtual + ~MachThreadContext_arm(); + + virtual lldb_private::RegisterContext * + CreateRegisterContext (lldb_private::StackFrame *frame) const; + + virtual void + InitializeInstance(); + + virtual void + ThreadWillResume (); + + virtual bool + ShouldStop (); + + virtual void + RefreshStateAfterStop (); + + static uint32_t + GetCPUType (); + +protected: + kern_return_t + EnableHardwareSingleStep (bool enable); + +protected: + lldb::addr_t m_hw_single_chained_step_addr; + uint32_t m_bvr0_reg; + uint32_t m_bcr0_reg; + uint32_t m_bvr0_save; + uint32_t m_bcr0_save; +}; +#endif // #ifndef liblldb_MachThreadContext_arm_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.cpp new file mode 100644 index 00000000000..ea148a69f41 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.cpp @@ -0,0 +1,245 @@ +//===-- MachThreadContext_i386.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined (__i386__) || defined (__x86_64__) + + +#include "MachThreadContext_i386.h" + +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" + +#include "ProcessMacOSX.h" +#include "ThreadMacOSX.h" + +using namespace lldb; +using namespace lldb_private; + +MachThreadContext_i386::MachThreadContext_i386 (ThreadMacOSX &thread) : + MachThreadContext (thread), + m_flags_reg(LLDB_INVALID_REGNUM) +{ +} + +MachThreadContext_i386::~MachThreadContext_i386() +{ +} + + +MachThreadContext* +MachThreadContext_i386::Create (const ArchSpec &arch_spec, ThreadMacOSX &thread) +{ + return new MachThreadContext_i386(thread); +} + +// Class init function +void +MachThreadContext_i386::Initialize() +{ + ArchSpec arch_spec("i386"); + ProcessMacOSX::AddArchCreateCallback(arch_spec, MachThreadContext_i386::Create); +} + +// Instance init function +void +MachThreadContext_i386::InitializeInstance() +{ + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + assert (reg_ctx != NULL); + m_flags_reg = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); +} + + +uint32_t +MachThreadContext_i386::GetCPUType() +{ + return CPU_TYPE_I386; +} + +void +MachThreadContext_i386::ThreadWillResume() +{ + m_thread.GetRegisterContext()->HardwareSingleStep (m_thread.GetState() == eStateStepping); +} + +bool +MachThreadContext_i386::ShouldStop() +{ + return true; +} + +void +MachThreadContext_i386::RefreshStateAfterStop() +{ + m_thread.GetRegisterContext()->HardwareSingleStep (false); +} + +bool +MachThreadContext_i386::NotifyException (MachException::Data& exc) +{ + switch (exc.exc_type) + { + case EXC_BAD_ACCESS: + break; + case EXC_BAD_INSTRUCTION: + break; + case EXC_ARITHMETIC: + break; + case EXC_EMULATION: + break; + case EXC_SOFTWARE: + break; + case EXC_BREAKPOINT: + if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 2) + { + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + assert (reg_ctx); + lldb::addr_t pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS); + if (pc != LLDB_INVALID_ADDRESS && pc > 0) + { + pc -= 1; + reg_ctx->SetPC(pc); + } + return true; + } + break; + case EXC_SYSCALL: + break; + case EXC_MACH_SYSCALL: + break; + case EXC_RPC_ALERT: + break; + } + return false; +} + + +// Set the single step bit in the processor status register. +//kern_return_t +//MachThreadContext_i386::EnableHardwareSingleStep (bool enable) +//{ +// RegisterContext *reg_ctx = m_thread.GetRegisterContext(); +// assert (reg_ctx); +// Scalar rflags_scalar; +// +// if (reg_ctx->ReadRegisterValue (m_flags_reg, rflags_scalar)) +// { +// Flags rflags(rflags_scalar.UInt()); +// const uint32_t trace_bit = 0x100u; +// if (enable) +// { +// // If the trace bit is already cleared, there is nothing to do +// if (rflags.IsSet (trace_bit)) +// return KERN_SUCCESS; +// else +// rflags.Set (trace_bit); +// } +// else +// { +// // If the trace bit is already cleared, there is nothing to do +// if (rflags.IsClear (trace_bit)) +// return KERN_SUCCESS; +// else +// rflags.Clear(trace_bit); +// } +// +// rflags_scalar = rflags.GetAllFlagBits(); +// // If the code makes it here we have changes to the GPRs which +// // we need to write back out, so lets do that. +// if (reg_ctx->WriteRegisterValue(m_flags_reg, rflags_scalar)) +// return KERN_SUCCESS; +// } +// // Return the error code for reading the GPR registers back +// return KERN_INVALID_ARGUMENT; +//} + +RegisterContext * +MachThreadContext_i386::CreateRegisterContext (StackFrame *frame) const +{ + return new RegisterContextMach_i386(m_thread, frame); +} + + +size_t +MachThreadContext_i386::GetStackFrameData(StackFrame *first_frame, std::vector<std::pair<lldb::addr_t, lldb::addr_t> >& fp_pc_pairs) +{ + fp_pc_pairs.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); + + Frame_i386 frame = { reg_ctx->GetFP(0), reg_ctx->GetPC(LLDB_INVALID_ADDRESS) }; + + fp_pc_pairs.push_back(std::make_pair(frame.fp, frame.pc)); + + 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 != 0) + fp_pc_pairs.push_back(std::make_pair(frame.fp, frame.pc)); + } + if (!fp_pc_pairs.empty()) + { + lldb::addr_t first_frame_pc = fp_pc_pairs.front().second; + 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)) + { + // Construct a correct second frame (we already read the pc for it above + frame.fp = fp_pc_pairs.front().first; + + // Insert the frame + fp_pc_pairs.insert(fp_pc_pairs.begin()+1, std::make_pair(frame.fp, frame.pc)); + + // Correct the fp in the first frame to use the SP + fp_pc_pairs.front().first = first_frame_sp; + } + } + } + } + } + return fp_pc_pairs.size(); +} + + +#endif // #if defined (__i386__) diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.h new file mode 100644 index 00000000000..4bee2e7be8f --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.h @@ -0,0 +1,57 @@ +//===-- MachThreadContext_i386.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_MachThreadContext_i386_h_ +#define liblldb_MachThreadContext_i386_h_ + +#if defined (__i386__) || defined (__x86_64__) + +#include "MachThreadContext.h" +#include "RegisterContextMach_i386.h" + +class ThreadMacOSX; + +class MachThreadContext_i386 : public MachThreadContext +{ +public: + static MachThreadContext* Create(const lldb_private::ArchSpec &arch_spec, ThreadMacOSX &thread); + + // Class init function + static void Initialize(); + + MachThreadContext_i386(ThreadMacOSX &thread); + + virtual + ~MachThreadContext_i386(); + + virtual lldb_private::RegisterContext * + CreateRegisterContext (lldb_private::StackFrame *frame) const; + + virtual void InitializeInstance(); + virtual void ThreadWillResume(); + virtual bool ShouldStop (); + virtual void RefreshStateAfterStop(); + + virtual bool NotifyException(MachException::Data& exc); + virtual size_t GetStackFrameData(lldb_private::StackFrame *first_frame, std::vector<std::pair<lldb::addr_t, lldb::addr_t> >& fp_pc_pairs); + static uint32_t GetCPUType(); + +protected: +// kern_return_t EnableHardwareSingleStep (bool enable); + uint32_t m_flags_reg; +private: + DISALLOW_COPY_AND_ASSIGN (MachThreadContext_i386); +}; + +//#if defined (__i386__) +//typedef MachThreadContext_i386 DNBArch; +//#endif + +#endif // defined (__i386__) || defined (__x86_64__) +#endif // #ifndef liblldb_MachThreadContext_i386_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.cpp new file mode 100644 index 00000000000..3ee9eb419de --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.cpp @@ -0,0 +1,255 @@ +//===-- MachThreadContext_x86_64.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined (__i386__) || defined (__x86_64__) + +#include <sys/cdefs.h> + +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" + +#include "MachThreadContext_x86_64.h" +#include "ProcessMacOSX.h" +#include "ThreadMacOSX.h" + +using namespace lldb; +using namespace lldb_private; + +MachThreadContext_x86_64::MachThreadContext_x86_64(ThreadMacOSX &thread) : + MachThreadContext (thread), + m_flags_reg(LLDB_INVALID_REGNUM) +{ +} + +MachThreadContext_x86_64::~MachThreadContext_x86_64() +{ +} + +MachThreadContext* +MachThreadContext_x86_64::Create(const ArchSpec &arch_spec, ThreadMacOSX &thread) +{ + return new MachThreadContext_x86_64(thread); +} + +// Class init function +void +MachThreadContext_x86_64::Initialize() +{ + ArchSpec arch_spec("x86_64"); + ProcessMacOSX::AddArchCreateCallback(arch_spec, MachThreadContext_x86_64::Create); +} + +// Instance init function +void +MachThreadContext_x86_64::InitializeInstance() +{ + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + assert (reg_ctx != NULL); + m_flags_reg = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); +} + +uint32_t +MachThreadContext_x86_64::GetCPUType() +{ + return CPU_TYPE_X86_64; +} + +void +MachThreadContext_x86_64::ThreadWillResume() +{ + m_thread.GetRegisterContext()->HardwareSingleStep (m_thread.GetState() == eStateStepping); +} + +bool +MachThreadContext_x86_64::ShouldStop() +{ + return true; +} + +void +MachThreadContext_x86_64::RefreshStateAfterStop() +{ + m_thread.GetRegisterContext()->HardwareSingleStep (false); +} + +bool +MachThreadContext_x86_64::NotifyException(MachException::Data& exc) +{ + switch (exc.exc_type) + { + case EXC_BAD_ACCESS: + break; + case EXC_BAD_INSTRUCTION: + break; + case EXC_ARITHMETIC: + break; + case EXC_EMULATION: + break; + case EXC_SOFTWARE: + break; + case EXC_BREAKPOINT: + if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 2) + { + RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + assert (reg_ctx); + lldb::addr_t pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS); + if (pc != LLDB_INVALID_ADDRESS && pc > 0) + { + pc -= 1; + reg_ctx->SetPC(pc); + } + return true; + } + break; + case EXC_SYSCALL: + break; + case EXC_MACH_SYSCALL: + break; + case EXC_RPC_ALERT: + break; + } + return false; +} + + +// Set the single step bit in the processor status register. +//kern_return_t +//MachThreadContext_x86_64::EnableHardwareSingleStep (bool enable) +//{ +// RegisterContext *reg_ctx = m_thread.GetRegisterContext(); +// assert (reg_ctx); +// Scalar rflags_scalar; +// +// if (reg_ctx->ReadRegisterValue (m_flags_reg, rflags_scalar)) +// { +// Flags rflags(rflags_scalar.UInt()); +// const uint32_t trace_bit = 0x100u; +// if (enable) +// { +// // If the trace bit is already set, there is nothing to do +// if (rflags.IsSet (trace_bit)) +// return KERN_SUCCESS; +// else +// rflags.Set (trace_bit); +// } +// else +// { +// // If the trace bit is already cleared, there is nothing to do +// if (rflags.IsClear (trace_bit)) +// return KERN_SUCCESS; +// else +// rflags.Clear(trace_bit); +// } +// +// rflags_scalar = rflags.GetAllFlagBits(); +// // If the code makes it here we have changes to the GPRs which +// // we need to write back out, so lets do that. +// if (reg_ctx->WriteRegisterValue(m_flags_reg, rflags_scalar)) +// return KERN_SUCCESS; +// } +// // Return the error code for reading the GPR registers back +// return KERN_INVALID_ARGUMENT; +//} +// + +//---------------------------------------------------------------------- +// Register information defintions for 32 bit PowerPC. +//---------------------------------------------------------------------- + + + +RegisterContext * +MachThreadContext_x86_64::CreateRegisterContext (StackFrame *frame) const +{ + return new RegisterContextMach_x86_64(m_thread, frame); +} + + +//bool +//MachThreadContext_x86_64::RegisterSetStateIsValid (uint32_t set) const +//{ +// return m_state.RegisterSetIsCached(set); +//} + + +size_t +MachThreadContext_x86_64::GetStackFrameData(StackFrame *first_frame, std::vector<std::pair<lldb::addr_t, lldb::addr_t> >& fp_pc_pairs) +{ + fp_pc_pairs.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); + + Frame_x86_64 frame = { reg_ctx->GetFP(0), reg_ctx->GetPC(LLDB_INVALID_ADDRESS) }; + + fp_pc_pairs.push_back(std::make_pair(frame.fp, frame.pc)); + 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) + fp_pc_pairs.push_back(std::make_pair(frame.fp, frame.pc)); + } + if (!fp_pc_pairs.empty()) + { + lldb::addr_t first_frame_pc = fp_pc_pairs.front().second; + 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)) + { + // Construct a correct second frame (we already read the pc for it above + frame.fp = fp_pc_pairs.front().first; + + // Insert the frame + fp_pc_pairs.insert(fp_pc_pairs.begin()+1, std::make_pair(frame.fp, frame.pc)); + + // Correct the fp in the first frame to use the SP + fp_pc_pairs.front().first = first_frame_sp; + } + } + } + } + } + return fp_pc_pairs.size(); +} + + +#endif // #if defined (__i386__) || defined (__x86_64__) diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.h new file mode 100644 index 00000000000..45d5a375052 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.h @@ -0,0 +1,72 @@ +//===-- MachThreadContext_x86_64.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_MachThreadContext_x86_64_h_ +#define liblldb_MachThreadContext_x86_64_h_ + +#if defined (__i386__) || defined (__x86_64__) + +#include "MachThreadContext.h" +#include "RegisterContextMach_x86_64.h" + +class ThreadMacOSX; + +class MachThreadContext_x86_64 : public MachThreadContext +{ +public: + static MachThreadContext* + Create(const lldb_private::ArchSpec &arch_spec, ThreadMacOSX &thread); + + // Class init function + static void + Initialize(); + + // Instance init function + void + InitializeInstance(); + + MachThreadContext_x86_64 (ThreadMacOSX &thread); + + virtual + ~MachThreadContext_x86_64(); + + virtual lldb_private::RegisterContext * + CreateRegisterContext (lldb_private::StackFrame *frame) const; + + virtual void + ThreadWillResume (); + + virtual bool + ShouldStop (); + + virtual void + RefreshStateAfterStop (); + + virtual bool + NotifyException (MachException::Data& exc); + + virtual size_t + GetStackFrameData (lldb_private::StackFrame *first_frame, std::vector<std::pair<lldb::addr_t, lldb::addr_t> >& fp_pc_pairs); + + static uint32_t + GetCPUType(); + +protected: +// kern_return_t EnableHardwareSingleStep (bool enable); + uint32_t m_flags_reg; +private: + DISALLOW_COPY_AND_ASSIGN (MachThreadContext_x86_64); +}; + +//#if defined (__x86_64__) +//typedef MachThreadContext_x86_64 DNBArch; +//#endif + +#endif // defined (__i386__) || defined (__x86_64__) +#endif // #ifndef liblldb_MachThreadContext_x86_64_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.cpp new file mode 100644 index 00000000000..c91af3c3596 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.cpp @@ -0,0 +1,195 @@ +//===-- MachVMMemory.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MachVMMemory.h" + +#include <mach/mach_vm.h> + +#include "MachVMRegion.h" +#include "ProcessMacOSXLog.h" + +using namespace lldb_private; + +MachVMMemory::MachVMMemory() : + m_page_size (kInvalidPageSize) +{ +} + +MachVMMemory::~MachVMMemory() +{ +} + +size_t +MachVMMemory::PageSize(lldb_private::Error &error) +{ + if (m_page_size == kInvalidPageSize) + { + error = ::host_page_size( ::mach_host_self(), &m_page_size); + if (error.Fail()) + m_page_size = 0; + } + + if (m_page_size != 0 && m_page_size != kInvalidPageSize) + { + if (error.Success()) + error.SetErrorString ("unable to determine page size"); + } + return m_page_size; +} + +size_t +MachVMMemory::MaxBytesLeftInPage (lldb::addr_t addr, size_t count) +{ + Error error; + const size_t page_size = PageSize(error); + if (page_size > 0) + { + size_t page_offset = (addr % page_size); + size_t bytes_left_in_page = page_size - page_offset; + if (count > bytes_left_in_page) + count = bytes_left_in_page; + } + return count; +} + +size_t +MachVMMemory::Read(task_t task, lldb::addr_t address, void *data, size_t data_count, Error &error) +{ + if (data == NULL || data_count == 0) + return 0; + + size_t total_bytes_read = 0; + lldb::addr_t curr_addr = address; + uint8_t *curr_data = (uint8_t*)data; + while (total_bytes_read < data_count) + { + mach_vm_size_t curr_size = MaxBytesLeftInPage(curr_addr, data_count - total_bytes_read); + mach_msg_type_number_t curr_bytes_read = 0; + vm_offset_t vm_memory = NULL; + error = ::mach_vm_read (task, curr_addr, curr_size, &vm_memory, &curr_bytes_read); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY|PD_LOG_VERBOSE); + + if (log || error.Fail()) + error.PutToLog (log, "::mach_vm_read (task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt => %i)", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read); + + if (error.Success()) + { + if (curr_bytes_read != curr_size) + { + if (log) + error.PutToLog (log, "::mach_vm_read (task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt=>%i) only read %u of %llu bytes", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read, curr_bytes_read, (uint64_t)curr_size); + } + ::memcpy (curr_data, (void *)vm_memory, curr_bytes_read); + ::vm_deallocate (mach_task_self (), vm_memory, curr_bytes_read); + total_bytes_read += curr_bytes_read; + curr_addr += curr_bytes_read; + curr_data += curr_bytes_read; + } + else + { + break; + } + } + return total_bytes_read; +} + + +size_t +MachVMMemory::Write(task_t task, lldb::addr_t address, const void *data, size_t data_count, Error &error) +{ + MachVMRegion vmRegion(task); + + size_t total_bytes_written = 0; + lldb::addr_t curr_addr = address; + const uint8_t *curr_data = (const uint8_t*)data; + + + while (total_bytes_written < data_count) + { + if (vmRegion.GetRegionForAddress(curr_addr)) + { + mach_vm_size_t curr_data_count = data_count - total_bytes_written; + mach_vm_size_t region_bytes_left = vmRegion.BytesRemaining(curr_addr); + if (region_bytes_left == 0) + { + break; + } + if (curr_data_count > region_bytes_left) + curr_data_count = region_bytes_left; + + if (vmRegion.SetProtections(curr_addr, curr_data_count, VM_PROT_READ | VM_PROT_WRITE)) + { + size_t bytes_written = WriteRegion(task, curr_addr, curr_data, curr_data_count, error); + if (bytes_written <= 0) + { + // Error should have already be posted by WriteRegion... + break; + } + else + { + total_bytes_written += bytes_written; + curr_addr += bytes_written; + curr_data += bytes_written; + } + } + else + { + ProcessMacOSXLog::LogIf (PD_LOG_MEMORY_PROTECTIONS, "Failed to set read/write protections on region for address: [0x%8.8llx-0x%8.8llx)", (uint64_t)curr_addr, (uint64_t)(curr_addr + curr_data_count)); + break; + } + } + else + { + ProcessMacOSXLog::LogIf (PD_LOG_MEMORY_PROTECTIONS, "Failed to get region for address: 0x%8.8llx", (uint64_t)address); + break; + } + } + + return total_bytes_written; +} + + +size_t +MachVMMemory::WriteRegion(task_t task, const lldb::addr_t address, const void *data, const size_t data_count, Error &error) +{ + if (data == NULL || data_count == 0) + return 0; + + size_t total_bytes_written = 0; + lldb::addr_t curr_addr = address; + const uint8_t *curr_data = (const uint8_t*)data; + while (total_bytes_written < data_count) + { + mach_msg_type_number_t curr_data_count = MaxBytesLeftInPage(curr_addr, data_count - total_bytes_written); + error = ::mach_vm_write (task, curr_addr, (pointer_t) curr_data, curr_data_count); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY); + if (log || error.Fail()) + error.PutToLog (log, "::mach_vm_write ( task = 0x%4.4x, addr = 0x%8.8llx, data = %8.8p, dataCnt = %u )", task, (uint64_t)curr_addr, curr_data, curr_data_count); + +#if defined (__powerpc__) || defined (__ppc__) + vm_machine_attribute_val_t mattr_value = MATTR_VAL_CACHE_FLUSH; + + error = ::vm_machine_attribute (task, curr_addr, curr_data_count, MATTR_CACHE, &mattr_value); + if (log || error.Fail()) + error.Log(log, "::vm_machine_attribute ( task = 0x%4.4x, addr = 0x%8.8llx, size = %u, attr = MATTR_CACHE, mattr_value => MATTR_VAL_CACHE_FLUSH )", task, (uint64_t)curr_addr, curr_data_count); +#endif + + if (error.Success()) + { + total_bytes_written += curr_data_count; + curr_addr += curr_data_count; + curr_data += curr_data_count; + } + else + { + break; + } + } + return total_bytes_written; +} diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.h new file mode 100644 index 00000000000..866fa369706 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.h @@ -0,0 +1,36 @@ +//===-- MachVMMemory.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_MachVMMemory_h_ +#define liblldb_MachVMMemory_h_ + +#include <mach/mach.h> + +#include "lldb/lldb-private.h" +#include "lldb/Core/Error.h" + +class MachVMMemory +{ +public: + enum { kInvalidPageSize = ~0 }; + MachVMMemory(); + ~MachVMMemory(); + size_t Read(task_t task, lldb::addr_t address, void *data, size_t data_count, lldb_private::Error &error); + size_t Write(task_t task, lldb::addr_t address, const void *data, size_t data_count, lldb_private::Error &error); + size_t PageSize(lldb_private::Error &error); + +protected: + size_t MaxBytesLeftInPage(lldb::addr_t addr, size_t count); + + size_t WriteRegion(task_t task, const lldb::addr_t address, const void *data, const size_t data_count, lldb_private::Error &error); + vm_size_t m_page_size; +}; + + +#endif // #ifndef liblldb_MachVMMemory_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.cpp new file mode 100644 index 00000000000..87044fb31b2 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.cpp @@ -0,0 +1,183 @@ +//===-- MachVMRegion.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <assert.h> +#include <mach/mach_vm.h> +#include "MachVMRegion.h" +#include "ProcessMacOSXLog.h" + +using namespace lldb_private; + +MachVMRegion::MachVMRegion(task_t task) : + m_task(task), + m_addr(LLDB_INVALID_ADDRESS), + m_err(), + m_start(LLDB_INVALID_ADDRESS), + m_size(0), + m_depth(-1), + m_data(), + m_curr_protection(0), + m_protection_addr(LLDB_INVALID_ADDRESS), + m_protection_size(0) +{ + memset(&m_data, 0, sizeof(m_data)); +} + +MachVMRegion::~MachVMRegion() +{ + // Restore any original protections and clear our vars + Clear(); +} + +void +MachVMRegion::Clear() +{ + RestoreProtections(); + m_addr = LLDB_INVALID_ADDRESS; + m_err.Clear(); + m_start = LLDB_INVALID_ADDRESS; + m_size = 0; + m_depth = -1; + memset(&m_data, 0, sizeof(m_data)); + m_curr_protection = 0; + m_protection_addr = LLDB_INVALID_ADDRESS; + m_protection_size = 0; +} + +bool +MachVMRegion::SetProtections(mach_vm_address_t addr, mach_vm_size_t size, vm_prot_t prot) +{ + if (ContainsAddress(addr)) + { + mach_vm_size_t prot_size = size; + mach_vm_address_t end_addr = EndAddress(); + if (prot_size > (end_addr - addr)) + prot_size = end_addr - addr; + + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY_PROTECTIONS); + if (prot_size > 0) + { + if (prot == (m_curr_protection & VM_PROT_ALL)) + { + if (log) + log->Printf ("MachVMRegion::%s: protections (%u) already sufficient for task 0x%4.4x at address 0x%8.8llx) ", __FUNCTION__, prot, m_task, (uint64_t)addr); + // Protections are already set as requested... + return true; + } + else + { + m_err = ::mach_vm_protect (m_task, addr, prot_size, 0, prot); + if (log || m_err.Fail()) + m_err.PutToLog(log, "::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)addr, (uint64_t)prot_size, 0, prot); + if (m_err.Fail()) + { + // Try again with the ability to create a copy on write region + m_err = ::mach_vm_protect (m_task, addr, prot_size, 0, prot | VM_PROT_COPY); + if (log || m_err.Fail()) + m_err.PutToLog(log, "::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)addr, (uint64_t)prot_size, 0, prot | VM_PROT_COPY); + } + if (m_err.Success()) + { + m_curr_protection = prot; + m_protection_addr = addr; + m_protection_size = prot_size; + return true; + } + } + } + else + { + log->Printf("MachVMRegion::%s: Zero size for task 0x%4.4x at address 0x%8.8llx) ", __FUNCTION__, m_task, (uint64_t)addr); + } + } + return false; +} + +bool +MachVMRegion::RestoreProtections() +{ + if (m_curr_protection != m_data.protection && m_protection_size > 0) + { + m_err = ::mach_vm_protect (m_task, m_protection_addr, m_protection_size, 0, m_data.protection); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY_PROTECTIONS); + if (log || m_err.Fail()) + m_err.PutToLog(log, "::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)m_protection_addr, (uint64_t)m_protection_size, 0, m_data.protection); + if (m_err.Success()) + { + m_protection_size = 0; + m_protection_addr = LLDB_INVALID_ADDRESS; + m_curr_protection = m_data.protection; + return true; + } + } + else + { + m_err.Clear(); + return true; + } + + return false; +} + +bool +MachVMRegion::GetRegionForAddress(lldb::addr_t addr) +{ + // Restore any original protections and clear our vars + Clear(); + m_addr = addr; + m_start = addr; + m_depth = 1024; + mach_msg_type_number_t info_size = kRegionInfoSize; + assert(sizeof(info_size) == 4); + m_err = ::mach_vm_region_recurse (m_task, &m_start, &m_size, &m_depth, (vm_region_recurse_info_t)&m_data, &info_size); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY_PROTECTIONS); + if (log || m_err.Fail()) + m_err.PutToLog(log, "::mach_vm_region_recurse ( task = 0x%4.4x, address => 0x%8.8llx, size => %llu, nesting_depth => %d, info => %p, infoCnt => %d) addr = 0x%8.8llx ", m_task, (uint64_t)m_start, (uint64_t)m_size, m_depth, &m_data, info_size, (uint64_t)addr); + if (m_err.Fail()) + { + return false; + } + else + { + if (log && log->GetMask().IsSet(PD_LOG_VERBOSE)) + { + log->Printf("info = { prot = %u, " + "max_prot = %u, " + "inheritance = 0x%8.8x, " + "offset = 0x%8.8llx, " + "user_tag = 0x%8.8x, " + "ref_count = %u, " + "shadow_depth = %u, " + "ext_pager = %u, " + "share_mode = %u, " + "is_submap = %d, " + "behavior = %d, " + "object_id = 0x%8.8x, " + "user_wired_count = 0x%4.4x }", + m_data.protection, + m_data.max_protection, + m_data.inheritance, + (uint64_t)m_data.offset, + m_data.user_tag, + m_data.ref_count, + m_data.shadow_depth, + m_data.external_pager, + m_data.share_mode, + m_data.is_submap, + m_data.behavior, + m_data.object_id, + m_data.user_wired_count); + } + } + + m_curr_protection = m_data.protection; + + return true; +} diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.h new file mode 100644 index 00000000000..b12353df823 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.h @@ -0,0 +1,63 @@ +//===-- MachVMRegion.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_MachVMRegion_h_ +#define liblldb_MachVMRegion_h_ + +#include <mach/mach.h> +#include "lldb/lldb-private.h" +#include "lldb/Core/Error.h" + +class MachVMRegion +{ +public: + MachVMRegion(task_t task); + ~MachVMRegion(); + + void Clear(); + mach_vm_address_t StartAddress() const { return m_start; } + mach_vm_address_t EndAddress() const { return m_start + m_size; } + mach_vm_address_t BytesRemaining(mach_vm_address_t addr) const + { + if (ContainsAddress(addr)) + return m_size - (addr - m_start); + else + return 0; + } + bool ContainsAddress(mach_vm_address_t addr) const + { + return addr >= StartAddress() && addr < EndAddress(); + } + + bool SetProtections(mach_vm_address_t addr, mach_vm_size_t size, vm_prot_t prot); + bool RestoreProtections(); + bool GetRegionForAddress(lldb::addr_t addr); +protected: +#if defined (VM_REGION_SUBMAP_SHORT_INFO_COUNT_64) + typedef vm_region_submap_short_info_data_64_t RegionInfo; + enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 }; +#else + typedef vm_region_submap_info_data_64_t RegionInfo; + enum { kRegionInfoSize = VM_REGION_SUBMAP_INFO_COUNT_64 }; +#endif + + task_t m_task; + mach_vm_address_t m_addr; + lldb_private::Error m_err; + mach_vm_address_t m_start; + mach_vm_size_t m_size; + natural_t m_depth; + RegionInfo m_data; + vm_prot_t m_curr_protection; // The current, possibly modified protections. Original value is saved in m_data.protections. + mach_vm_address_t m_protection_addr; // The start address at which protections were changed + mach_vm_size_t m_protection_size; // The size of memory that had its protections changed + +}; + +#endif // #ifndef liblldb_MachVMRegion_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/ProcessControl-mig.defs b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/ProcessControl-mig.defs new file mode 100644 index 00000000000..7f16fe13356 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/ProcessControl-mig.defs @@ -0,0 +1,16 @@ +/* + * nub.defs + */ + +/* + * DNBConfig.h is autogenerated by a perl script that is run as a build + * script in XCode. XCode is responsible for calling the script and setting + * the include paths correctly to locate it. The file will exist in the + * derived sources directory in the build folder. + * + */ + +#include "DNBConfig.h" + + +#include <mach/mach_exc.defs> diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.cpp new file mode 100644 index 00000000000..3c37b64dc46 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.cpp @@ -0,0 +1,2228 @@ +//===-- ProcessMacOSX.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 +#include <errno.h> +#include <mach/mach.h> +#include <mach/mach_vm.h> +#include <spawn.h> +#include <sys/fcntl.h> +#include <sys/types.h> +#include <sys/ptrace.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <unistd.h> + +// C++ Includes +#include <algorithm> +#include <map> + +// Other libraries and framework includes + +#include "lldb/Breakpoint/WatchpointLocation.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/TargetList.h" +#include "PseudoTerminal.h" + +#if defined (__arm__) + +#include <CoreFoundation/CoreFoundation.h> +#include <SpringBoardServices/SpringBoardServer.h> +#include <SpringBoardServices/SBSWatchdogAssertion.h> + +#endif // #if defined (__arm__) + +// Project includes +#include "lldb/Host/Host.h" +#include "ProcessMacOSX.h" +#include "ProcessMacOSXLog.h" +#include "ThreadMacOSX.h" + + +#if 0 +#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_LOG(fmt, ...) +#endif + +#ifndef MACH_PROCESS_USE_POSIX_SPAWN +#define MACH_PROCESS_USE_POSIX_SPAWN 1 +#endif + + +#if defined (__arm__) + +static bool +IsSBProcess (lldb::pid_t pid) +{ + bool opt_runningApps = true; + bool opt_debuggable = false; + + CFReleaser<CFArrayRef> sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (opt_runningApps, opt_debuggable)); + if (sbsAppIDs.get() != NULL) + { + CFIndex count = ::CFArrayGetCount (sbsAppIDs.get()); + CFIndex i = 0; + for (i = 0; i < count; i++) + { + CFStringRef displayIdentifier = (CFStringRef)::CFArrayGetValueAtIndex (sbsAppIDs.get(), i); + + // Get the process id for the app (if there is one) + lldb::pid_t sbs_pid = LLDB_INVALID_PROCESS_ID; + if (::SBSProcessIDForDisplayIdentifier ((CFStringRef)displayIdentifier, &sbs_pid) == TRUE) + { + if (sbs_pid == pid) + return true; + } + } + } + return false; +} + + +#endif // #if defined (__arm__) + +using namespace lldb; +using namespace lldb_private; +// +//void * +//ProcessMacOSX::WaitForChildProcessToExit (void *pid_ptr) +//{ +// const lldb::pid_t pid = *((lldb::user_id_t *)pid_ptr); +// +// Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD); +// +// if (log) +// log->Printf ("ProcessMacOSX::%s (arg = %p) thread starting...", __FUNCTION__, pid_ptr); +// +// int status = -1; +// +// while (1) +// { +// if (log) +// log->Printf("::waitpid (pid = %i, stat_loc = %p, options = 0)...", pid, &status); +// +// lldb::pid_t return_pid = ::waitpid (pid, &status, 0); +// +// if (return_pid < 0) +// { +// if (log) +// log->Printf("%s ::waitpid (pid = %i, stat_loc = %p, options = 0) => errno = %i, status = 0x%8.8x", pid, &status, errno, status); +// break; +// } +// +// bool set_exit_status = false; +// if (WIFSTOPPED(status)) +// { +// if (log) +// log->Printf("::waitpid (pid = %i, stat_loc = %p, options = 0) => return_pid = %i, status = 0x%8.8x (STOPPED)", pid, &status, return_pid, status); +// } +// else if (WIFEXITED(status)) +// { +// set_exit_status = true; +// if (log) +// log->Printf("::waitpid (pid = %i, stat_loc = %p, options = 0) => return_pid = %i, status = 0x%8.8x (EXITED)", pid, &status, return_pid, status); +// } +// else if (WIFSIGNALED(status)) +// { +// set_exit_status = true; +// if (log) +// log->Printf("::waitpid (pid = %i, stat_loc = %p, options = 0) => return_pid = %i, status = 0x%8.8x (SIGNALED)", pid, &status, return_pid, status); +// } +// else +// { +// if (log) +// log->Printf("::waitpid (pid = %i, stat_loc = %p, options = 0) => return_pid = %i, status = 0x%8.8x", pid, &status, return_pid, status); +// } +// +// if (set_exit_status) +// { +// // Try and deliver the news to the process if it is still around +// TargetSP target_sp(TargetList::SharedList().FindTargetWithProcessID (return_pid)); +// if (target_sp.get()) +// { +// ProcessMacOSX *process = dynamic_cast<ProcessMacOSX*>(target_sp->GetProcess().get()); +// if (process) +// { +// process->SetExitStatus (status); +// if (log) +// log->Printf("Setting exit status of %i to 0x%8.8x", pid, status); +// process->Task().ShutDownExceptionThread(); +// } +// } +// // Break out of the loop and return. +// break; +// } +// } +// +// if (log) +// log->Printf ("ProcessMacOSX::%s (arg = %p) thread exiting...", __FUNCTION__, pid_ptr); +// +// return NULL; +//} +// + +const char * +ProcessMacOSX::GetPluginNameStatic() +{ + return "process.macosx"; +} + +const char * +ProcessMacOSX::GetPluginDescriptionStatic() +{ + return "Native MacOSX user process debugging plug-in."; +} + +void +ProcessMacOSX::Terminate() +{ + PluginManager::UnregisterPlugin (ProcessMacOSX::CreateInstance); +} + + +Process* +ProcessMacOSX::CreateInstance (Target &target, Listener &listener) +{ + ProcessMacOSX::Initialize(); + + return new ProcessMacOSX (target, listener); +} + +bool +ProcessMacOSX::CanDebug(Target &target) +{ + // For now we are just making sure the file exists for a given module + ModuleSP exe_module_sp(target.GetExecutableModule()); + if (exe_module_sp.get()) + return exe_module_sp->GetFileSpec().Exists(); + return false; +} + +//---------------------------------------------------------------------- +// ProcessMacOSX constructor +//---------------------------------------------------------------------- +ProcessMacOSX::ProcessMacOSX(Target& target, Listener &listener) : + Process (target, listener), + m_dynamic_loader_ap (), +// m_wait_thread (LLDB_INVALID_HOST_THREAD), + m_byte_order (eByteOrderHost), + m_stdio_ours (false), + m_child_stdin (-1), + m_child_stdout (-1), + m_child_stderr (-1), + m_task (this), + m_flags (eFlagsNone), + m_stdio_thread (LLDB_INVALID_HOST_THREAD), + m_stdio_mutex (Mutex::eMutexTypeRecursive), + m_stdout_data (), + m_exception_messages (), + m_exception_messages_mutex (Mutex::eMutexTypeRecursive), + m_arch_spec () +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ProcessMacOSX::~ProcessMacOSX() +{ +// m_mach_process.UnregisterNotificationCallbacks (this); + Clear(); +} + +//---------------------------------------------------------------------- +// PluginInterface +//---------------------------------------------------------------------- +const char * +ProcessMacOSX::GetPluginName() +{ + return "Process debugging plug-in for MacOSX"; +} + +const char * +ProcessMacOSX::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessMacOSX::GetPluginVersion() +{ + return 1; +} + +void +ProcessMacOSX::GetPluginCommandHelp (const char *command, Stream *strm) +{ + strm->Printf("The following arguments can be supplied to the 'log %s' command:\n", GetShortPluginName()); + strm->PutCString("\tverbose - enable verbose logging\n"); + strm->PutCString("\tprocess - enable process logging\n"); + strm->PutCString("\tthread - enable thread logging\n"); + strm->PutCString("\texceptions - enable exception logging\n"); + strm->PutCString("\tdynamic - enable DynamicLoader logging\n"); + strm->PutCString("\tmemory-calls - enable memory read and write call logging\n"); + strm->PutCString("\tmemory-data-short - log short memory read and write byte data\n"); + strm->PutCString("\tmemory-data-long - log all memory read and write byte data\n"); + strm->PutCString("\tmemory-protections - log memory protection calls\n"); + strm->PutCString("\tbreakpoints - log breakpoint calls\n"); + strm->PutCString("\twatchpoints - log watchpoint calls\n"); + strm->PutCString("\tevents - log event and event queue status\n"); + strm->PutCString("\tstep - log step related activity\n"); + strm->PutCString("\ttask - log task functions\n"); +} + +Error +ProcessMacOSX::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +ProcessMacOSX::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + +//---------------------------------------------------------------------- +// Process Control +//---------------------------------------------------------------------- +Error +ProcessMacOSX::DoLaunch +( + Module* module, + char const *argv[], + char const *envp[], + const char *stdin_path, + const char *stdout_path, + const char *stderr_path +) +{ +// ::LogSetBitMask (PD_LOG_DEFAULT); +// ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); +// ::LogSetLogFile ("/dev/stdout"); + + Error error; + ObjectFile * object_file = module->GetObjectFile(); + if (object_file) + { + ArchSpec arch_spec(module->GetArchitecture()); + + // Set our user ID to our process ID. + SetID (LaunchForDebug(argv[0], argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, eLaunchDefault, error)); + } + else + { + // Set our user ID to an invalid process ID. + SetID (LLDB_INVALID_PROCESS_ID); + error.SetErrorToGenericError (); + error.SetErrorStringWithFormat("Failed to get object file from '%s' for arch %s.\n", module->GetFileSpec().GetFilename().AsCString(), module->GetArchitecture().AsCString()); + } + + // Return the process ID we have + return error; +} + +Error +ProcessMacOSX::DoAttach (lldb::pid_t attach_pid) +{ + Error error; + + // Clear out and clean up from any current state + Clear(); + // HACK: require arch be set correctly at the target level until we can + // figure out a good way to determine the arch of what we are attaching to + m_arch_spec = m_target.GetArchitecture(); + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS); + if (attach_pid != LLDB_INVALID_PROCESS_ID) + { + SetPrivateState (eStateAttaching); + SetID(attach_pid); + // Let ourselves know we are going to be using SBS if the correct flag bit is set... +#if defined (__arm__) + if (IsSBProcess(pid)) + m_flags |= eFlagsUsingSBS; +#endif + + if (Task().GetTaskPortForProcessID(error) == TASK_NULL) + { + if (log) + log->Printf ("error attaching to pid %i: %s", GetID(), error.AsCString()); + + } + else + { + Task().StartExceptionThread(error); + + if (error.Success()) + { + errno = 0; + if (::ptrace (PT_ATTACHEXC, GetID(), 0, 0) == 0) + { + m_flags.Set (eFlagsAttached); + // Sleep a bit to let the exception get received and set our process status + // to stopped. + ::usleep(250000); + + if (log) + log->Printf ("successfully attached to pid %d", GetID()); + return error; + } + else + { + error.SetErrorToErrno(); + if (log) + log->Printf ("error: failed to attach to pid %d", GetID()); + } + } + else + { + if (log) + log->Printf ("error: failed to start exception thread for pid %d: %s", GetID(), error.AsCString()); + } + + } + } + SetID (LLDB_INVALID_PROCESS_ID); + if (error.Success()) + error.SetErrorStringWithFormat ("failed to attach to pid %d", attach_pid); + return error; +} + +Error +ProcessMacOSX::WillLaunchOrAttach () +{ + Error error; + // TODO: this is hardcoded for macosx right now. We need this to be more dynamic + m_dynamic_loader_ap.reset(DynamicLoader::FindPlugin(this, "dynamic-loader.macosx-dyld")); + + if (m_dynamic_loader_ap.get() == NULL) + error.SetErrorString("unable to find the dynamic loader named 'dynamic-loader.macosx-dyld'"); + + return error; +} + + +Error +ProcessMacOSX::WillLaunch (Module* module) +{ + return WillLaunchOrAttach (); +} + +void +ProcessMacOSX::DidLaunchOrAttach () +{ + if (GetID() == LLDB_INVALID_PROCESS_ID) + { + m_dynamic_loader_ap.reset(); + } + else + { + Module * exe_module = GetTarget().GetExecutableModule ().get(); + assert (exe_module); + + m_arch_spec = exe_module->GetArchitecture(); + assert (m_arch_spec.IsValid()); + + ObjectFile *exe_objfile = exe_module->GetObjectFile(); + assert (exe_objfile); + + m_byte_order = exe_objfile->GetByteOrder(); + assert (m_byte_order != eByteOrderInvalid); + // Install a signal handler so we can catch when our child process + // dies and set the exit status correctly. + + Host::StartMonitoringChildProcess (Process::SetProcessExitStatus, NULL, GetID(), false); + + if (m_arch_spec == ArchSpec("arm")) + { + // On ARM we want the actual target triple of the OS to get the + // most capable ARM slice for the process. Since this plug-in is + // only used for doing native debugging this will work. + m_target_triple = Host::GetTargetTriple(); + } + else + { + // We want the arch of the process, and the vendor and OS from the + // host OS. + StreamString triple; + + triple.Printf("%s-%s-%s", + m_arch_spec.AsCString(), + Host::GetVendorString().AsCString("apple"), + Host::GetOSString().AsCString("darwin")); + + m_target_triple.SetCString(triple.GetString().c_str()); + } + } +} + +void +ProcessMacOSX::DidLaunch () +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::DidLaunch()"); + DidLaunchOrAttach (); + if (m_dynamic_loader_ap.get()) + m_dynamic_loader_ap->DidLaunch(); +} + +void +ProcessMacOSX::DidAttach () +{ + DidLaunchOrAttach (); + if (m_dynamic_loader_ap.get()) + m_dynamic_loader_ap->DidAttach(); +} + +Error +ProcessMacOSX::WillAttach (lldb::pid_t pid) +{ + return WillLaunchOrAttach (); +} + +Error +ProcessMacOSX::DoResume () +{ + Error error; + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::Resume()"); + const StateType state = m_private_state.GetValue(); + + if (CanResume(state)) + { + error = PrivateResume(LLDB_INVALID_THREAD_ID); + } + else if (state == eStateRunning) + { + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "Resume() - task 0x%x is running, ignoring...", Task().GetTaskPort()); + } + else + { + error.SetErrorStringWithFormat("task 0x%x can't continue, ignoring...", Task().GetTaskPort()); + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "Resume() - task 0x%x can't continue, ignoring...", Task().GetTaskPort()); + } + return error; +} + +size_t +ProcessMacOSX::GetSoftwareBreakpointTrapOpcode (BreakpointSite* bp_site) +{ + const uint8_t *trap_opcode = NULL; + uint32_t trap_opcode_size = 0; + + static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 }; + //static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE }; + static const uint8_t g_ppc_breakpoint_opcode[] = { 0x7F, 0xC0, 0x00, 0x08 }; + static const uint8_t g_i386_breakpoint_opcode[] = { 0xCC }; + + switch (m_arch_spec.GetCPUType()) + { + case CPU_TYPE_ARM: + // TODO: fill this in for ARM. We need to dig up the symbol for + // the address in the breakpoint locaiton and figure out if it is + // an ARM or Thumb breakpoint. + trap_opcode = g_arm_breakpoint_opcode; + trap_opcode_size = sizeof(g_arm_breakpoint_opcode); + break; + + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + trap_opcode = g_ppc_breakpoint_opcode; + trap_opcode_size = sizeof(g_ppc_breakpoint_opcode); + break; + + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + trap_opcode = g_i386_breakpoint_opcode; + trap_opcode_size = sizeof(g_i386_breakpoint_opcode); + break; + + default: + assert(!"Unhandled architecture in ProcessMacOSX::GetSoftwareBreakpointTrapOpcode()"); + return 0; + } + + if (trap_opcode && trap_opcode_size) + { + if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) + return trap_opcode_size; + } + return 0; +} +uint32_t +ProcessMacOSX::UpdateThreadListIfNeeded () +{ + // locker will keep a mutex locked until it goes out of scope + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD); + if (log && log->GetMask().IsSet(PD_LOG_VERBOSE)) + log->Printf ("ProcessMacOSX::%s (pid = %4.4x)", __FUNCTION__, GetID()); + + const uint32_t stop_id = GetStopID(); + if (m_thread_list.GetSize(false) == 0 || stop_id != m_thread_list.GetStopID()) + { + // Update the thread list's stop id immediately so we don't recurse into this function. + thread_array_t thread_list = NULL; + mach_msg_type_number_t thread_list_count = 0; + task_t task = Task().GetTaskPort(); + Error err(::task_threads (task, &thread_list, &thread_list_count), eErrorTypeMachKernel); + + if (log || err.Fail()) + err.PutToLog(log, "::task_threads ( task = 0x%4.4x, thread_list => %p, thread_list_count => %u )", task, thread_list, thread_list_count); + + if (err.GetError() == KERN_SUCCESS && thread_list_count > 0) + { + ThreadList curr_thread_list (this); + curr_thread_list.SetStopID(stop_id); + + size_t idx; + // Iterator through the current thread list and see which threads + // we already have in our list (keep them), which ones we don't + // (add them), and which ones are not around anymore (remove them). + for (idx = 0; idx < thread_list_count; ++idx) + { + const lldb::tid_t tid = thread_list[idx]; + ThreadSP thread_sp(GetThreadList().FindThreadByID (tid, false)); + if (thread_sp.get() == NULL) + thread_sp.reset (new ThreadMacOSX (*this, tid)); + curr_thread_list.AddThread(thread_sp); + } + + m_thread_list = curr_thread_list; + + // Free the vm memory given to us by ::task_threads() + vm_size_t thread_list_size = (vm_size_t) (thread_list_count * sizeof (lldb::tid_t)); + ::vm_deallocate (::mach_task_self(), + (vm_address_t)thread_list, + thread_list_size); + } + } + return GetThreadList().GetSize(false); +} + + +void +ProcessMacOSX::RefreshStateAfterStop () +{ + // If we are attaching, let our dynamic loader plug-in know so it can get + // an initial list of shared libraries. + + // We must be attaching if we don't already have a valid architecture + if (!m_arch_spec.IsValid()) + { + Module *exe_module = GetTarget().GetExecutableModule().get(); + if (exe_module) + m_arch_spec = exe_module->GetArchitecture(); + } + // Discover new threads: + UpdateThreadListIfNeeded (); + + // Let all threads recover from stopping and do any clean up based + // on the previous thread state (if any). + m_thread_list.RefreshStateAfterStop(); + + // Let each thread know of any exceptions + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + task_t task = Task().GetTaskPort(); + size_t i; + for (i=0; i<m_exception_messages.size(); ++i) + { + // Let the thread list figure use the ProcessMacOSX to forward all exceptions + // on down to each thread. + if (m_exception_messages[i].state.task_port == task) + { + ThreadSP thread_sp(m_thread_list.FindThreadByID(m_exception_messages[i].state.thread_port)); + if (thread_sp.get()) + { + ThreadMacOSX *macosx_thread = (ThreadMacOSX *)thread_sp.get(); + macosx_thread->NotifyException (m_exception_messages[i].state); + } + } + if (log) + m_exception_messages[i].PutToLog(log); + } + +} + +Error +ProcessMacOSX::DoHalt () +{ + return Signal (SIGSTOP); +} + +Error +ProcessMacOSX::WillDetach () +{ + Error error; + const StateType state = m_private_state.GetValue(); + + if (IsRunning(state)) + { + error.SetErrorToGenericError(); + error.SetErrorString("Process must be stopped in order to detach."); + } + return error; +} + +Error +ProcessMacOSX::DoSIGSTOP (bool clear_all_breakpoints) +{ + Error error; + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS); + + if (log) + log->Printf ("ProcessMacOSX::DoSIGSTOP()"); + EventSP event_sp; + TimeValue timeout_time; + + StateType state = m_private_state.GetValue(); + + lldb::pid_t pid = GetID(); + + if (IsRunning(state)) + { + // If our process is running, we need to SIGSTOP it so we can detach. + if (log) + log->Printf ("ProcessMacOSX::DoDestroy() - kill (%i, SIGSTOP)", pid); + + // Send the SIGSTOP and wait a few seconds for it to stop + + // Pause the Private State Thread so it doesn't intercept the events we need to wait for. + PausePrivateStateThread(); + + m_thread_list.DiscardThreadPlans(); + + // First jettison all the current thread plans, since we want to make sure it + // really just stops. + + if (::kill (pid, SIGSTOP) == 0) + error.Clear(); + else + error.SetErrorToErrno(); + + if (error.Fail()) + error.PutToLog(log, "::kill (pid = %i, SIGSTOP)", pid); + + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds(2); + + state = WaitForStateChangedEventsPrivate (&timeout_time, event_sp); + + // Resume the private state thread at this point. + ResumePrivateStateThread(); + + if (!StateIsStoppedState (state)) + { + if (log) + log->Printf("ProcessMacOSX::DoSIGSTOP() failed to stop after sending SIGSTOP"); + return error; + } + if (clear_all_breakpoints) + GetTarget().DisableAllBreakpoints(); + } + else if (!HasExited(state)) + { + if (clear_all_breakpoints) + GetTarget().DisableAllBreakpoints(); + +// const uint32_t num_threads = GetNumThreads(); +// for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) +// { +// Thread *thread = GetThreadAtIndex(thread_idx); +// thread->SetResumeState(eStateRunning); +// if (thread_idx == 0) +// thread->SetResumeSignal(SIGSTOP); +// } + + // Our process was stopped, so resume it and then SIGSTOP it so we can + // detach. + // But discard all the thread plans first, so we don't keep going because we + // are in mid-plan. + + // Pause the Private State Thread so it doesn't intercept the events we need to wait for. + PausePrivateStateThread(); + + m_thread_list.DiscardThreadPlans(); + + if (::kill (pid, SIGSTOP) == 0) + error.Clear(); + else + error.SetErrorToErrno(); + + if (log || error.Fail()) + error.PutToLog(log, "ProcessMacOSX::DoSIGSTOP() ::kill (pid = %i, SIGSTOP)", pid); + + error = PrivateResume(LLDB_INVALID_THREAD_ID); + + // Wait a few seconds for our process to resume + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds(2); + state = WaitForStateChangedEventsPrivate (&timeout_time, event_sp); + + // Make sure the process resumed + if (StateIsStoppedState (state)) + { + if (log) + log->Printf ("ProcessMacOSX::DoSIGSTOP() couldn't resume process, state = %s", StateAsCString(state)); + error.SetErrorStringWithFormat("ProcessMacOSX::DoSIGSTOP() couldn't resume process, state = %s", StateAsCString(state)); + } + else + { + // Send the SIGSTOP and wait a few seconds for it to stop + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds(2); + state = WaitForStateChangedEventsPrivate (&timeout_time, event_sp); + if (!StateIsStoppedState (state)) + { + if (log) + log->Printf("ProcessMacOSX::DoSIGSTOP() failed to stop after sending SIGSTOP"); + error.SetErrorString("ProcessMacOSX::DoSIGSTOP() failed to stop after sending SIGSTOP"); + } + } + // Resume the private state thread at this point. + ResumePrivateStateThread(); + } + + return error; +} + +Error +ProcessMacOSX::DoDestroy () +{ + Error error; + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS); + if (log) + log->Printf ("ProcessMacOSX::DoDestroy()"); + + error = DoSIGSTOP (true); + if (error.Success()) + { + StopSTDIOThread(true); + + if (log) + log->Printf ("ProcessMacOSX::DoDestroy() DoSIGSTOP succeeded"); + const StateType state = m_private_state.GetValue(); + // Scope for "locker" so we can reply to all of our exceptions (the SIGSTOP + // exception). + { + Mutex::Locker locker(m_exception_messages_mutex); + ReplyToAllExceptions(); + } + if (log) + log->Printf ("ProcessMacOSX::DoDestroy() replied to all exceptions"); + + // Shut down the exception thread and cleanup our exception remappings + Task().ShutDownExceptionThread(); + + if (log) + log->Printf ("ProcessMacOSX::DoDestroy() exception thread has been shutdown"); + + if (!HasExited(state)) + { + lldb::pid_t pid = GetID(); + + // Detach from our process while we are stopped. + errno = 0; + + // Detach from our process + ::ptrace (PT_KILL, pid, 0, 0); + + error.SetErrorToErrno(); + + if (log || error.Fail()) + error.PutToLog (log, "::ptrace (PT_KILL, %u, 0, 0)", pid); + + // Resume our task and let the SIGKILL do its thing. The thread named + // "ProcessMacOSX::WaitForChildProcessToExit(void*)" will catch the + // process exiting, so we don't need to set our state to exited in this + // function. + Task().Resume(); + } + + // NULL our task out as we have already retored all exception ports + Task().Clear(); + + // Clear out any notion of the process we once were + Clear(); + } + return error; +} + +ByteOrder +ProcessMacOSX::GetByteOrder () const +{ + return m_byte_order; +} + +//------------------------------------------------------------------ +// Process Queries +//------------------------------------------------------------------ + +bool +ProcessMacOSX::IsAlive () +{ + return MachTask::IsValid (Task().GetTaskPort()); +} + +lldb::addr_t +ProcessMacOSX::GetImageInfoAddress() +{ + return Task().GetDYLDAllImageInfosAddress(); +} + +DynamicLoader * +ProcessMacOSX::GetDynamicLoader() +{ + return m_dynamic_loader_ap.get(); +} + +//------------------------------------------------------------------ +// Process Memory +//------------------------------------------------------------------ + +size_t +ProcessMacOSX::DoReadMemory (lldb::addr_t addr, void *buf, size_t size, Error& error) +{ + return Task().ReadMemory(addr, buf, size, error); +} + +size_t +ProcessMacOSX::DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, Error& error) +{ + return Task().WriteMemory(addr, buf, size, error); +} + +lldb::addr_t +ProcessMacOSX::DoAllocateMemory (size_t size, uint32_t permissions, Error& error) +{ + return Task().AllocateMemory (size, permissions, error); +} + +Error +ProcessMacOSX::DoDeallocateMemory (lldb::addr_t ptr) +{ + return Task().DeallocateMemory (ptr); +} + +//------------------------------------------------------------------ +// Process STDIO +//------------------------------------------------------------------ + +size_t +ProcessMacOSX::GetSTDOUT (char *buf, size_t buf_size, Error &error) +{ + error.Clear(); + Mutex::Locker locker(m_stdio_mutex); + size_t bytes_available = m_stdout_data.size(); + if (bytes_available > 0) + { + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::%s (&%p[%u]) ...", __FUNCTION__, buf, buf_size); + if (bytes_available > buf_size) + { + memcpy(buf, m_stdout_data.data(), buf_size); + m_stdout_data.erase(0, buf_size); + bytes_available = buf_size; + } + else + { + memcpy(buf, m_stdout_data.data(), bytes_available); + m_stdout_data.clear(); + + //ResetEventBits(eBroadcastBitSTDOUT); + } + } + return bytes_available; +} + +size_t +ProcessMacOSX::GetSTDERR (char *buf, size_t buf_size, Error &error) +{ + error.Clear(); + return 0; +} + +size_t +ProcessMacOSX::PutSTDIN (const char *buf, size_t buf_size, Error &error) +{ + if (m_child_stdin == -1) + { + error.SetErrorString ("Invalid child stdin handle."); + } + else + { + ssize_t bytes_written = ::write (m_child_stdin, buf, buf_size); + if (bytes_written == -1) + error.SetErrorToErrno(); + else + { + error.Clear(); + return bytes_written; + } + } + return 0; +} + +Error +ProcessMacOSX::EnableBreakpoint (BreakpointSite *bp_site) +{ + Error error; + assert (bp_site != NULL); + const lldb::addr_t addr = bp_site->GetLoadAddress(); + const lldb::user_id_t site_id = bp_site->GetID(); + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_BREAKPOINTS); + if (log) + log->Printf ("ProcessMacOSX::EnableBreakpoint (site_id = %d) addr = 0x%8.8llx", site_id, (uint64_t)addr); + + if (bp_site->IsEnabled()) + { + if (log) + log->Printf ("ProcessMacOSX::EnableBreakpoint (site_id = %d) addr = 0x%8.8llx -- SUCCESS (already enabled)", site_id, (uint64_t)addr); + return error; + } + + if (bp_site->HardwarePreferred()) + { + ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(bp_site->GetThreadID()).get(); + if (thread) + { + bp_site->SetHardwareIndex (thread->SetHardwareBreakpoint(bp_site)); + if (bp_site->IsHardware()) + { + bp_site->SetEnabled(true); + return error; + } + } + } + + // Just let lldb::Process::EnableSoftwareBreakpoint() handle everything... + return EnableSoftwareBreakpoint (bp_site); +} + +Error +ProcessMacOSX::DisableBreakpoint (BreakpointSite *bp_site) +{ + Error error; + assert (bp_site != NULL); + const lldb::addr_t addr = bp_site->GetLoadAddress(); + const lldb::user_id_t site_id = bp_site->GetID(); + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_BREAKPOINTS); + if (log) + log->Printf ("ProcessMacOSX::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx", site_id, (uint64_t)addr); + + if (bp_site->IsHardware()) + { + ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(bp_site->GetThreadID()).get(); + if (thread) + { + if (thread->ClearHardwareBreakpoint(bp_site)) + { + bp_site->SetEnabled(false); + if (log) + log->Printf ("ProcessMacOSX::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx -- SUCCESS (hardware)", site_id, (uint64_t)addr); + return error; + } + } + error.SetErrorString("hardware breakpoints are no supported"); + return error; + } + + // Just let lldb::Process::EnableSoftwareBreakpoint() handle everything... + return DisableSoftwareBreakpoint (bp_site); +} + +Error +ProcessMacOSX::EnableWatchpoint (WatchpointLocation *wp) +{ + Error error; + if (wp) + { + lldb::user_id_t watchID = wp->GetID(); + lldb::addr_t addr = wp->GetLoadAddress(); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_WATCHPOINTS); + if (log) + log->Printf ("ProcessMacOSX::EnableWatchpoint(watchID = %d)", watchID); + if (wp->IsEnabled()) + { + if (log) + log->Printf("ProcessMacOSX::EnableWatchpoint(watchID = %d) addr = 0x%8.8llx: watchpoint already enabled.", watchID, (uint64_t)addr); + return error; + } + else + { + ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(wp->GetThreadID()).get(); + if (thread) + { + wp->SetHardwareIndex (thread->SetHardwareWatchpoint (wp)); + if (wp->IsHardware ()) + { + wp->SetEnabled(true); + return error; + } + } + else + { + error.SetErrorString("Watchpoints currently only support thread specific watchpoints."); + } + } + } + return error; +} + +Error +ProcessMacOSX::DisableWatchpoint (WatchpointLocation *wp) +{ + Error error; + if (wp) + { + lldb::user_id_t watchID = wp->GetID(); + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_WATCHPOINTS); + + lldb::addr_t addr = wp->GetLoadAddress(); + if (log) + log->Printf ("ProcessMacOSX::DisableWatchpoint (watchID = %d) addr = 0x%8.8llx", watchID, (uint64_t)addr); + + if (wp->IsHardware()) + { + ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(wp->GetThreadID()).get(); + if (thread) + { + if (thread->ClearHardwareWatchpoint (wp)) + { + wp->SetEnabled(false); + if (log) + log->Printf ("ProcessMacOSX::Disablewatchpoint (watchID = %d) addr = 0x%8.8llx (hardware) => success", watchID, (uint64_t)addr); + return error; + } + } + } + // TODO: clear software watchpoints if we implement them + error.SetErrorToGenericError(); + } + else + { + error.SetErrorString("Watchpoint location argument was NULL."); + } + return error; +} + + +static ProcessMacOSX::CreateArchCalback +ArchCallbackMap(const ArchSpec& arch_spec, ProcessMacOSX::CreateArchCalback callback, bool add ) +{ + // We must wrap the "g_arch_map" file static in a function to avoid + // any global constructors so we don't get a build verification error + typedef std::multimap<ArchSpec, ProcessMacOSX::CreateArchCalback> ArchToProtocolMap; + static ArchToProtocolMap g_arch_map; + + if (add) + { + g_arch_map.insert(std::make_pair(arch_spec, callback)); + return callback; + } + else + { + ArchToProtocolMap::const_iterator pos = g_arch_map.find(arch_spec); + if (pos != g_arch_map.end()) + { + return pos->second; + } + } + return NULL; +} + +void +ProcessMacOSX::AddArchCreateCallback(const ArchSpec& arch_spec, CreateArchCalback callback) +{ + ArchCallbackMap (arch_spec, callback, true); +} + +ProcessMacOSX::CreateArchCalback +ProcessMacOSX::GetArchCreateCallback() +{ + return ArchCallbackMap (m_arch_spec, NULL, false); +} + +void +ProcessMacOSX::Clear() +{ + // Clear any cached thread list while the pid and task are still valid + + Task().Clear(); + // Now clear out all member variables + CloseChildFileDescriptors(); + + m_flags = eFlagsNone; + m_thread_list.Clear(); + { + Mutex::Locker locker(m_exception_messages_mutex); + m_exception_messages.clear(); + } + +} + +bool +ProcessMacOSX::StartSTDIOThread() +{ + // If we created and own the child STDIO file handles, then we track the + // STDIO ourselves, else we let whomever owns these file handles track + // the IO themselves. + if (m_stdio_ours) + { + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::%s ( )", __FUNCTION__); + // Create the thread that watches for the child STDIO + m_stdio_thread = Host::ThreadCreate ("<lldb.process.process-macosx.stdio>", ProcessMacOSX::STDIOThread, this, NULL); + return m_stdio_thread != LLDB_INVALID_HOST_THREAD; + } + return false; +} + + +void +ProcessMacOSX::StopSTDIOThread(bool close_child_fds) +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::%s ( )", __FUNCTION__); + // Stop the stdio thread + if (m_stdio_thread != LLDB_INVALID_HOST_THREAD) + { + Host::ThreadCancel (m_stdio_thread, NULL); + thread_result_t result = NULL; + Host::ThreadJoin (m_stdio_thread, &result, NULL); + if (close_child_fds) + CloseChildFileDescriptors(); + else + { + // We may have given up control of these file handles, so just + // set them to invalid values so the STDIO thread can exit when + // we interrupt it with pthread_cancel... + m_child_stdin = -1; + m_child_stdout = -1; + m_child_stderr = -1; + } + } +} + + +void * +ProcessMacOSX::STDIOThread(void *arg) +{ + ProcessMacOSX *proc = (ProcessMacOSX*) arg; + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS); + if (log) + log->Printf ("ProcessMacOSX::%s (arg = %p) thread starting...", __FUNCTION__, arg); + + // We start use a base and more options so we can control if we + // are currently using a timeout on the mach_msg. We do this to get a + // bunch of related exceptions on our exception port so we can process + // then together. When we have multiple threads, we can get an exception + // per thread and they will come in consecutively. The main thread loop + // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT + // flag set in the options, so we will wait forever for an exception on + // our exception port. After we get one exception, we then will use the + // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current + // exceptions for our process. After we have received the last pending + // exception, we will get a timeout which enables us to then notify + // our main thread that we have an exception bundle avaiable. We then wait + // for the main thread to tell this exception thread to start trying to get + // exceptions messages again and we start again with a mach_msg read with + // infinite timeout. + Error err; + int stdout_fd = proc->GetStdoutFileDescriptor(); + int stderr_fd = proc->GetStderrFileDescriptor(); + if (stdout_fd == stderr_fd) + stderr_fd = -1; + + while (stdout_fd >= 0 || stderr_fd >= 0) + { + //::pthread_testcancel (); + + fd_set read_fds; + FD_ZERO (&read_fds); + if (stdout_fd >= 0) + FD_SET (stdout_fd, &read_fds); + if (stderr_fd >= 0) + FD_SET (stderr_fd, &read_fds); + int nfds = std::max<int>(stdout_fd, stderr_fd) + 1; + + int num_set_fds = select (nfds, &read_fds, NULL, NULL, NULL); + if (log) + log->Printf("select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); + + if (num_set_fds < 0) + { + int select_errno = errno; + if (log) + { + err.SetError (select_errno, eErrorTypePOSIX); + err.LogIfError(log, "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); + } + + switch (select_errno) + { + case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate the requested number of file descriptors, or we have non-blocking IO + break; + case EBADF: // One of the descriptor sets specified an invalid descriptor. + return NULL; + break; + case EINTR: // A signal was delivered before the time limit expired and before any of the selected events occurred. + case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. + default: // Other unknown error + break; + } + } + else if (num_set_fds == 0) + { + } + else + { + char s[1024]; + s[sizeof(s)-1] = '\0'; // Ensure we have NULL termination + int bytes_read = 0; + if (stdout_fd >= 0 && FD_ISSET (stdout_fd, &read_fds)) + { + do + { + bytes_read = ::read (stdout_fd, s, sizeof(s)-1); + if (bytes_read < 0) + { + int read_errno = errno; + if (log) + log->Printf("read (stdout_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); + } + else if (bytes_read == 0) + { + // EOF... + if (log) + log->Printf("read (stdout_fd, ) => %d (reached EOF for child STDOUT)", bytes_read); + stdout_fd = -1; + } + else if (bytes_read > 0) + { + proc->AppendSTDOUT(s, bytes_read); + } + + } while (bytes_read > 0); + } + + if (stderr_fd >= 0 && FD_ISSET (stderr_fd, &read_fds)) + { + do + { + bytes_read = ::read (stderr_fd, s, sizeof(s)-1); + if (bytes_read < 0) + { + int read_errno = errno; + if (log) + log->Printf("read (stderr_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); + } + else if (bytes_read == 0) + { + // EOF... + if (log) + log->Printf("read (stderr_fd, ) => %d (reached EOF for child STDERR)", bytes_read); + stderr_fd = -1; + } + else if (bytes_read > 0) + { + proc->AppendSTDOUT(s, bytes_read); + } + + } while (bytes_read > 0); + } + } + } + + if (log) + log->Printf("ProcessMacOSX::%s (%p): thread exiting...", __FUNCTION__, arg); + + return NULL; +} + +Error +ProcessMacOSX::DoSignal (int signal) +{ + Error error; + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS); + if (log) + log->Printf ("ProcessMacOSX::DoSignal (signal = %d)", signal); + if (::kill (GetID(), signal) != 0) + { + error.SetErrorToErrno(); + error.LogIfError(log, "ProcessMacOSX::DoSignal (%d)", signal); + } + return error; +} + + +Error +ProcessMacOSX::DoDetach() +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS); + if (log) + log->Printf ("ProcessMacOSX::DoDetach()"); + + Error error (DoSIGSTOP (true)); + if (error.Success()) + { + CloseChildFileDescriptors (); + + // Scope for "locker" so we can reply to all of our exceptions (the SIGSTOP + // exception). + { + Mutex::Locker locker(m_exception_messages_mutex); + ReplyToAllExceptions(); + } + + // Shut down the exception thread and cleanup our exception remappings + Task().ShutDownExceptionThread(); + + lldb::pid_t pid = GetID(); + + // Detach from our process while we are stopped. + errno = 0; + + // Detach from our process + ::ptrace (PT_DETACH, pid, (caddr_t)1, 0); + + error.SetErrorToErrno(); + + if (log || error.Fail()) + error.PutToLog(log, "::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid); + + // Resume our task + Task().Resume(); + + // NULL our task out as we have already retored all exception ports + Task().Clear(); + + // Clear out any notion of the process we once were + Clear(); + + SetPrivateState (eStateDetached); + } + return error; +} + + + +Error +ProcessMacOSX::ReplyToAllExceptions() +{ + Error error; + Mutex::Locker locker(m_exception_messages_mutex); + if (m_exception_messages.empty() == false) + { + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + + MachException::Message::iterator pos; + MachException::Message::iterator begin = m_exception_messages.begin(); + MachException::Message::iterator end = m_exception_messages.end(); + for (pos = begin; pos != end; ++pos) + { + int resume_signal = -1; + ThreadSP thread_sp = m_thread_list.FindThreadByID(pos->state.thread_port); + if (thread_sp.get()) + resume_signal = thread_sp->GetResumeSignal(); + if (log) + log->Printf ("Replying to exception %d for thread 0x%4.4x (resume_signal = %i).", std::distance(begin, pos), thread_sp->GetID(), resume_signal); + Error curr_error (pos->Reply (Task().GetTaskPort(), GetID(), resume_signal)); + + // Only report the first error + if (curr_error.Fail() && error.Success()) + error = curr_error; + + error.LogIfError(log, "Error replying to exception"); + } + + // Erase all exception message as we should have used and replied + // to them all already. + m_exception_messages.clear(); + } + return error; +} + + +Error +ProcessMacOSX::PrivateResume (lldb::tid_t tid) +{ + + Mutex::Locker locker(m_exception_messages_mutex); + Error error (ReplyToAllExceptions()); + + // Let the thread prepare to resume and see if any threads want us to + // step over a breakpoint instruction (ProcessWillResume will modify + // the value of stepOverBreakInstruction). + //StateType process_state = m_thread_list.ProcessWillResume(this); + + // Set our state accordingly + SetPrivateState (eStateRunning); + + // Now resume our task. + error = Task().Resume(); + return error; +} + +// Called by the exception thread when an exception has been received from +// our process. The exception message is completely filled and the exception +// data has already been copied. +void +ProcessMacOSX::ExceptionMessageReceived (const MachException::Message& exceptionMessage) +{ + Mutex::Locker locker(m_exception_messages_mutex); + + if (m_exception_messages.empty()) + Task().Suspend(); + + ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "ProcessMacOSX::ExceptionMessageReceived ( )"); + + // Use a locker to automatically unlock our mutex in case of exceptions + // Add the exception to our internal exception stack + m_exception_messages.push_back(exceptionMessage); +} + + +//bool +//ProcessMacOSX::GetProcessInfo (struct kinfo_proc* proc_info) +//{ +// int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, GetID() }; +// size_t buf_size = sizeof(struct kinfo_proc); +// +// if (::sysctl (mib, (unsigned)(sizeof(mib)/sizeof(int)), &proc_info, &buf_size, NULL, 0) == 0) +// return buf_size > 0; +// +// return false; +//} +// +// +void +ProcessMacOSX::ExceptionMessageBundleComplete() +{ + // We have a complete bundle of exceptions for our child process. + Mutex::Locker locker(m_exception_messages_mutex); + ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "%s: %d exception messages.", __PRETTY_FUNCTION__, m_exception_messages.size()); + if (!m_exception_messages.empty()) + { + SetPrivateState (eStateStopped); + } + else + { + ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "%s empty exception messages bundle.", __PRETTY_FUNCTION__, m_exception_messages.size()); + } +} + +bool +ProcessMacOSX::ReleaseChildFileDescriptors ( int *stdin_fileno, int *stdout_fileno, int *stderr_fileno ) +{ + if (stdin_fileno) + *stdin_fileno = m_child_stdin; + if (stdout_fileno) + *stdout_fileno = m_child_stdout; + if (stderr_fileno) + *stderr_fileno = m_child_stderr; + // Stop the stdio thread if we have one, but don't have it close the child + // file descriptors since we are giving control of these descriptors to the + // caller + bool close_child_fds = false; + StopSTDIOThread(close_child_fds); + return true; +} + +void +ProcessMacOSX::AppendSTDOUT (const char* s, size_t len) +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::%s (<%d> %s) ...", __FUNCTION__, len, s); + Mutex::Locker locker(m_stdio_mutex); + m_stdout_data.append(s, len); + + // FIXME: Make a real data object for this and put it out. + BroadcastEventIfUnique (eBroadcastBitSTDOUT); +} + +lldb::pid_t +ProcessMacOSX::LaunchForDebug +( + const char *path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + PDLaunchType launch_type, + Error &launch_err) +{ + // Clear out and clean up from any current state + Clear(); + + m_arch_spec = arch_spec; + + if (launch_type == eLaunchDefault) + launch_type = eLaunchPosixSpawn; + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS); + if (log) + log->Printf ("%s( path = '%s', argv = %p, envp = %p, launch_type = %u )", __FUNCTION__, path, argv, envp, launch_type); + + // Fork a child process for debugging + SetPrivateState (eStateLaunching); + switch (launch_type) + { + case eLaunchForkExec: + SetID(ProcessMacOSX::ForkChildForPTraceDebugging(path, argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, this, launch_err)); + break; + + case eLaunchPosixSpawn: + SetID(ProcessMacOSX::PosixSpawnChildForPTraceDebugging(path, argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, this, launch_err)); + break; + +#if defined (__arm__) + + case eLaunchSpringBoard: + { + const char *app_ext = strstr(path, ".app"); + if (app_ext != NULL) + { + std::string app_bundle_path(path, app_ext + strlen(".app")); + return SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, launch_err); + } + } + break; + +#endif + + default: + // Invalid launch + launch_err.SetErrorToGenericError (); + return LLDB_INVALID_PROCESS_ID; + } + + lldb::pid_t pid = GetID(); + + if (pid == LLDB_INVALID_PROCESS_ID) + { + // If we don't have a valid process ID and no one has set the error, + // then return a generic error + if (launch_err.Success()) + launch_err.SetErrorToGenericError (); + } + else + { + // Make sure we can get our task port before going any further + Task().GetTaskPortForProcessID (launch_err); + + // If that goes well then kick off our exception thread + if (launch_err.Success()) + Task().StartExceptionThread(launch_err); + + if (launch_err.Success()) + { + //m_path = path; +// size_t i; +// if (argv) +// { +// char const *arg; +// for (i=0; (arg = argv[i]) != NULL; i++) +// m_args.push_back(arg); +// } + + StartSTDIOThread(); + + if (launch_type == eLaunchPosixSpawn) + { + + //SetState (eStateAttaching); + errno = 0; + if (::ptrace (PT_ATTACHEXC, pid, 0, 0) == 0) + launch_err.Clear(); + else + launch_err.SetErrorToErrno(); + + if (launch_err.Fail() || log) + launch_err.PutToLog(log, "::ptrace (PT_ATTACHEXC, pid = %i, 0, 0 )", pid); + + if (launch_err.Success()) + m_flags.Set (eFlagsAttached); + else + SetPrivateState (eStateExited); + } + else + { + launch_err.Clear(); + } + } + else + { + // We were able to launch the process, but not get its task port + // so now we need to make it sleep with da fishes. + SetID(LLDB_INVALID_PROCESS_ID); + ::ptrace (PT_KILL, pid, 0, 0 ); + ::kill (pid, SIGCONT); + pid = LLDB_INVALID_PROCESS_ID; + } + + } + return pid; +} + +lldb::pid_t +ProcessMacOSX::PosixSpawnChildForPTraceDebugging +( + const char *path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSX* process, + Error &err +) +{ + posix_spawnattr_t attr; + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS); + + Error local_err; // Errors that don't affect the spawning. + if (log) + log->Printf ("%s ( path='%s', argv=%p, envp=%p, process )", __FUNCTION__, path, argv, envp); + err.SetError( ::posix_spawnattr_init (&attr), eErrorTypePOSIX); + if (err.Fail() || log) + err.PutToLog(log, "::posix_spawnattr_init ( &attr )"); + if (err.Fail()) + return LLDB_INVALID_PROCESS_ID; + + err.SetError( ::posix_spawnattr_setflags (&attr, POSIX_SPAWN_START_SUSPENDED), eErrorTypePOSIX); + if (err.Fail() || log) + err.PutToLog(log, "::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED )"); + if (err.Fail()) + return LLDB_INVALID_PROCESS_ID; + +#if !defined(__arm__) + + // We don't need to do this for ARM, and we really shouldn't now that we + // have multiple CPU subtypes and no posix_spawnattr call that allows us + // to set which CPU subtype to launch... + cpu_type_t cpu = arch_spec.GetCPUType(); + if (cpu != 0 && cpu != CPU_TYPE_ANY && cpu != LLDB_INVALID_CPUTYPE) + { + size_t ocount = 0; + err.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &ocount), eErrorTypePOSIX); + if (err.Fail() || log) + err.PutToLog(log, "::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %zu )", cpu, ocount); + + if (err.Fail() != 0 || ocount != 1) + return LLDB_INVALID_PROCESS_ID; + } + +#endif + + lldb_utility::PseudoTerminal pty; + + posix_spawn_file_actions_t file_actions; + err.SetError( ::posix_spawn_file_actions_init (&file_actions), eErrorTypePOSIX); + int file_actions_valid = err.Success(); + if (!file_actions_valid || log) + err.PutToLog(log, "::posix_spawn_file_actions_init ( &file_actions )"); + Error stdio_err; + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + if (file_actions_valid) + { + // If the user specified any STDIO files, then use those + if (stdin_path || stdout_path || stderr_path) + { + process->SetSTDIOIsOurs(false); + if (stderr_path != NULL && stderr_path[0]) + { + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDERR_FILENO, stderr_path, O_RDWR, 0), eErrorTypePOSIX); + if (stdio_err.Fail() || log) + stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDERR_FILENO, path = '%s', oflag = O_RDWR, mode = 0 )", stderr_path); + } + + if (stdin_path != NULL && stdin_path[0]) + { + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO, stdin_path, O_RDONLY, 0), eErrorTypePOSIX); + if (stdio_err.Fail() || log) + stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDIN_FILENO, path = '%s', oflag = O_RDONLY, mode = 0 )", stdin_path); + } + + if (stdout_path != NULL && stdout_path[0]) + { + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDOUT_FILENO, stdout_path, O_WRONLY, 0), eErrorTypePOSIX); + if (stdio_err.Fail() || log) + stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDOUT_FILENO, path = '%s', oflag = O_WRONLY, mode = 0 )", stdout_path); + } + } + else + { + // The user did not specify any STDIO files, use a pseudo terminal. + // Callers can then access the file handles using the + // ProcessMacOSX::ReleaseChildFileDescriptors() function, otherwise + // this class will spawn a thread that tracks STDIO and buffers it. + process->SetSTDIOIsOurs(true); + char error_str[1024]; + if (pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, error_str, sizeof(error_str))) + { + const char* slave_name = pty.GetSlaveName(error_str, sizeof(error_str)); + if (slave_name == NULL) + slave_name = "/dev/null"; + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDERR_FILENO, slave_name, O_RDWR|O_NOCTTY, 0), eErrorTypePOSIX); + if (stdio_err.Fail() || log) + stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDERR_FILENO, path = '%s', oflag = O_RDWR|O_NOCTTY, mode = 0 )", slave_name); + + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO, slave_name, O_RDONLY|O_NOCTTY, 0), eErrorTypePOSIX); + if (stdio_err.Fail() || log) + stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDIN_FILENO, path = '%s', oflag = O_RDONLY|O_NOCTTY, mode = 0 )", slave_name); + + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDOUT_FILENO, slave_name, O_WRONLY|O_NOCTTY, 0), eErrorTypePOSIX); + if (stdio_err.Fail() || log) + stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDOUT_FILENO, path = '%s', oflag = O_WRONLY|O_NOCTTY, mode = 0 )", slave_name); + } + else + { + if (error_str[0]) + stdio_err.SetErrorString(error_str); + else + stdio_err.SetErrorString("Unable to open master side of pty for inferior."); + } + + } + err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), eErrorTypePOSIX); + if (err.Fail() || log) + err.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp); + + if (stdio_err.Success()) + { + // If we have a valid process and we created the STDIO file handles, + // then remember them on our process class so we can spawn a STDIO + // thread and close them when we are done with them. + if (process != NULL && process->STDIOIsOurs()) + { + int master_fd = pty.ReleaseMasterFileDescriptor (); + process->SetChildFileDescriptors (master_fd, master_fd, master_fd); + } + } + } + else + { + err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), eErrorTypePOSIX); + if (err.Fail() || log) + err.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp); + } + + // We have seen some cases where posix_spawnp was returning a valid + // looking pid even when an error was returned, so clear it out + if (err.Fail()) + pid = LLDB_INVALID_PROCESS_ID; + + if (file_actions_valid) + { + local_err.SetError( ::posix_spawn_file_actions_destroy (&file_actions), eErrorTypePOSIX); + if (local_err.Fail() || log) + local_err.PutToLog(log, "::posix_spawn_file_actions_destroy ( &file_actions )"); + } + + return pid; +} + +lldb::pid_t +ProcessMacOSX::ForkChildForPTraceDebugging +( + const char *path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSX* process, + Error &launch_err +) +{ + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + if (stdin_path || stdout_path || stderr_path) + { + assert(!"TODO: ForkChildForPTraceDebugging doesn't currently support fork/exec with user file handles..."); + } + else + { + + // Use a fork that ties the child process's stdin/out/err to a pseudo + // terminal so we can read it in our ProcessMacOSX::STDIOThread + // as unbuffered io. + lldb_utility::PseudoTerminal pty; + char error_str[1024]; + pid = pty.Fork(error_str, sizeof(error_str)); + + if (pid < 0) + { + launch_err.SetErrorString (error_str); + //-------------------------------------------------------------- + // Error during fork. + //-------------------------------------------------------------- + return pid; + } + else if (pid == 0) + { + //-------------------------------------------------------------- + // Child process + //-------------------------------------------------------------- + ::ptrace (PT_TRACE_ME, 0, 0, 0); // Debug this process + ::ptrace (PT_SIGEXC, 0, 0, 0); // Get BSD signals as mach exceptions + + // If our parent is setgid, lets make sure we don't inherit those + // extra powers due to nepotism. + ::setgid (getgid ()); + + // Let the child have its own process group. We need to execute + // this call in both the child and parent to avoid a race condition + // between the two processes. + ::setpgid (0, 0); // Set the child process group to match its pid + + // Sleep a bit to before the exec call + ::sleep (1); + + // Turn this process into + ::execv (path, (char * const *)argv); + // Exit with error code. Child process should have taken + // over in above exec call and if the exec fails it will + // exit the child process below. + ::exit (127); + } + else + { + //-------------------------------------------------------------- + // Parent process + //-------------------------------------------------------------- + // Let the child have its own process group. We need to execute + // this call in both the child and parent to avoid a race condition + // between the two processes. + ::setpgid (pid, pid); // Set the child process group to match its pid + + if (process != NULL) + { + // Release our master pty file descriptor so the pty class doesn't + // close it and so we can continue to use it in our STDIO thread + int master_fd = pty.ReleaseMasterFileDescriptor (); + process->SetChildFileDescriptors (master_fd, master_fd, master_fd); + } + } + } + return pid; +} + +#if defined (__arm__) + +lldb::pid_t +ProcessMacOSX::SBLaunchForDebug +( + const char *path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + Error &launch_err +) +{ + // Clear out and clean up from any current state + Clear(); + + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path); + + // Fork a child process for debugging + SetState(eStateLaunching); + m_pid = ProcessMacOSX::SBLaunchForDebug(path, argv, envp, this, launch_err); + if (m_pid != 0) + { + m_flags |= eFlagsUsingSBS; + //m_path = path; +// size_t i; +// char const *arg; +// for (i=0; (arg = argv[i]) != NULL; i++) +// m_args.push_back(arg); + Task().StartExceptionThread(); + StartSTDIOThread(); + SetState (eStateAttaching); + int err = ptrace (PT_ATTACHEXC, m_pid, 0, 0); + if (err == 0) + { + m_flags |= eFlagsAttached; + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "successfully attached to pid %d", m_pid); + } + else + { + SetState (eStateExited); + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "error: failed to attach to pid %d", m_pid); + } + } + return m_pid; +} + +#include <servers/bootstrap.h> +#include "CFBundle.h" +#include "CFData.h" +#include "CFString.h" + +lldb::pid_t +ProcessMacOSX::SBLaunchForDebug +( + const char *app_bundle_path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSX* process, + Error &launch_err +) +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, process); + CFAllocatorRef alloc = kCFAllocatorDefault; + if (argv[0] == NULL) + return LLDB_INVALID_PROCESS_ID; + + size_t argc = 0; + // Count the number of arguments + while (argv[argc] != NULL) + argc++; + + // Enumerate the arguments + size_t first_launch_arg_idx = 1; + CFReleaser<CFMutableArrayRef> launch_argv; + + if (argv[first_launch_arg_idx]) + { + size_t launch_argc = argc > 0 ? argc - 1 : 0; + launch_argv.reset (::CFArrayCreateMutable (alloc, launch_argc, &kCFTypeArrayCallBacks)); + size_t i; + char const *arg; + CFString launch_arg; + for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++) + { + launch_arg.reset(::CFStringCreateWithCString (alloc, arg, kCFStringEncodingUTF8)); + if (launch_arg.get() != NULL) + CFArrayAppendValue(launch_argv.get(), launch_arg.get()); + else + break; + } + } + + // Next fill in the arguments dictionary. Note, the envp array is of the form + // Variable=value but SpringBoard wants a CF dictionary. So we have to convert + // this here. + + CFReleaser<CFMutableDictionaryRef> launch_envp; + + if (envp[0]) + { + launch_envp.reset(::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + const char *value; + int name_len; + CFString name_string, value_string; + + for (int i = 0; envp[i] != NULL; i++) + { + value = strstr (envp[i], "="); + + // If the name field is empty or there's no =, skip it. Somebody's messing with us. + if (value == NULL || value == envp[i]) + continue; + + name_len = value - envp[i]; + + // Now move value over the "=" + value++; + + name_string.reset(::CFStringCreateWithBytes(alloc, (const UInt8 *) envp[i], name_len, kCFStringEncodingUTF8, false)); + value_string.reset(::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8)); + CFDictionarySetValue (launch_envp.get(), name_string.get(), value_string.get()); + } + } + + CFString stdout_cf_path; + CFString stderr_cf_path; + PseudoTerminal pty; + + if (stdin_path || stdout_path || stderr_path) + { + process->SetSTDIOIsOurs(false); + if (stdout_path) + stdout_cf_path.SetFileSystemRepresentation (stdout_path); + if (stderr_path) + stderr_cf_path.SetFileSystemRepresentation (stderr_path); + } + else + { + process->SetSTDIOIsOurs(true); + PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY); + if (pty_err == PseudoTerminal::success) + { + const char* slave_name = pty.SlaveName(); + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name); + if (slave_name && slave_name[0]) + { + ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO); + stdout_cf_path.SetFileSystemRepresentation (slave_name); + stderr_cf_path.(stdout_cf_path); + } + } + } + + if (stdout_cf_path.get() == NULL) + stdout_cf_path.SetFileSystemRepresentation ("/dev/null"); + if (stderr_cf_path.get() == NULL) + stderr_cf_path.SetFileSystemRepresentation ("/dev/null"); + + CFBundle bundle(app_bundle_path); + CFStringRef bundleIDCFStr = bundle.GetIdentifier(); + std::string bundleID; + if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL) + { + struct stat app_bundle_stat; + if (::stat (app_bundle_path, &app_bundle_stat) < 0) + { + launch_err.SetError(errno, eErrorTypePOSIX); + launch_err.SetErrorStringWithFormat("%s: \"%s\".\n", launch_err.AsString(), app_bundle_path); + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() error: %s", __FUNCTION__, launch_err.AsCString()); + } + else + { + launch_err.SetError(-1, eErrorTypeGeneric); + launch_err.SetErrorStringWithFormat("Failed to extract CFBundleIdentifier from %s.\n", app_bundle_path); + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path); + } + return LLDB_INVALID_PROCESS_ID; + } + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str()); + + + CFData argv_data(NULL); + + if (launch_argv.get()) + { + if (argv_data.Serialize(launch_argv.get(), kCFPropertyListBinaryFormat_v1_0) == NULL) + { + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() error: failed to serialize launch arg array...", __FUNCTION__); + return LLDB_INVALID_PROCESS_ID; + } + } + + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() serialized launch arg array", __FUNCTION__); + + // Find SpringBoard + SBSApplicationLaunchError sbs_error = 0; + sbs_error = SBSLaunchApplication ( bundleIDCFStr, + (CFURLRef)NULL, // openURL + launch_argv.get(), + launch_envp.get(), // CFDictionaryRef environment + stdout_cf_path.get(), + stderr_cf_path.get(), + SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice); + + + launch_err.SetError(sbs_error, eErrorTypeSpringBoard); + + if (sbs_error == SBSApplicationLaunchErrorSuccess) + { + static const useconds_t pid_poll_interval = 200000; + static const useconds_t pid_poll_timeout = 30000000; + + useconds_t pid_poll_total = 0; + + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); + // Poll until the process is running, as long as we are getting valid responses and the timeout hasn't expired + // A return PID of 0 means the process is not running, which may be because it hasn't been (asynchronously) started + // yet, or that it died very quickly (if you weren't using waitForDebugger). + while (!pid_found && pid_poll_total < pid_poll_timeout) + { + usleep (pid_poll_interval); + pid_poll_total += pid_poll_interval; + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() polling Springboard for pid for %s...", __FUNCTION__, bundleID.c_str()); + pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); + } + + if (pid_found) + { + // If we have a valid process and we created the STDIO file handles, + // then remember them on our process class so we can spawn a STDIO + // thread and close them when we are done with them. + if (process != NULL && process->STDIOIsOurs()) + { + // Release our master pty file descriptor so the pty class doesn't + // close it and so we can continue to use it in our STDIO thread + int master_fd = pty.ReleaseMasterFD(); + process->SetChildFileDescriptors(master_fd, master_fd, master_fd); + } + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid); + } + else + { + LogError("failed to lookup the process ID for CFBundleIdentifier %s.", bundleID.c_str()); + } + return pid; + } + + LogError("unable to launch the application with CFBundleIdentifier '%s' sbs_error = %u", bundleID.c_str(), sbs_error); + return LLDB_INVALID_PROCESS_ID; +} + +#endif // #if defined (__arm__) + + +#include "MachThreadContext_x86_64.h" +#include "MachThreadContext_i386.h" +#include "MachThreadContext_arm.h" + +void +ProcessMacOSX::Initialize() +{ + static bool g_initialized = false; + + if (g_initialized == false) + { + g_initialized = true; + + MachThreadContext_x86_64::Initialize(); + MachThreadContext_i386::Initialize(); + MachThreadContext_arm::Initialize(); + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + + Log::Callbacks log_callbacks = { + ProcessMacOSXLog::DisableLog, + ProcessMacOSXLog::EnableLog, + ProcessMacOSXLog::ListLogCategories + }; + + Log::RegisterLogChannel (ProcessMacOSX::GetPluginNameStatic(), log_callbacks); + + + } +} + + + diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.h b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.h new file mode 100644 index 00000000000..8388d4e46fe --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.h @@ -0,0 +1,490 @@ +//===-- ProcessMacOSX.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_MacOSXProcess_H_ +#define liblldb_MacOSXProcess_H_ + +// C Includes + +// C++ Includes +#include <list> + +// Other libraries and framework includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/ThreadSafeValue.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +// Project includes +#include "MachTask.h" +#include "MachException.h" + +typedef enum PDLaunch +{ + eLaunchDefault = 0, + eLaunchPosixSpawn, + eLaunchForkExec, +#if defined (__arm__) + eLaunchSpringBoard, +#endif +} PDLaunchType; + + + +class ThreadMacOSX; +class MachThreadContext; + +class ProcessMacOSX : + public lldb_private::Process +{ +public: + friend class ThreadMacOSX; + friend class MachTask; + + typedef MachThreadContext* (*CreateArchCalback) (const lldb_private::ArchSpec &arch_spec, ThreadMacOSX &thread); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + static Process* + CreateInstance (lldb_private::Target& target, lldb_private::Listener &listener); + + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ProcessMacOSX(lldb_private::Target& target, lldb_private::Listener &listener); + + virtual + ~ProcessMacOSX(); + + //------------------------------------------------------------------ + // Check if a given Process + //------------------------------------------------------------------ + virtual bool + CanDebug (lldb_private::Target &target); + + //------------------------------------------------------------------ + // Creating a new process, or attaching to an existing one + //------------------------------------------------------------------ + virtual lldb_private::Error + WillLaunch (lldb_private::Module* module); + + virtual lldb_private::Error + DoLaunch (lldb_private::Module* module, + char const *argv[], // Can be NULL + char const *envp[], // Can be NULL + const char *stdin_path, // Can be NULL + const char *stdout_path, // Can be NULL + const char *stderr_path); // Can be NULL + + virtual void + DidLaunch (); + + virtual lldb_private::Error + WillAttach (lldb::pid_t pid); + + virtual lldb_private::Error + DoAttach (lldb::pid_t pid); + + virtual void + DidAttach (); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + + //------------------------------------------------------------------ + // Process Control + //------------------------------------------------------------------ + virtual lldb_private::Error + DoResume (); + + virtual lldb_private::Error + DoHalt (); + + virtual lldb_private::Error + WillDetach (); + + virtual lldb_private::Error + DoDetach (); + + virtual lldb_private::Error + DoSignal (int signal); + + virtual lldb_private::Error + DoDestroy (); + + virtual void + RefreshStateAfterStop(); + + //------------------------------------------------------------------ + // Process Queries + //------------------------------------------------------------------ + virtual bool + IsAlive (); + + virtual lldb::addr_t + GetImageInfoAddress(); + + //------------------------------------------------------------------ + // Process Memory + //------------------------------------------------------------------ + virtual size_t + DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error); + + virtual size_t + DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, lldb_private::Error &error); + + virtual lldb::addr_t + DoAllocateMemory (size_t size, uint32_t permissions, lldb_private::Error &error); + + virtual lldb_private::Error + DoDeallocateMemory (lldb::addr_t ptr); + + //------------------------------------------------------------------ + // Process STDIO + //------------------------------------------------------------------ + virtual size_t + GetSTDOUT (char *buf, size_t buf_size, lldb_private::Error &error); + + virtual size_t + GetSTDERR (char *buf, size_t buf_size, lldb_private::Error &error); + + virtual size_t + PutSTDIN (const char *buf, size_t buf_size, lldb_private::Error &error); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + virtual size_t + GetSoftwareBreakpointTrapOpcode (lldb_private::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableBreakpoint (lldb_private::BreakpointSite *bp_site); + + virtual lldb_private::Error + DisableBreakpoint (lldb_private::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Watchpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableWatchpoint (lldb_private::WatchpointLocation *wp_loc); + + virtual lldb_private::Error + DisableWatchpoint (lldb_private::WatchpointLocation *wp_loc); + + virtual lldb::ByteOrder + GetByteOrder () const; + + virtual lldb_private::DynamicLoader * + GetDynamicLoader (); + + static void + AddArchCreateCallback(const lldb_private::ArchSpec& arch_spec, + ProcessMacOSX::CreateArchCalback callback); + +protected: + + bool m_stdio_ours; // True if we created and own the child STDIO file handles, false if they were supplied to us and owned by someone else + int m_child_stdin; + int m_child_stdout; + int m_child_stderr; + MachTask m_task; // The mach task for this process + lldb_private::Flags m_flags; // Process specific flags (see eFlags enums) + lldb::thread_t m_stdio_thread; // Thread ID for the thread that watches for child process stdio + lldb_private::Mutex m_stdio_mutex; // Multithreaded protection for stdio + std::string m_stdout_data; + MachException::Message::collection m_exception_messages; // A collection of exception messages caught when listening to the exception port + lldb_private::Mutex m_exception_messages_mutex; // Multithreaded protection for m_exception_messages + lldb_private::ArchSpec m_arch_spec; + std::auto_ptr<lldb_private::DynamicLoader> m_dynamic_loader_ap; +// lldb::thread_t m_wait_thread; + lldb::ByteOrder m_byte_order; + + //---------------------------------------------------------------------- + // Child process control + //---------------------------------------------------------------------- + lldb::pid_t + LaunchForDebug (const char *path, + char const *argv[], + char const *envp[], + lldb_private::ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + PDLaunchType launch_type, + lldb_private::Error &launch_err); + + static lldb::pid_t + ForkChildForPTraceDebugging (const char *path, + char const *argv[], + char const *envp[], + lldb_private::ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSX* process, + lldb_private::Error &launch_err); + + static lldb::pid_t + PosixSpawnChildForPTraceDebugging (const char *path, + char const *argv[], + char const *envp[], + lldb_private::ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSX* process, + lldb_private::Error &launch_err); + +#if defined (__arm__) + lldb::pid_t + SBLaunchForDebug (const char *path, + char const *argv[], + char const *envp[], + lldb_private::ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + lldb_private::Error &launch_err); + + static lldb::pid_t + SBLaunchForDebug (const char *path, + char const *argv[], + char const *envp[], + lldb_private::ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSX* process, + lldb_private::Error &launch_err); +#endif + + //---------------------------------------------------------------------- + // Exception thread functions + //---------------------------------------------------------------------- + bool + StartSTDIOThread (); + + void + StopSTDIOThread (bool close_child_fds); + + static void * + STDIOThread (void *arg); + + void + ExceptionMessageReceived (const MachException::Message& exceptionMessage); + + void + ExceptionMessageBundleComplete (); + + //---------------------------------------------------------------------- + // Accessors + //---------------------------------------------------------------------- + bool + ProcessIDIsValid ( ) const; + + MachTask& + Task() { return m_task; } + + const MachTask& + Task() const { return m_task; } + + bool + IsRunning ( lldb::StateType state ) + { + return state == lldb::eStateRunning || IsStepping(state); + } + + bool + IsStepping ( lldb::StateType state) + { + return state == lldb::eStateStepping; + } + bool + CanResume ( lldb::StateType state) + { + return state == lldb::eStateStopped; + } + + bool + HasExited (lldb::StateType state) + { + return state == lldb::eStateExited; + } + + void + SetChildFileDescriptors (int stdin_fileno, int stdout_fileno, int stderr_fileno) + { + m_child_stdin = stdin_fileno; + m_child_stdout = stdout_fileno; + m_child_stderr = stderr_fileno; + } + + int + GetStdinFileDescriptor () const + { + return m_child_stdin; + } + + int + GetStdoutFileDescriptor () const + { + return m_child_stdout; + } + int + GetStderrFileDescriptor () const + { + return m_child_stderr; + } + bool + ReleaseChildFileDescriptors ( int *stdin_fileno, int *stdout_fileno, int *stderr_fileno ); + + void + AppendSTDOUT (const char* s, size_t len); + + void + CloseChildFileDescriptors () + { + if (m_child_stdin >= 0) + { + ::close (m_child_stdin); + m_child_stdin = -1; + } + if (m_child_stdout >= 0) + { + ::close (m_child_stdout); + m_child_stdout = -1; + } + if (m_child_stderr >= 0) + { + ::close (m_child_stderr); + m_child_stderr = -1; + } + } + + bool + ProcessUsingSpringBoard() const + { + return m_flags.IsSet(eFlagsUsingSBS); + } + + lldb_private::ArchSpec& + GetArchSpec() + { + return m_arch_spec; + } + const lldb_private::ArchSpec& + GetArchSpec() const + { + return m_arch_spec; + } + + CreateArchCalback + GetArchCreateCallback(); + + enum + { + eFlagsNone = 0, + eFlagsAttached = (1 << 0), + eFlagsUsingSBS = (1 << 1) + }; + + void + Clear ( ); + + lldb_private::Error + ReplyToAllExceptions(); + + lldb_private::Error + PrivateResume ( lldb::tid_t tid); + + lldb_private::Flags & + GetFlags () + { + return m_flags; + } + + const lldb_private::Flags & + GetFlags () const + { + return m_flags; + } + + bool + STDIOIsOurs() const + { + return m_stdio_ours; + } + + void + SetSTDIOIsOurs(bool b) + { + m_stdio_ours = b; + } + + uint32_t + UpdateThreadListIfNeeded (); + +private: + + void + DidLaunchOrAttach (); + + lldb_private::Error + DoSIGSTOP (bool clear_all_breakpoints); + + lldb_private::Error + WillLaunchOrAttach (); + +// static void * +// WaitForChildProcessToExit (void *pid_ptr); +// +// + //------------------------------------------------------------------ + // For ProcessMacOSX only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ProcessMacOSX); + +}; + +#endif // liblldb_MacOSXProcess_H_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.cpp new file mode 100644 index 00000000000..4bfd1ff466e --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.cpp @@ -0,0 +1,124 @@ +//===-- ProcessMacOSXLog.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessMacOSXLog.h" + +#include "lldb/Core/Args.h" +#include "lldb/Core/StreamFile.h" + +#include "ProcessMacOSX.h" + +using namespace lldb; +using namespace lldb_private; + + +static Log* g_log = NULL; // Leak for now as auto_ptr was being cleaned up + // by global constructors before other threads + // were done with it. +Log * +ProcessMacOSXLog::GetLogIfAllCategoriesSet (uint32_t mask) +{ + Log *log = g_log; + if (log && mask) + { + uint32_t log_mask = log->GetMask().GetAllFlagBits(); + if ((log_mask & mask) != mask) + return NULL; + } + return log; +} + +void +ProcessMacOSXLog::DisableLog () +{ + if (g_log) + { + delete g_log; + g_log = NULL; + } +} + +Log * +ProcessMacOSXLog::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, Args &args, Stream *feedback_strm) +{ + DisableLog (); + g_log = new Log (log_stream_sp); + if (g_log) + { + uint32_t flag_bits = 0; + bool got_unknown_category = false; + const size_t argc = args.GetArgumentCount(); + for (size_t i=0; i<argc; ++i) + { + const char *arg = args.GetArgumentAtIndex(i); + + if (::strcasecmp (arg, "all") == 0 ) flag_bits |= PD_LOG_ALL; + else if (::strcasestr (arg, "break") == arg ) flag_bits |= PD_LOG_BREAKPOINTS; + else if (::strcasecmp (arg, "default") == 0 ) flag_bits |= PD_LOG_DEFAULT; + else if (::strcasestr (arg, "exc") == arg ) flag_bits |= PD_LOG_EXCEPTIONS; + else if (::strcasecmp (arg, "memory") == 0 ) flag_bits |= PD_LOG_MEMORY; + else if (::strcasecmp (arg, "data-short") == 0 ) flag_bits |= PD_LOG_MEMORY_DATA_SHORT; + else if (::strcasecmp (arg, "data-long") == 0 ) flag_bits |= PD_LOG_MEMORY_DATA_LONG; + else if (::strcasecmp (arg, "protections")== 0 ) flag_bits |= PD_LOG_MEMORY_PROTECTIONS; + else if (::strcasecmp (arg, "process") == 0 ) flag_bits |= PD_LOG_PROCESS; + else if (::strcasecmp (arg, "step") == 0 ) flag_bits |= PD_LOG_STEP; + else if (::strcasecmp (arg, "task") == 0 ) flag_bits |= PD_LOG_TASK; + else if (::strcasecmp (arg, "thread") == 0 ) flag_bits |= PD_LOG_THREAD; + else if (::strcasecmp (arg, "verbose") == 0 ) flag_bits |= PD_LOG_VERBOSE; + else if (::strcasestr (arg, "watch") == arg ) flag_bits |= PD_LOG_WATCHPOINTS; + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + if (got_unknown_category == false) + { + got_unknown_category = true; + ListLogCategories (feedback_strm); + } + } + } + if (flag_bits == 0) + flag_bits = PD_LOG_DEFAULT; + g_log->GetMask().SetAllFlagBits(flag_bits); + g_log->GetOptions().SetAllFlagBits(log_options); + } + return g_log; +} + +void +ProcessMacOSXLog::ListLogCategories (Stream *strm) +{ + strm->Printf("Logging categories for '%s':\n" + "\tall - turn on all available logging categories\n" + "\tbreak - log breakpoints\n" + "\tdefault - enable the default set of logging categories for liblldb\n" + "\tmemory - log memory reads and writes\n" + "\tdata-short - log memory bytes for memory reads and writes for short transactions only\n" + "\tdata-long - log memory bytes for memory reads and writes for all transactions\n" + "\tprocess - log process events and activities\n" + "\tprotections - log memory protections\n" + "\ttask - log mach task calls\n" + "\tthread - log thread events and activities\n" + "\tstep - log step related activities\n" + "\tverbose - enable verbose loggging\n" + "\twatch - log watchpoint related activities\n", ProcessMacOSX::GetPluginNameStatic()); +} + + +void +ProcessMacOSXLog::LogIf (uint32_t mask, const char *format, ...) +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (mask); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.h b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.h new file mode 100644 index 00000000000..cb2a4e8ee98 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.h @@ -0,0 +1,62 @@ +//===-- ProcessMacOSXLog.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_ProcessMacOSXLog_h_ +#define liblldb_ProcessMacOSXLog_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +// Project includes +#include "lldb/Core/Log.h" + +#define PD_LOG_VERBOSE (1u << 0) +#define PD_LOG_PROCESS (1u << 1) +#define PD_LOG_THREAD (1u << 2) +#define PD_LOG_EXCEPTIONS (1u << 3) +#define PD_LOG_MEMORY (1u << 4) // Log memory reads/writes calls +#define PD_LOG_MEMORY_DATA_SHORT (1u << 5) // Log short memory reads/writes bytes +#define PD_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes +#define PD_LOG_MEMORY_PROTECTIONS (1u << 7) // Log memory protection changes +#define PD_LOG_BREAKPOINTS (1u << 8) +#define PD_LOG_WATCHPOINTS (1u << 9) +#define PD_LOG_STEP (1u << 10) +#define PD_LOG_TASK (1u << 11) +#define PD_LOG_ALL (UINT32_MAX) +#define PD_LOG_DEFAULT (PD_LOG_PROCESS |\ + PD_LOG_TASK |\ + PD_LOG_THREAD |\ + PD_LOG_EXCEPTIONS |\ + PD_LOG_MEMORY |\ + PD_LOG_MEMORY_DATA_SHORT |\ + PD_LOG_BREAKPOINTS |\ + PD_LOG_WATCHPOINTS |\ + PD_LOG_STEP ) + +class ProcessMacOSXLog +{ +public: + static lldb_private::Log * + GetLogIfAllCategoriesSet(uint32_t mask = 0); + + static void + DisableLog (); + + static lldb_private::Log * + EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, lldb_private::Args &args, lldb_private::Stream *feedback_strm); + + static void + ListLogCategories (lldb_private::Stream *strm); + + static void + LogIf (uint32_t mask, const char *format, ...); +}; + +#endif // liblldb_ProcessMacOSXLog_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.cpp new file mode 100644 index 00000000000..835d003a9b2 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.cpp @@ -0,0 +1,1819 @@ +//===-- ProcessMacOSXRemote.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//---------------------------------------------------------------------- +// +// ProcessMacOSXRemote.cpp +// liblldb +// +// Created by Greg Clayton on 4/21/09. +// +// +//---------------------------------------------------------------------- + +// C Includes +#include <errno.h> + +// C++ Includes +//#include <algorithm> +//#include <map> + +// Other libraries and framework includes + +// Project includes +#include "ProcessMacOSXRemote.h" +#include "ProcessMacOSXLog.h" +#include "ThreadMacOSX.h" + +Process* +ProcessMacOSXRemote::CreateInstance (Target &target) +{ + return new ProcessMacOSXRemote (target); +} + +bool +ProcessMacOSXRemote::CanDebug(Target &target) +{ + // For now we are just making sure the file exists for a given module + ModuleSP exe_module_sp(target.GetExecutableModule()); + if (exe_module_sp.get()) + return exe_module_sp->GetFileSpec().Exists(); + return false; +} + +//---------------------------------------------------------------------- +// ProcessMacOSXRemote constructor +//---------------------------------------------------------------------- +ProcessMacOSXRemote::ProcessMacOSXRemote(Target& target) : + Process (target), + m_flags (0), + m_arch_spec (), + m_dynamic_loader_ap (), + m_byte_order(eByteOrderInvalid) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ProcessMacOSXRemote::~DCProcessMacOSXRemote() +{ + Clear(); +} + +//---------------------------------------------------------------------- +// Process Control +//---------------------------------------------------------------------- +lldb::pid_t +ProcessMacOSXRemote::DoLaunch +( + Module* module, + char const *argv[], + char const *envp[], + const char *stdin_path, + const char *stdout_path, + const char *stderr_path +) +{ +// ::LogSetBitMask (PD_LOG_DEFAULT); +// ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); +// ::LogSetLogFile ("/dev/stdout"); + + ObjectFile * object_file = module->GetObjectFile(); + if (object_file) + { + char exec_file_path[PATH_MAX]; + FileSpec* file_spec_ptr = object_file->GetFileSpec(); + if (file_spec_ptr) + file_spec_ptr->GetPath(exec_file_path, sizeof(exec_file_path)); + + ArchSpec arch_spec(module->GetArchitecture()); + + switch (arch_spec.GetCPUType()) + { + + } + // Set our user ID to our process ID. + SetID(LaunchForDebug(exec_file_path, argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, eLaunchDefault, GetError())); + } + else + { + // Set our user ID to an invalid process ID. + SetID(LLDB_INVALID_PROCESS_ID); + GetError().SetErrorToGenericError (); + GetError().SetErrorStringWithFormat ("Failed to get object file from '%s' for arch %s.\n", module->GetFileSpec().GetFilename().AsCString(), module->GetArchitecture().AsCString()); + } + + // Return the process ID we have + return GetID(); +} + +lldb::pid_t +ProcessMacOSXRemote::DoAttach (lldb::pid_t attach_pid) +{ + // Set our user ID to the attached process ID (which can be invalid if + // the attach fails + lldb::pid_t pid = AttachForDebug(attach_pid); + SetID(pid); + +// if (pid != LLDB_INVALID_PROCESS_ID) +// { +// // Wait for a process stopped event, but don't consume it +// if (WaitForEvents(LLDB_EVENT_STOPPED, NULL, 30)) +// { +// } +// } +// + // Return the process ID we have + return pid; +} + + +void +ProcessMacOSXRemote::DidLaunch () +{ + if (GetID() == LLDB_INVALID_PROCESS_ID) + { + m_dynamic_loader_ap.reset(); + } + else + { + Module * exe_module = GetTarget().GetExecutableModule ().get(); + assert(exe_module); + ObjectFile *exe_objfile = exe_module->GetObjectFile(); + assert(exe_objfile); + m_byte_order = exe_objfile->GetByteOrder(); + assert(m_byte_order != eByteOrderInvalid); + // Install a signal handler so we can catch when our child process + // dies and set the exit status correctly. + m_wait_thread = Host::ThreadCreate (ProcessMacOSXRemote::WaitForChildProcessToExit, &m_uid, &m_error); + if (m_wait_thread != LLDB_INVALID_HOST_THREAD) + { + // Don't need to get the return value of this thread, so just let + // it clean up after itself when it dies. + Host::ThreadDetach (m_wait_thread, NULL); + } + m_dynamic_loader_ap.reset(DynamicLoader::FindPlugin(this, "macosx-dyld")); + } + +} + +void +ProcessMacOSXRemote::DidAttach () +{ + DidLaunch (); + m_need_to_run_did_attach = true; +} + +bool +ProcessMacOSXRemote::DoResume () +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSXRemote::Resume()"); + State state = GetState(); + + if (CanResume(state)) + { + PrivateResume(LLDB_INVALID_THREAD_ID); + } + else if (state == eStateRunning) + { + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "Resume() - task 0x%x is running, ignoring...", m_task.TaskPort()); + GetError().Clear(); + + } + else + { + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "Resume() - task 0x%x can't continue, ignoring...", m_task.TaskPort()); + GetError().SetError(UINT_MAX, Error::Generic); + } + + return GetError().Success(); +} + +size_t +ProcessMacOSXRemote::GetSoftwareBreakpointTrapOpcode (BreakpointSite *bp_site) +{ + ModuleSP exe_module_sp(GetTarget().GetExecutableModule()); + if (exe_module_sp.get()) + { + const ArchSpec &exe_arch = exe_module_sp->GetArchitecture(); + const uint8_t *trap_opcode = NULL; + uint32_t trap_opcode_size = 0; + + static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 }; + //static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE }; + static const uint8_t g_ppc_breakpoint_opcode[] = { 0x7F, 0xC0, 0x00, 0x08 }; + static const uint8_t g_i386_breakpoint_opcode[] = { 0xCC }; + + switch (exe_arch.GetCPUType()) + { + case CPU_TYPE_ARM: + // TODO: fill this in for ARM. We need to dig up the symbol for + // the address in the breakpoint locaiton and figure out if it is + // an ARM or Thumb breakpoint. + trap_opcode = g_arm_breakpoint_opcode; + trap_opcode_size = sizeof(g_arm_breakpoint_opcode); + break; + + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + trap_opcode = g_ppc_breakpoint_opcode; + trap_opcode_size = sizeof(g_ppc_breakpoint_opcode); + break; + + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + trap_opcode = g_i386_breakpoint_opcode; + trap_opcode_size = sizeof(g_i386_breakpoint_opcode); + break; + + default: + assert(!"Unhandled architecture in ProcessMacOSXRemote::GetSoftwareBreakpointTrapOpcode()"); + return 0; + } + + if (trap_opcode && trap_opcode_size) + { + if (bp_loc->SetTrapOpcode(trap_opcode, trap_opcode_size)) + return trap_opcode_size; + } + } + // No executable yet, so we can't tell what the breakpoint opcode will be. + return 0; +} +uint32_t +ProcessMacOSXRemote::UpdateThreadListIfNeeded () +{ + // locker will keep a mutex locked until it goes out of scope + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD); + if (log && log->GetMask().IsSet(PD_LOG_VERBOSE)) + log->Printf ("ProcessMacOSXRemote::%s (pid = %4.4x)", __FUNCTION__, GetID()); + + const uint32_t stop_id = GetStopID(); + if (m_thread_list.GetSize() == 0 || stop_id != m_thread_list.GetID()) + { + m_thread_list.SetID (stop_id); + thread_array_t thread_list = NULL; + mach_msg_type_number_t thread_list_count = 0; + task_t task = Task().TaskPort(); + Error err(::task_threads (task, &thread_list, &thread_list_count), Error::MachKernel); + + if (log || err.Fail()) + err.Log(log, "::task_threads ( task = 0x%4.4x, thread_list => %p, thread_list_count => %u )", task, thread_list, thread_list_count); + + if (err.GetError() == KERN_SUCCESS && thread_list_count > 0) + { + ThreadList curr_thread_list; + + size_t idx; + // Iterator through the current thread list and see which threads + // we already have in our list (keep them), which ones we don't + // (add them), and which ones are not around anymore (remove them). + for (idx = 0; idx < thread_list_count; ++idx) + { + const lldb::tid_t tid = thread_list[idx]; + ThreadSP thread_sp(m_thread_list.FindThreadByID (tid)); + if (thread_sp.get() == NULL) + thread_sp.reset (new ThreadMacOSX (this, tid)); + curr_thread_list.AddThread(thread_sp); + } + + m_thread_list = curr_thread_list; + + // Free the vm memory given to us by ::task_threads() + vm_size_t thread_list_size = (vm_size_t) (thread_list_count * sizeof (lldb::tid_t)); + ::vm_deallocate (::mach_task_self(), + (vm_address_t)thread_list, + thread_list_size); + } + } + return m_thread_list.GetSize(); +} + +bool +ProcessMacOSXRemote::ShouldStop () +{ + // If we are attaching, let our dynamic loader plug-in know so it can get + // an initial list of shared libraries. + if (m_need_to_run_did_attach && m_dynamic_loader_ap.get()) + { + m_need_to_run_did_attach = false; + m_dynamic_loader_ap->DidAttach(); + } + + // We must be attaching if we don't already have a valid architecture + if (!m_arch_spec.IsValid()) + { + Module *exe_module = GetTarget().GetExecutableModule().get(); + if (exe_module) + m_arch_spec = exe_module->GetArchitecture(); + } + // Let all threads recover from stopping and do any clean up based + // on the previous thread state (if any). + UpdateThreadListIfNeeded (); + + if (m_thread_list.ShouldStop()) + { + // Let each thread know of any exceptions + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + task_t task = m_task.TaskPort(); + size_t i; + for (i=0; i<m_exception_messages.size(); ++i) + { + // Let the thread list figure use the ProcessMacOSXRemote to forward all exceptions + // on down to each thread. + if (m_exception_messages[i].state.task_port == task) + { + ThreadSP thread_sp(m_thread_list.FindThreadByID(m_exception_messages[i].state.thread_port)); + if (thread_sp.get()) + { + ThreadMacOSX *macosx_thread = (ThreadMacOSX *)thread_sp.get(); + macosx_thread->NotifyException (m_exception_messages[i].state); + } + } + if (log) + m_exception_messages[i].Log(log); + } + return true; + } + return false; +} + +bool +ProcessMacOSXRemote::DoHalt () +{ + return Kill (SIGINT); +} + +bool +ProcessMacOSXRemote::WillDetach () +{ + State state = GetState(); + + if (IsRunning(state)) + { + m_error.SetErrorToGenericError(); + m_error.SetErrorString("Process must be stopped in order to detach."); + return false; + } + return true; +} + +bool +ProcessMacOSXRemote::DoDetach () +{ + m_use_public_queue = false; + bool success = Detach(); + m_use_public_queue = true; + if (success) + SetState (eStateDetached); + return success; +} + +bool +ProcessMacOSXRemote::DoKill (int signal) +{ + return Kill (signal); +} + + +//------------------------------------------------------------------ +// Thread Queries +//------------------------------------------------------------------ + +Thread * +ProcessMacOSXRemote::GetCurrentThread () +{ + return m_thread_list.GetCurrentThread().get(); +} + +ByteOrder +ProcessMacOSXRemote::GetByteOrder () const +{ + return m_byte_order; +} + + + +//------------------------------------------------------------------ +// Process Queries +//------------------------------------------------------------------ + +bool +ProcessMacOSXRemote::IsAlive () +{ + return MachTask::IsValid (Task().TaskPort()); +} + +bool +ProcessMacOSXRemote::IsRunning () +{ + return LLDB_STATE_IS_RUNNING(GetState()); +} + +lldb::addr_t +ProcessMacOSXRemote::GetImageInfoAddress() +{ + return Task().GetDYLDAllImageInfosAddress(); +} + +DynamicLoader * +ProcessMacOSXRemote::GetDynamicLoader() +{ + return m_dynamic_loader_ap.get(); +} + +//------------------------------------------------------------------ +// Process Memory +//------------------------------------------------------------------ + +size_t +ProcessMacOSXRemote::DoReadMemory (lldb::addr_t addr, void *buf, size_t size) +{ + return Task().ReadMemory(addr, buf, size); +} + +size_t +ProcessMacOSXRemote::DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size) +{ + return Task().WriteMemory(addr, buf, size); +} + +//------------------------------------------------------------------ +// Process STDIO +//------------------------------------------------------------------ + +size_t +ProcessMacOSXRemote::GetSTDOUT (char *buf, size_t buf_size) +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSXRemote::%s (&%p[%u]) ...", __FUNCTION__, buf, buf_size); + Mutex::Locker locker(m_stdio_mutex); + size_t bytes_available = m_stdout_data.size(); + if (bytes_available > 0) + { + if (bytes_available > buf_size) + { + memcpy(buf, m_stdout_data.data(), buf_size); + m_stdout_data.erase(0, buf_size); + bytes_available = buf_size; + } + else + { + memcpy(buf, m_stdout_data.data(), bytes_available); + m_stdout_data.clear(); + } + } + return bytes_available; +} + +size_t +ProcessMacOSXRemote::GetSTDERR (char *buf, size_t buf_size) +{ + return 0; +} + +bool +ProcessMacOSXRemote::EnableBreakpoint (BreakpointLocation *bp) +{ + assert (bp != NULL); + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_BREAKPOINTS); + lldb::user_id_t breakID = bp->GetID(); + lldb::addr_t addr = bp->GetAddress(); + if (bp->IsEnabled()) + { + if (log) + log->Printf("ProcessMacOSXRemote::EnableBreakpoint ( breakID = %d ) breakpoint already enabled.", breakID); + return true; + } + else + { + if (bp->HardwarePreferred()) + { + ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(bp->GetThreadID()).get(); + if (thread) + { + bp->SetHardwareIndex (thread->EnableHardwareBreakpoint(bp)); + if (bp->IsHardware()) + { + bp->SetEnabled(true); + return true; + } + } + } + + const size_t break_op_size = GetSoftwareBreakpointTrapOpcode (bp); + assert (break_op_size > 0); + const uint8_t * const break_op = bp->GetTrapOpcodeBytes(); + + if (break_op_size > 0) + { + // Save the original opcode by reading it + if (m_task.ReadMemory(addr, bp->GetSavedOpcodeBytes(), break_op_size) == break_op_size) + { + // Write a software breakpoint in place of the original opcode + if (m_task.WriteMemory(addr, break_op, break_op_size) == break_op_size) + { + uint8_t verify_break_op[4]; + if (m_task.ReadMemory(addr, verify_break_op, break_op_size) == break_op_size) + { + if (memcmp(break_op, verify_break_op, break_op_size) == 0) + { + bp->SetEnabled(true); + if (log) + log->Printf("ProcessMacOSXRemote::EnableBreakpoint ( breakID = %d ) SUCCESS.", breakID, (uint64_t)addr); + return true; + } + else + { + GetError().SetErrorString("Failed to verify the breakpoint trap in memory."); + } + } + else + { + GetError().SetErrorString("Unable to read memory to verify breakpoint trap."); + } + } + else + { + GetError().SetErrorString("Unable to write breakpoint trap to memory."); + } + } + else + { + GetError().SetErrorString("Unable to read memory at breakpoint address."); + } + } + } + + if (log) + { + const char *err_string = GetError().AsCString(); + log->Printf ("ProcessMacOSXRemote::EnableBreakpoint ( breakID = %d ) error: %s", + breakID, err_string ? err_string : "NULL"); + } + GetError().SetErrorToGenericError(); + return false; +} + +bool +ProcessMacOSXRemote::DisableBreakpoint (BreakpointLocation *bp) +{ + assert (bp != NULL); + lldb::addr_t addr = bp->GetAddress(); + lldb::user_id_t breakID = bp->GetID(); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_BREAKPOINTS); + if (log) + log->Printf ("ProcessMacOSXRemote::DisableBreakpoint (breakID = %d) addr = 0x%8.8llx", breakID, (uint64_t)addr); + + if (bp->IsHardware()) + { + ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(bp->GetThreadID()).get(); + if (thread) + { + if (thread->DisableHardwareBreakpoint(bp)) + { + bp->SetEnabled(false); + if (log) + log->Printf ("ProcessMacOSXRemote::DisableBreakpoint (breakID = %d) (hardware) => success", breakID); + return true; + } + } + return false; + } + + const size_t break_op_size = bp->GetByteSize(); + assert (break_op_size > 0); + const uint8_t * const break_op = bp->GetTrapOpcodeBytes(); + if (break_op_size > 0) + { + // Clear a software breakoint instruction + uint8_t curr_break_op[break_op_size]; + bool break_op_found = false; + + // Read the breakpoint opcode + if (m_task.ReadMemory(addr, curr_break_op, break_op_size) == break_op_size) + { + bool verify = false; + if (bp->IsEnabled()) + { + // Make sure we have the a breakpoint opcode exists at this address + if (memcmp(curr_break_op, break_op, break_op_size) == 0) + { + break_op_found = true; + // We found a valid breakpoint opcode at this address, now restore + // the saved opcode. + if (m_task.WriteMemory(addr, bp->GetSavedOpcodeBytes(), break_op_size) == break_op_size) + { + verify = true; + } + else + { + GetError().SetErrorString("Memory write failed when restoring original opcode."); + } + } + else + { + GetError().SetErrorString("Original breakpoint trap is no longer in memory."); + // Set verify to true and so we can check if the original opcode has already been restored + verify = true; + } + } + else + { + if (log) + log->Printf ("ProcessMacOSXRemote::DisableBreakpoint (breakID = %d) is already disabled", breakID); + // Set verify to true and so we can check if the original opcode is there + verify = true; + } + + if (verify) + { + uint8_t verify_opcode[break_op_size]; + // Verify that our original opcode made it back to the inferior + if (m_task.ReadMemory(addr, verify_opcode, break_op_size) == break_op_size) + { + // compare the memory we just read with the original opcode + if (memcmp(bp->GetSavedOpcodeBytes(), verify_opcode, break_op_size) == 0) + { + // SUCCESS + bp->SetEnabled(false); + if (log) + log->Printf ("ProcessMacOSXRemote::DisableBreakpoint (breakID = %d) SUCCESS", breakID); + return true; + } + else + { + if (break_op_found) + GetError().SetErrorString("Failed to restore original opcode."); + } + } + else + { + GetError().SetErrorString("Failed to read memory to verify that breakpoint trap was restored."); + } + } + } + else + { + GetError().SetErrorString("Unable to read memory that should contain the breakpoint trap."); + } + } + + GetError().SetErrorToGenericError(); + return false; +} + +bool +ProcessMacOSXRemote::EnableWatchpoint (WatchpointLocation *wp) +{ + if (wp) + { + lldb::user_id_t watchID = wp->GetID(); + lldb::addr_t addr = wp->GetAddress(); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_WATCHPOINTS); + if (log) + log->Printf ("ProcessMacOSXRemote::EnableWatchpoint(watchID = %d)", watchID); + if (wp->IsEnabled()) + { + if (log) + log->Printf("ProcessMacOSXRemote::EnableWatchpoint(watchID = %d) addr = 0x%8.8llx: watchpoint already enabled.", watchID, (uint64_t)addr); + return true; + } + else + { + ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(wp->GetThreadID()).get(); + if (thread) + { + wp->SetHardwareIndex (thread->EnableHardwareWatchpoint (wp)); + if (wp->IsHardware ()) + { + wp->SetEnabled(true); + return true; + } + } + else + { + GetError().SetErrorString("Watchpoints currently only support thread specific watchpoints."); + } + } + } + return false; +} + +bool +ProcessMacOSXRemote::DisableWatchpoint (WatchpointLocation *wp) +{ + if (wp) + { + lldb::user_id_t watchID = wp->GetID(); + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_WATCHPOINTS); + + lldb::addr_t addr = wp->GetAddress(); + if (log) + log->Printf ("ProcessMacOSXRemote::DisableWatchpoint (watchID = %d) addr = 0x%8.8llx", watchID, (uint64_t)addr); + + if (wp->IsHardware()) + { + ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(wp->GetThreadID()).get(); + if (thread) + { + if (thread->DisableHardwareWatchpoint (wp)) + { + wp->SetEnabled(false); + if (log) + log->Printf ("ProcessMacOSXRemote::Disablewatchpoint (watchID = %d) addr = 0x%8.8llx (hardware) => success", watchID, (uint64_t)addr); + return true; + } + } + } + // TODO: clear software watchpoints if we implement them + } + else + { + GetError().SetErrorString("Watchpoint location argument was NULL."); + } + GetError().SetErrorToGenericError(); + return false; +} + + +static ProcessMacOSXRemote::CreateArchCalback +ArchDCScriptInterpreter::TypeMap(const ArchSpec& arch_spec, ProcessMacOSXRemote::CreateArchCalback callback, bool add ) +{ + // We must wrap the "g_arch_map" file static in a function to avoid + // any global constructors so we don't get a build verification error + typedef std::multimap<ArchSpec, ProcessMacOSXRemote::CreateArchCalback> ArchToProtocolMap; + static ArchToProtocolMap g_arch_map; + + if (add) + { + g_arch_map.insert(std::make_pair(arch_spec, callback)); + return callback; + } + else + { + ArchToProtocolMap::const_iterator pos = g_arch_map.find(arch_spec); + if (pos != g_arch_map.end()) + { + return pos->second; + } + } + return NULL; +} + +void +ProcessMacOSXRemote::AddArchCreateDCScriptInterpreter::Type(const ArchSpec& arch_spec, CreateArchCalback callback) +{ + ArchDCScriptInterpreter::TypeMap (arch_spec, callback, true); +} + +ProcessMacOSXRemote::CreateArchCalback +ProcessMacOSXRemote::GetArchCreateDCScriptInterpreter::Type() +{ + return ArchDCScriptInterpreter::TypeMap (m_arch_spec, NULL, false); +} + +void +ProcessMacOSXRemote::Clear() +{ + // Clear any cached thread list while the pid and task are still valid + + m_task.Clear(); + // Now clear out all member variables + CloseChildFileDescriptors(); + + m_flags = eFlagsNone; + m_thread_list.Clear(); + { + Mutex::Locker locker(m_exception_messages_mutex); + m_exception_messages.clear(); + } + +} + + +bool +ProcessMacOSXRemote::Kill (int signal) +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS); + if (log) + log->Printf ("ProcessMacOSXRemote::Kill(signal = %d)", signal); + State state = GetState(); + + if (IsRunning(state)) + { + if (::kill (GetID(), signal) == 0) + { + GetError().Clear(); + } + else + { + GetError().SetErrorToErrno(); + GetError().LogIfError(log, "ProcessMacOSXRemote::Kill(%d)", signal); + } + } + else + { + if (log) + log->Printf ("ProcessMacOSXRemote::Kill(signal = %d) pid %u (task = 0x%4.4x) was't running, ignoring...", signal, GetID(), m_task.TaskPort()); + GetError().Clear(); + } + return GetError().Success(); + +} + + +bool +ProcessMacOSXRemote::Detach() +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSXRemote::Detach()"); + + State state = GetState(); + + if (!IsRunning(state)) + { + // Resume our process + PrivateResume(LLDB_INVALID_THREAD_ID); + + // We have resumed and now we wait for that event to get posted + Event event; + if (WaitForPrivateEvents(LLDB_EVENT_RUNNING, &event, 2) == false) + return false; + + + // We need to be stopped in order to be able to detach, so we need + // to send ourselves a SIGSTOP + if (Kill(SIGSTOP)) + { + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS); + + lldb::pid_t pid = GetID(); + // Wait for our process stop event to get posted + if (WaitForPrivateEvents(LLDB_EVENT_STOPPED, &event, 2) == false) + { + GetError().Log(log, "::kill (pid = %u, SIGSTOP)", pid); + return false; + } + + // Shut down the exception thread and cleanup our exception remappings + m_task.ShutDownExceptionThread(); + + // Detach from our process while we are stopped. + errno = 0; + + // Detach from our process + ::ptrace (PT_DETACH, pid, (caddr_t)1, 0); + + GetError().SetErrorToErrno(); + + if (log || GetError().Fail()) + GetError().Log(log, "::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid); + + // Resume our task + m_task.Resume(); + + // NULL our task out as we have already retored all exception ports + m_task.Clear(); + + // Clear out any notion of the process we once were + Clear(); + } + } + else + { + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSXRemote::Detach() error: process must be stopped (SIGINT the process first)."); + } + return false; +} + + + +void +ProcessMacOSXRemote::ReplyToAllExceptions() +{ + Mutex::Locker locker(m_exception_messages_mutex); + if (m_exception_messages.empty() == false) + { + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS); + + MachException::Message::iterator pos; + MachException::Message::iterator begin = m_exception_messages.begin(); + MachException::Message::iterator end = m_exception_messages.end(); + for (pos = begin; pos != end; ++pos) + { + if (log) + log->Printf ("Replying to exception %d...", std::distance(begin, pos)); + int resume_signal = 0; + ThreadSP thread_sp = m_thread_list.FindThreadByID(pos->state.thread_port); + if (thread_sp.get()) + resume_signal = thread_sp->GetResumeSignal(); + GetError() = pos->Reply (Task().TaskPort(), GetID(), resume_signal); + GetError().LogIfError(log, "Error replying to exception"); + } + + // Erase all exception message as we should have used and replied + // to them all already. + m_exception_messages.clear(); + } +} +void +ProcessMacOSXRemote::PrivateResume (lldb::tid_t tid) +{ + Mutex::Locker locker(m_exception_messages_mutex); + ReplyToAllExceptions(); + + // Let the thread prepare to resume and see if any threads want us to + // step over a breakpoint instruction (ProcessWillResume will modify + // the value of stepOverBreakInstruction). + //StateType process_state = m_thread_list.ProcessWillResume(this); + + // Set our state accordingly + SetState(eStateRunning); + + // Now resume our task. + GetError() = m_task.Resume(); + +} + +// Called by the exception thread when an exception has been received from +// our process. The exception message is completely filled and the exception +// data has already been copied. +void +ProcessMacOSXRemote::ExceptionMessageReceived (const MachException::Message& exceptionMessage) +{ + Mutex::Locker locker(m_exception_messages_mutex); + + if (m_exception_messages.empty()) + m_task.Suspend(); + + ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "ProcessMacOSXRemote::ExceptionMessageReceived ( )"); + + // Use a locker to automatically unlock our mutex in case of exceptions + // Add the exception to our internal exception stack + m_exception_messages.push_back(exceptionMessage); +} + + +//bool +//ProcessMacOSXRemote::GetProcessInfo (struct kinfo_proc* proc_info) +//{ +// int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, GetID() }; +// size_t buf_size = sizeof(struct kinfo_proc); +// +// if (::sysctl (mib, (unsigned)(sizeof(mib)/sizeof(int)), &proc_info, &buf_size, NULL, 0) == 0) +// return buf_size > 0; +// +// return false; +//} +// +// +void +ProcessMacOSXRemote::ExceptionMessageBundleComplete() +{ + // We have a complete bundle of exceptions for our child process. + Mutex::Locker locker(m_exception_messages_mutex); + ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "%s: %d exception messages.", __PRETTY_FUNCTION__, m_exception_messages.size()); + if (!m_exception_messages.empty()) + { + SetState (eStateStopped); + } + else + { + ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "%s empty exception messages bundle.", __PRETTY_FUNCTION__, m_exception_messages.size()); + } +} + +bool +ProcessMacOSXRemote::ReleaseChildFileDescriptors ( int *stdin_fileno, int *stdout_fileno, int *stderr_fileno ) +{ + if (stdin_fileno) + *stdin_fileno = m_child_stdin; + if (stdout_fileno) + *stdout_fileno = m_child_stdout; + if (stderr_fileno) + *stderr_fileno = m_child_stderr; + // Stop the stdio thread if we have one, but don't have it close the child + // file descriptors since we are giving control of these descriptors to the + // caller + bool close_child_fds = false; + StopSTDIOThread(close_child_fds); + return true; +} + +void +ProcessMacOSXRemote::AppendSTDOUT (char* s, size_t len) +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSXRemote::%s (<%d> %s) ...", __FUNCTION__, len, s); + Mutex::Locker locker(m_stdio_mutex); + m_stdout_data.append(s, len); + AppendEvent (LLDB_EVENT_STDIO); +} + +void * +ProcessMacOSXRemote::STDIOThread(void *arg) +{ + ProcessMacOSXRemote *proc = (ProcessMacOSXRemote*) arg; + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS); + if (log) + log->Printf ("ProcessMacOSXRemote::%s (arg = %p) thread starting...", __FUNCTION__, arg); + + // We start use a base and more options so we can control if we + // are currently using a timeout on the mach_msg. We do this to get a + // bunch of related exceptions on our exception port so we can process + // then together. When we have multiple threads, we can get an exception + // per thread and they will come in consecutively. The main thread loop + // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT + // flag set in the options, so we will wait forever for an exception on + // our exception port. After we get one exception, we then will use the + // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current + // exceptions for our process. After we have received the last pending + // exception, we will get a timeout which enables us to then notify + // our main thread that we have an exception bundle avaiable. We then wait + // for the main thread to tell this exception thread to start trying to get + // exceptions messages again and we start again with a mach_msg read with + // infinite timeout. + Error err; + int stdout_fd = proc->GetStdoutFileDescriptor(); + int stderr_fd = proc->GetStderrFileDescriptor(); + if (stdout_fd == stderr_fd) + stderr_fd = -1; + + while (stdout_fd >= 0 || stderr_fd >= 0) + { + ::pthread_testcancel (); + + fd_set read_fds; + FD_ZERO (&read_fds); + if (stdout_fd >= 0) + FD_SET (stdout_fd, &read_fds); + if (stderr_fd >= 0) + FD_SET (stderr_fd, &read_fds); + int nfds = std::max<int>(stdout_fd, stderr_fd) + 1; + + int num_set_fds = select (nfds, &read_fds, NULL, NULL, NULL); + if (log) + log->Printf("select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); + + if (num_set_fds < 0) + { + int select_errno = errno; + if (log) + { + err.SetError (select_errno, Error::POSIX); + err.LogIfError(log, "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds); + } + + switch (select_errno) + { + case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate the requested number of file descriptors, or we have non-blocking IO + break; + case EBADF: // One of the descriptor sets specified an invalid descriptor. + return NULL; + break; + case EINTR: // A signal was delivered before the time limit expired and before any of the selected events occurred. + case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. + default: // Other unknown error + break; + } + } + else if (num_set_fds == 0) + { + } + else + { + char s[1024]; + s[sizeof(s)-1] = '\0'; // Ensure we have NULL termination + int bytes_read = 0; + if (stdout_fd >= 0 && FD_ISSET (stdout_fd, &read_fds)) + { + do + { + bytes_read = ::read (stdout_fd, s, sizeof(s)-1); + if (bytes_read < 0) + { + int read_errno = errno; + if (log) + log->Printf("read (stdout_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); + } + else if (bytes_read == 0) + { + // EOF... + if (log) + log->Printf("read (stdout_fd, ) => %d (reached EOF for child STDOUT)", bytes_read); + stdout_fd = -1; + } + else if (bytes_read > 0) + { + proc->AppendSTDOUT(s, bytes_read); + } + + } while (bytes_read > 0); + } + + if (stderr_fd >= 0 && FD_ISSET (stderr_fd, &read_fds)) + { + do + { + bytes_read = ::read (stderr_fd, s, sizeof(s)-1); + if (bytes_read < 0) + { + int read_errno = errno; + if (log) + log->Printf("read (stderr_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno)); + } + else if (bytes_read == 0) + { + // EOF... + if (log) + log->Printf("read (stderr_fd, ) => %d (reached EOF for child STDERR)", bytes_read); + stderr_fd = -1; + } + else if (bytes_read > 0) + { + proc->AppendSTDOUT(s, bytes_read); + } + + } while (bytes_read > 0); + } + } + } + + if (log) + log->Printf("ProcessMacOSXRemote::%s (%p): thread exiting...", __FUNCTION__, arg); + + return NULL; +} + +lldb::pid_t +ProcessMacOSXRemote::AttachForDebug (lldb::pid_t pid) +{ + // Clear out and clean up from any current state + Clear(); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS); + if (pid != 0) + { + SetState(eStateAttaching); + SetID(pid); + // Let ourselves know we are going to be using SBS if the correct flag bit is set... +#if defined (__arm__) + if (IsSBProcess(pid)) + m_flags |= eFlagsUsingSBS; +#endif + m_task.StartExceptionThread(GetError()); + + if (GetError().Success()) + { + if (ptrace (PT_ATTACHEXC, pid, 0, 0) == 0) + { + m_flags.Set (eFlagsAttached); + // Sleep a bit to let the exception get received and set our process status + // to stopped. + ::usleep(250000); + if (log) + log->Printf ("successfully attached to pid %d", pid); + return GetID(); + } + else + { + GetError().SetErrorToErrno(); + if (log) + log->Printf ("error: failed to attach to pid %d", pid); + } + } + else + { + GetError().Log(log, "ProcessMacOSXRemote::%s (pid = %i) failed to start exception thread", __FUNCTION__, pid); + } + } + return LLDB_INVALID_PROCESS_ID; +} + +lldb::pid_t +ProcessMacOSXRemote::LaunchForDebug +( + const char *path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + PDLaunchType launch_type, + Error &launch_err) +{ + // Clear out and clean up from any current state + Clear(); + + m_arch_spec = arch_spec; + + if (launch_type == eLaunchDefault) + launch_type = eLaunchPosixSpawn; + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS); + if (log) + log->Printf ("%s( path = '%s', argv = %p, envp = %p, launch_type = %u )", __FUNCTION__, path, argv, envp, launch_type); + + // Fork a child process for debugging + SetState(eStateLaunching); + switch (launch_type) + { + case eLaunchForkExec: + SetID(ProcessMacOSXRemote::ForkChildForPTraceDebugging(path, argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, this, launch_err)); + break; + + case eLaunchPosixSpawn: + SetID(ProcessMacOSXRemote::PosixSpawnChildForPTraceDebugging(path, argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, this, launch_err)); + break; + +#if defined (__arm__) + + case eLaunchSpringBoard: + { + const char *app_ext = strstr(path, ".app"); + if (app_ext != NULL) + { + std::string app_bundle_path(path, app_ext + strlen(".app")); + return SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, launch_err); + } + } + break; + +#endif + + default: + // Invalid launch + launch_err.SetErrorToGenericError (); + return LLDB_INVALID_PROCESS_ID; + } + + lldb::pid_t pid = GetID(); + + if (pid == LLDB_INVALID_PROCESS_ID) + { + // If we don't have a valid process ID and no one has set the error, + // then return a generic error + if (launch_err.Success()) + launch_err.SetErrorToGenericError (); + } + else + { + // Make sure we can get our task port before going any further + m_task.TaskPortForProcessID (launch_err); + + // If that goes well then kick off our exception thread + if (launch_err.Success()) + m_task.StartExceptionThread(launch_err); + + if (launch_err.Success()) + { + //m_path = path; +// size_t i; +// if (argv) +// { +// char const *arg; +// for (i=0; (arg = argv[i]) != NULL; i++) +// m_args.push_back(arg); +// } + + StartSTDIOThread(); + + if (launch_type == eLaunchPosixSpawn) + { + + //SetState (eStateAttaching); + errno = 0; + if (::ptrace (PT_ATTACHEXC, pid, 0, 0) == 0) + launch_err.Clear(); + else + launch_err.SetErrorToErrno(); + + if (launch_err.Fail() || log) + launch_err.Log(log, "::ptrace (PT_ATTACHEXC, pid = %i, 0, 0 )", pid); + + if (launch_err.Success()) + m_flags.Set (eFlagsAttached); + else + SetState (eStateExited); + } + else + { + launch_err.Clear(); + } + } + else + { + // We were able to launch the process, but not get its task port + // so now we need to make it sleep with da fishes. + SetID(LLDB_INVALID_PROCESS_ID); + ::kill (pid, SIGCONT); + ::kill (pid, SIGKILL); + pid = LLDB_INVALID_PROCESS_ID; + } + + } + return pid; +} + +lldb::pid_t +ProcessMacOSXRemote::PosixSpawnChildForPTraceDebugging +( + const char *path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSXRemote* process, + Error &err +) +{ + posix_spawnattr_t attr; + + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS); + + Error local_err; // Errors that don't affect the spawning. + if (log) + log->Printf ("%s ( path='%s', argv=%p, envp=%p, process )", __FUNCTION__, path, argv, envp); + err.SetError( ::posix_spawnattr_init (&attr), Error::POSIX); + if (err.Fail() || log) + err.Log(log, "::posix_spawnattr_init ( &attr )"); + if (err.Fail()) + return LLDB_INVALID_PROCESS_ID; + + err.SetError( ::posix_spawnattr_setflags (&attr, POSIX_SPAWN_START_SUSPENDED), Error::POSIX); + if (err.Fail() || log) + err.Log(log, "::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED )"); + if (err.Fail()) + return LLDB_INVALID_PROCESS_ID; + +#if !defined(__arm__) + + // We don't need to do this for ARM, and we really shouldn't now that we + // have multiple CPU subtypes and no posix_spawnattr call that allows us + // to set which CPU subtype to launch... + cpu_type_t cpu = arch_spec.GetCPUType(); + if (cpu != 0 && cpu != CPU_TYPE_ANY && cpu != LLDB_INVALID_CPUTYPE) + { + size_t ocount = 0; + err.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &ocount), Error::POSIX); + if (err.Fail() || log) + err.Log(log, "::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %zu )", cpu, ocount); + + if (err.Fail() != 0 || ocount != 1) + return LLDB_INVALID_PROCESS_ID; + } + +#endif + + PseudoTerminal pty; + + posix_spawn_file_actions_t file_actions; + err.SetError( ::posix_spawn_file_actions_init (&file_actions), Error::POSIX); + int file_actions_valid = err.Success(); + if (!file_actions_valid || log) + err.Log(log, "::posix_spawn_file_actions_init ( &file_actions )"); + Error stdio_err; + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + if (file_actions_valid) + { + // If the user specified any STDIO files, then use those + if (stdin_path || stdout_path || stderr_path) + { + process->SetSTDIOIsOurs(false); + if (stderr_path != NULL && stderr_path[0]) + { + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDERR_FILENO, stderr_path, O_RDWR, 0), Error::POSIX); + if (stdio_err.Fail() || log) + stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDERR_FILENO, path = '%s', oflag = O_RDWR, mode = 0 )", stderr_path); + } + + if (stdin_path != NULL && stdin_path[0]) + { + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO, stdin_path, O_RDONLY, 0), Error::POSIX); + if (stdio_err.Fail() || log) + stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDIN_FILENO, path = '%s', oflag = O_RDONLY, mode = 0 )", stdin_path); + } + + if (stdout_path != NULL && stdout_path[0]) + { + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDOUT_FILENO, stdout_path, O_WRONLY, 0), Error::POSIX); + if (stdio_err.Fail() || log) + stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDOUT_FILENO, path = '%s', oflag = O_WRONLY, mode = 0 )", stdout_path); + } + } + else + { + // The user did not specify any STDIO files, use a pseudo terminal. + // Callers can then access the file handles using the + // ProcessMacOSXRemote::ReleaseChildFileDescriptors() function, otherwise + // this class will spawn a thread that tracks STDIO and buffers it. + process->SetSTDIOIsOurs(true); + if (pty.OpenFirstAvailableMaster(O_RDWR, &stdio_err)) + { + const char* slave_name = pty.GetSlaveName(&stdio_err); + if (slave_name == NULL) + slave_name = "/dev/null"; + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDERR_FILENO, slave_name, O_RDWR, 0), Error::POSIX); + if (stdio_err.Fail() || log) + stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDERR_FILENO, path = '%s', oflag = O_RDWR, mode = 0 )", slave_name); + + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO, slave_name, O_RDONLY, 0), Error::POSIX); + if (stdio_err.Fail() || log) + stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDIN_FILENO, path = '%s', oflag = O_RDONLY, mode = 0 )", slave_name); + + stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDOUT_FILENO, slave_name, O_WRONLY, 0), Error::POSIX); + if (stdio_err.Fail() || log) + stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDOUT_FILENO, path = '%s', oflag = O_WRONLY, mode = 0 )", slave_name); + } + } + err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), Error::POSIX); + if (err.Fail() || log) + err.Log(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp); + + if (stdio_err.Success()) + { + // If we have a valid process and we created the STDIO file handles, + // then remember them on our process class so we can spawn a STDIO + // thread and close them when we are done with them. + if (process != NULL && process->STDIOIsOurs()) + { + int master_fd = pty.ReleaseMasterFileDescriptor (); + process->SetChildFileDescriptors (master_fd, master_fd, master_fd); + } + } + } + else + { + err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), Error::POSIX); + if (err.Fail() || log) + err.Log(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp); + } + + // We have seen some cases where posix_spawnp was returning a valid + // looking pid even when an error was returned, so clear it out + if (err.Fail()) + pid = LLDB_INVALID_PROCESS_ID; + + if (file_actions_valid) + { + local_err.SetError( ::posix_spawn_file_actions_destroy (&file_actions), Error::POSIX); + if (local_err.Fail() || log) + local_err.Log(log, "::posix_spawn_file_actions_destroy ( &file_actions )"); + } + + return pid; +} + +lldb::pid_t +ProcessMacOSXRemote::ForkChildForPTraceDebugging +( + const char *path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSXRemote* process, + Error &launch_err +) +{ + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + if (stdin_path || stdout_path || stderr_path) + { + assert(!"TODO: ForkChildForPTraceDebugging doesn't currently support fork/exec with user file handles..."); + } + else + { + + // Use a fork that ties the child process's stdin/out/err to a pseudo + // terminal so we can read it in our ProcessMacOSXRemote::STDIOThread + // as unbuffered io. + PseudoTerminal pty; + pid = pty.Fork(&launch_err); + + if (pid < 0) + { + //-------------------------------------------------------------- + // Error during fork. + //-------------------------------------------------------------- + return pid; + } + else if (pid == 0) + { + //-------------------------------------------------------------- + // Child process + //-------------------------------------------------------------- + ::ptrace (PT_TRACE_ME, 0, 0, 0); // Debug this process + ::ptrace (PT_SIGEXC, 0, 0, 0); // Get BSD signals as mach exceptions + + // If our parent is setgid, lets make sure we don't inherit those + // extra powers due to nepotism. + ::setgid (getgid ()); + + // Let the child have its own process group. We need to execute + // this call in both the child and parent to avoid a race condition + // between the two processes. + ::setpgid (0, 0); // Set the child process group to match its pid + + // Sleep a bit to before the exec call + ::sleep (1); + + // Turn this process into + ::execv (path, (char * const *)argv); + // Exit with error code. Child process should have taken + // over in above exec call and if the exec fails it will + // exit the child process below. + ::exit (127); + } + else + { + //-------------------------------------------------------------- + // Parent process + //-------------------------------------------------------------- + // Let the child have its own process group. We need to execute + // this call in both the child and parent to avoid a race condition + // between the two processes. + ::setpgid (pid, pid); // Set the child process group to match its pid + + if (process != NULL) + { + // Release our master pty file descriptor so the pty class doesn't + // close it and so we can continue to use it in our STDIO thread + int master_fd = pty.ReleaseMasterFileDescriptor (); + process->SetChildFileDescriptors (master_fd, master_fd, master_fd); + } + } + } + return pid; +} + +#if defined (__arm__) + +lldb::pid_t +ProcessMacOSXRemote::SBLaunchForDebug +( + const char *path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + Error &launch_err +) +{ + // Clear out and clean up from any current state + Clear(); + + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path); + + // Fork a child process for debugging + SetState(eStateLaunching); + m_pid = ProcessMacOSXRemote::SBLaunchForDebug(path, argv, envp, this, launch_err); + if (m_pid != 0) + { + m_flags |= eFlagsUsingSBS; + //m_path = path; +// size_t i; +// char const *arg; +// for (i=0; (arg = argv[i]) != NULL; i++) +// m_args.push_back(arg); + m_task.StartExceptionThread(); + StartSTDIOThread(); + SetState (eStateAttaching); + int err = ptrace (PT_ATTACHEXC, m_pid, 0, 0); + if (err == 0) + { + m_flags |= eFlagsAttached; + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "successfully attached to pid %d", m_pid); + } + else + { + SetState (eStateExited); + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "error: failed to attach to pid %d", m_pid); + } + } + return m_pid; +} + +#include <servers/bootstrap.h> +#include "CFBundle.h" +#include "CFData.h" +#include "CFString.h" + +lldb::pid_t +ProcessMacOSXRemote::SBLaunchForDebug +( + const char *app_bundle_path, + char const *argv[], + char const *envp[], + ArchSpec& arch_spec, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + ProcessMacOSXRemote* process, + Error &launch_err +) +{ + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, process); + CFAllocatorRef alloc = kCFAllocatorDefault; + if (argv[0] == NULL) + return LLDB_INVALID_PROCESS_ID; + + size_t argc = 0; + // Count the number of arguments + while (argv[argc] != NULL) + argc++; + + // Enumerate the arguments + size_t first_launch_arg_idx = 1; + CFReleaser<CFMutableArrayRef> launch_argv; + + if (argv[first_launch_arg_idx]) + { + size_t launch_argc = argc > 0 ? argc - 1 : 0; + launch_argv.reset (::CFArrayCreateMutable (alloc, launch_argc, &kCFTypeArrayCallBacks)); + size_t i; + char const *arg; + CFString launch_arg; + for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++) + { + launch_arg.reset(::CFStringCreateWithCString (alloc, arg, kCFStringEncodingUTF8)); + if (launch_arg.get() != NULL) + CFArrayAppendValue(launch_argv.get(), launch_arg.get()); + else + break; + } + } + + // Next fill in the arguments dictionary. Note, the envp array is of the form + // Variable=value but SpringBoard wants a CF dictionary. So we have to convert + // this here. + + CFReleaser<CFMutableDictionaryRef> launch_envp; + + if (envp[0]) + { + launch_envp.reset(::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + const char *value; + int name_len; + CFString name_string, value_string; + + for (int i = 0; envp[i] != NULL; i++) + { + value = strstr (envp[i], "="); + + // If the name field is empty or there's no =, skip it. Somebody's messing with us. + if (value == NULL || value == envp[i]) + continue; + + name_len = value - envp[i]; + + // Now move value over the "=" + value++; + + name_string.reset(::CFStringCreateWithBytes(alloc, (const UInt8 *) envp[i], name_len, kCFStringEncodingUTF8, false)); + value_string.reset(::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8)); + CFDictionarySetValue (launch_envp.get(), name_string.get(), value_string.get()); + } + } + + CFString stdout_cf_path; + CFString stderr_cf_path; + PseudoTerminal pty; + + if (stdin_path || stdout_path || stderr_path) + { + process->SetSTDIOIsOurs(false); + if (stdout_path) + stdout_cf_path.SetFileSystemRepresentation (stdout_path); + if (stderr_path) + stderr_cf_path.SetFileSystemRepresentation (stderr_path); + } + else + { + process->SetSTDIOIsOurs(true); + PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR); + if (pty_err == PseudoTerminal::success) + { + const char* slave_name = pty.SlaveName(); + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name); + if (slave_name && slave_name[0]) + { + ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO); + stdout_cf_path.SetFileSystemRepresentation (slave_name); + stderr_cf_path.(stdout_cf_path); + } + } + } + + if (stdout_cf_path.get() == NULL) + stdout_cf_path.SetFileSystemRepresentation ("/dev/null"); + if (stderr_cf_path.get() == NULL) + stderr_cf_path.SetFileSystemRepresentation ("/dev/null"); + + CFBundle bundle(app_bundle_path); + CFStringRef bundleIDCFStr = bundle.GetIdentifier(); + std::string bundleID; + if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL) + { + struct stat app_bundle_stat; + if (::stat (app_bundle_path, &app_bundle_stat) < 0) + { + launch_err.SetError(errno, Error::POSIX); + launch_err.SetErrorStringWithFormat ("%s: \"%s\".\n", launch_err.AsString(), app_bundle_path); + } + else + { + launch_err.SetError(-1, Error::Generic); + launch_err.SetErrorStringWithFormat ("Failed to extract CFBundleIdentifier from %s.\n", app_bundle_path); + } + return LLDB_INVALID_PROCESS_ID; + } + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str()); + + + CFData argv_data(NULL); + + if (launch_argv.get()) + { + if (argv_data.Serialize(launch_argv.get(), kCFPropertyListBinaryFormat_v1_0) == NULL) + { + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() error: failed to serialize launch arg array...", __FUNCTION__); + return LLDB_INVALID_PROCESS_ID; + } + } + + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() serialized launch arg array", __FUNCTION__); + + // Find SpringBoard + SBSApplicationLaunchError sbs_error = 0; + sbs_error = SBSLaunchApplication ( bundleIDCFStr, + (CFURLRef)NULL, // openURL + launch_argv.get(), + launch_envp.get(), // CFDictionaryRef environment + stdout_cf_path.get(), + stderr_cf_path.get(), + SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice); + + + launch_err.SetError(sbs_error, Error::SpringBoard); + + if (sbs_error == SBSApplicationLaunchErrorSuccess) + { + static const useconds_t pid_poll_interval = 200000; + static const useconds_t pid_poll_timeout = 30000000; + + useconds_t pid_poll_total = 0; + + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); + // Poll until the process is running, as long as we are getting valid responses and the timeout hasn't expired + // A return PID of 0 means the process is not running, which may be because it hasn't been (asynchronously) started + // yet, or that it died very quickly (if you weren't using waitForDebugger). + while (!pid_found && pid_poll_total < pid_poll_timeout) + { + usleep (pid_poll_interval); + pid_poll_total += pid_poll_interval; + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() polling Springboard for pid for %s...", __FUNCTION__, bundleID.c_str()); + pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid); + } + + if (pid_found) + { + // If we have a valid process and we created the STDIO file handles, + // then remember them on our process class so we can spawn a STDIO + // thread and close them when we are done with them. + if (process != NULL && process->STDIOIsOurs()) + { + // Release our master pty file descriptor so the pty class doesn't + // close it and so we can continue to use it in our STDIO thread + int master_fd = pty.ReleaseMasterFD(); + process->SetChildFileDescriptors(master_fd, master_fd, master_fd); + } + ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid); + } + else + { + LogError("failed to lookup the process ID for CFBundleIdentifier %s.", bundleID.c_str()); + } + return pid; + } + + LogError("unable to launch the application with CFBundleIdentifier '%s' sbs_error = %u", bundleID.c_str(), sbs_error); + return LLDB_INVALID_PROCESS_ID; +} + +#endif // #if defined (__arm__) + diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.h b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.h new file mode 100644 index 00000000000..01905c6192a --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.h @@ -0,0 +1,206 @@ +//===-- ProcessMacOSXRemote.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//---------------------------------------------------------------------- +// +// ProcessMacOSXRemote.h +// liblldb +// +// Created by Greg Clayton on 4/21/09. +// +// +//---------------------------------------------------------------------- + +#ifndef liblldb_ProcessMacOSXRemote_H_ +#define liblldb_ProcessMacOSXRemote_H_ + +// C Includes + +// C++ Includes +#include <list> + +// Other libraries and framework includes +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +class ThreadMacOSXRemote; + +class ProcessMacOSXRemote : + public Process +{ +public: + friend class ThreadMacOSX; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ProcessMacOSXRemote(Target& target); + virtual ~DCProcessMacOSXRemote(); + + static Process* CreateInstance (Target& target); + + //------------------------------------------------------------------ + // Check if a given Process + //------------------------------------------------------------------ + virtual bool CanDebug(Target &target); + + //------------------------------------------------------------------ + // Creating a new process, or attaching to an existing one + //------------------------------------------------------------------ + virtual lldb::pid_t DoLaunch (Module* module, + char const *argv[], // Can be NULL + char const *envp[], // Can be NULL + const char *stdin_path, // Can be NULL + const char *stdout_path, // Can be NULL + const char *stderr_path); // Can be NULL + virtual void DidLaunch (); + virtual lldb::pid_t DoAttach (lldb::pid_t pid); + virtual void DidAttach (); + + //------------------------------------------------------------------ + // Process Control + //------------------------------------------------------------------ +// virtual bool WillResume (); + virtual bool DoResume (); +// virtual void DidResume (); + + virtual bool DoHalt (); + virtual bool WillDetach (); + virtual bool DoDetach (); + virtual bool DoKill (int signal); + + virtual bool ShouldStop (); + + //------------------------------------------------------------------ + // Process Queries + //------------------------------------------------------------------ + virtual bool IsAlive (); + virtual bool IsRunning (); + virtual lldb::addr_t GetImageInfoAddress(); + + //------------------------------------------------------------------ + // Process Memory + //------------------------------------------------------------------ + virtual size_t DoReadMemory (lldb::addr_t addr, void *buf, size_t size); + virtual size_t DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size); + + //------------------------------------------------------------------ + // Process STDIO + //------------------------------------------------------------------ + virtual size_t GetSTDOUT (char *buf, size_t buf_size); + virtual size_t GetSTDERR (char *buf, size_t buf_size); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + virtual size_t + GetSoftwareBreakpointTrapOpcode (lldb::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + virtual bool + EnableBreakpoint (lldb::BreakpointSite *bp_site); + + virtual bool + DisableBreakpoint (lldb::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Watchpoints + //---------------------------------------------------------------------- + virtual bool EnableWatchpoint (WatchpointLocation *wp_loc); + virtual bool DisableWatchpoint (WatchpointLocation *wp_loc); + + //------------------------------------------------------------------ + // Thread Queries + //------------------------------------------------------------------ + virtual Thread * GetCurrentThread (); + virtual bool SetCurrentThread (lldb::tid_t tid); + virtual Thread * GetThreadAtIndex (uint32_t idx); + virtual Thread * GetThreadByID (lldb::tid_t tid); + virtual size_t GetNumThreads (); + + virtual ByteOrder GetByteOrder () const; + + virtual DynamicLoader * + GetDynamicLoader (); + +protected: + Flags m_flags; // Process specific flags (see eFlags enums) + ArchSpec m_arch_spec; + std::auto_ptr<DynamicLoader> m_dynamic_loader_ap; + ByteOrder m_byte_order; + + //---------------------------------------------------------------------- + // Accessors + //---------------------------------------------------------------------- + bool + ProcessIDIsValid ( ) const; + + bool + IsRunning ( State state ) + { + return state == eStateRunning || IsStepping(state); + } + + bool + IsStepping ( State state) + { + return state == eStateStepping; + } + bool + CanResume ( State state) + { + return state == eStateStopped; + } + + ArchSpec& + GetArchSpec() + { + return m_arch_spec; + } + const ArchSpec& + GetArchSpec() const + { + return m_arch_spec; + } + + enum + { + eFlagsNone = 0, + eFlagsAttached = (1 << 0), + eFlagsUsingSBS = (1 << 1) + }; + + void + Clear ( ); + + Flags & + GetFlags () + { + return m_flags; + } + + const Flags & + GetFlags () const + { + return m_flags; + } + + uint32_t + UpdateThreadListIfNeeded (); + +private: + //------------------------------------------------------------------ + // For ProcessMacOSXRemote only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ProcessMacOSXRemote); + +}; + +#endif // liblldb_ProcessMacOSXRemote_H_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.cpp new file mode 100644 index 00000000000..37472547f55 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.cpp @@ -0,0 +1,1448 @@ +//===-- RegisterContextMach_arm.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextMach_arm.h" + +// C Includes +#include <mach/thread_act.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Scalar.h" + +// Project includes +#include "ARM_GCC_Registers.h" +#include "ARM_DWARF_Registers.h" +#include "ProcessMacOSXLog.h" + +using namespace lldb; +using namespace lldb_private; + +enum +{ + gpr_r0 = 0, + gpr_r1, + gpr_r2, + gpr_r3, + gpr_r4, + gpr_r5, + gpr_r6, + gpr_r7, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, gpr_sp = gpr_r13, + gpr_r14, gpr_lr = gpr_r14, + gpr_r15, gpr_pc = gpr_r15, + gpr_cpsr, + + fpu_s0, + fpu_s1, + fpu_s2, + fpu_s3, + fpu_s4, + fpu_s5, + fpu_s6, + fpu_s7, + fpu_s8, + fpu_s9, + fpu_s10, + fpu_s11, + fpu_s12, + fpu_s13, + fpu_s14, + fpu_s15, + fpu_s16, + fpu_s17, + fpu_s18, + fpu_s19, + fpu_s20, + fpu_s21, + fpu_s22, + fpu_s23, + fpu_s24, + fpu_s25, + fpu_s26, + fpu_s27, + fpu_s28, + fpu_s29, + fpu_s30, + fpu_s31, + fpu_fpscr, + + exc_exception, + exc_fsr, + exc_far, + + dbg_bvr0, + dbg_bvr1, + dbg_bvr2, + dbg_bvr3, + dbg_bvr4, + dbg_bvr5, + dbg_bvr6, + dbg_bvr7, + dbg_bvr8, + dbg_bvr9, + dbg_bvr10, + dbg_bvr11, + dbg_bvr12, + dbg_bvr13, + dbg_bvr14, + dbg_bvr15, + + dbg_bcr0, + dbg_bcr1, + dbg_bcr2, + dbg_bcr3, + dbg_bcr4, + dbg_bcr5, + dbg_bcr6, + dbg_bcr7, + dbg_bcr8, + dbg_bcr9, + dbg_bcr10, + dbg_bcr11, + dbg_bcr12, + dbg_bcr13, + dbg_bcr14, + dbg_bcr15, + + dbg_wvr0, + dbg_wvr1, + dbg_wvr2, + dbg_wvr3, + dbg_wvr4, + dbg_wvr5, + dbg_wvr6, + dbg_wvr7, + dbg_wvr8, + dbg_wvr9, + dbg_wvr10, + dbg_wvr11, + dbg_wvr12, + dbg_wvr13, + dbg_wvr14, + dbg_wvr15, + + dbg_wcr0, + dbg_wcr1, + dbg_wcr2, + dbg_wcr3, + dbg_wcr4, + dbg_wcr5, + dbg_wcr6, + dbg_wcr7, + dbg_wcr8, + dbg_wcr9, + dbg_wcr10, + dbg_wcr11, + dbg_wcr12, + dbg_wcr13, + dbg_wcr14, + dbg_wcr15, + + k_num_registers +}; + + +RegisterContextMach_arm::RegisterContextMach_arm(Thread &thread, StackFrame *frame) : + RegisterContext(thread, frame), + gpr(), + fpu(), + exc() +{ + uint32_t i; + for (i=0; i<kNumErrors; i++) + { + gpr_errs[i] = -1; + fpu_errs[i] = -1; + exc_errs[i] = -1; + } +} + +RegisterContextMach_arm::~RegisterContextMach_arm() +{ +} + + +#define GPR_OFFSET(idx) ((idx) * 4) +#define FPU_OFFSET(idx) ((idx) * 4 + sizeof (RegisterContextMach_arm::GPR)) +#define EXC_OFFSET(idx) ((idx) * 4 + sizeof (RegisterContextMach_arm::GPR) + sizeof (RegisterContextMach_arm::FPU)) +#define DBG_OFFSET(reg) (offsetof (RegisterContextMach_arm::DBG, reg) + sizeof (RegisterContextMach_arm::GPR) + sizeof (RegisterContextMach_arm::FPU) + sizeof (RegisterContextMach_arm::EXC)) + +#define DEFINE_DBG(reg, i) #reg, NULL, sizeof(((RegisterContextMach_arm::DBG *)NULL)->reg[i]), DBG_OFFSET(reg[i]), eEncodingUint, eFormatHex, dbg_##reg##i, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM } +#define REG_CONTEXT_SIZE (sizeof (RegisterContextMach_arm::GPR) + sizeof (RegisterContextMach_arm::FPU) + sizeof (RegisterContextMach_arm::EXC)) +// General purpose registers +static lldb::RegisterInfo +g_register_infos[] = +{ +// NAME ALT SZ OFFSET ENCODING FORMAT NATIVE COMPILER DWARF GENERIC +// ====== ======= == ============= ============= ============ ========== =============== =============== ========= +{ "r0", NULL, 4, GPR_OFFSET(0), eEncodingUint, eFormatHex, gpr_r0, { gcc_r0, dwarf_r0, LLDB_INVALID_REGNUM }}, +{ "r1", NULL, 4, GPR_OFFSET(1), eEncodingUint, eFormatHex, gpr_r1, { gcc_r1, dwarf_r1, LLDB_INVALID_REGNUM }}, +{ "r2", NULL, 4, GPR_OFFSET(2), eEncodingUint, eFormatHex, gpr_r2, { gcc_r2, dwarf_r2, LLDB_INVALID_REGNUM }}, +{ "r3", NULL, 4, GPR_OFFSET(3), eEncodingUint, eFormatHex, gpr_r3, { gcc_r3, dwarf_r3, LLDB_INVALID_REGNUM }}, +{ "r4", NULL, 4, GPR_OFFSET(4), eEncodingUint, eFormatHex, gpr_r4, { gcc_r4, dwarf_r4, LLDB_INVALID_REGNUM }}, +{ "r5", NULL, 4, GPR_OFFSET(5), eEncodingUint, eFormatHex, gpr_r5, { gcc_r5, dwarf_r5, LLDB_INVALID_REGNUM }}, +{ "r6", NULL, 4, GPR_OFFSET(6), eEncodingUint, eFormatHex, gpr_r6, { gcc_r6, dwarf_r6, LLDB_INVALID_REGNUM }}, +{ "r7", NULL, 4, GPR_OFFSET(7), eEncodingUint, eFormatHex, gpr_r7, { gcc_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP }}, +{ "r8", NULL, 4, GPR_OFFSET(8), eEncodingUint, eFormatHex, gpr_r8, { gcc_r8, dwarf_r8, LLDB_INVALID_REGNUM }}, +{ "r9", NULL, 4, GPR_OFFSET(9), eEncodingUint, eFormatHex, gpr_r9, { gcc_r9, dwarf_r9, LLDB_INVALID_REGNUM }}, +{ "r10", NULL, 4, GPR_OFFSET(10), eEncodingUint, eFormatHex, gpr_r10, { gcc_r10, dwarf_r10, LLDB_INVALID_REGNUM }}, +{ "r11", NULL, 4, GPR_OFFSET(11), eEncodingUint, eFormatHex, gpr_r11, { gcc_r11, dwarf_r11, LLDB_INVALID_REGNUM }}, +{ "r12", NULL, 4, GPR_OFFSET(12), eEncodingUint, eFormatHex, gpr_r12, { gcc_r12, dwarf_r12, LLDB_INVALID_REGNUM }}, +{ "sp", "r13", 4, GPR_OFFSET(13), eEncodingUint, eFormatHex, gpr_sp, { gcc_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP }}, +{ "lr", "r14", 4, GPR_OFFSET(14), eEncodingUint, eFormatHex, gpr_lr, { gcc_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA }}, +{ "pc", "r15", 4, GPR_OFFSET(15), eEncodingUint, eFormatHex, gpr_pc, { gcc_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC }}, +{ "cpsr", "psr", 4, GPR_OFFSET(16), eEncodingUint, eFormatHex, gpr_cpsr, { gcc_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS }}, + +{ "s0", NULL, 4, FPU_OFFSET(0), eEncodingIEEE754,eFormatFloat, fpu_s0, { LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM }}, +{ "s1", NULL, 4, FPU_OFFSET(1), eEncodingIEEE754,eFormatFloat, fpu_s1, { LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM }}, +{ "s2", NULL, 4, FPU_OFFSET(2), eEncodingIEEE754,eFormatFloat, fpu_s2, { LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM }}, +{ "s3", NULL, 4, FPU_OFFSET(3), eEncodingIEEE754,eFormatFloat, fpu_s3, { LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM }}, +{ "s4", NULL, 4, FPU_OFFSET(4), eEncodingIEEE754,eFormatFloat, fpu_s4, { LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM }}, +{ "s5", NULL, 4, FPU_OFFSET(5), eEncodingIEEE754,eFormatFloat, fpu_s5, { LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM }}, +{ "s6", NULL, 4, FPU_OFFSET(6), eEncodingIEEE754,eFormatFloat, fpu_s6, { LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM }}, +{ "s7", NULL, 4, FPU_OFFSET(7), eEncodingIEEE754,eFormatFloat, fpu_s7, { LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM }}, +{ "s8", NULL, 4, FPU_OFFSET(8), eEncodingIEEE754,eFormatFloat, fpu_s8, { LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM }}, +{ "s9", NULL, 4, FPU_OFFSET(9), eEncodingIEEE754,eFormatFloat, fpu_s9, { LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM }}, +{ "s10", NULL, 4, FPU_OFFSET(10), eEncodingIEEE754,eFormatFloat, fpu_s10, { LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM }}, +{ "s11", NULL, 4, FPU_OFFSET(11), eEncodingIEEE754,eFormatFloat, fpu_s11, { LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM }}, +{ "s12", NULL, 4, FPU_OFFSET(12), eEncodingIEEE754,eFormatFloat, fpu_s12, { LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM }}, +{ "s13", NULL, 4, FPU_OFFSET(13), eEncodingIEEE754,eFormatFloat, fpu_s13, { LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM }}, +{ "s14", NULL, 4, FPU_OFFSET(14), eEncodingIEEE754,eFormatFloat, fpu_s14, { LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM }}, +{ "s15", NULL, 4, FPU_OFFSET(15), eEncodingIEEE754,eFormatFloat, fpu_s15, { LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM }}, +{ "s16", NULL, 4, FPU_OFFSET(16), eEncodingIEEE754,eFormatFloat, fpu_s16, { LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM }}, +{ "s17", NULL, 4, FPU_OFFSET(17), eEncodingIEEE754,eFormatFloat, fpu_s17, { LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM }}, +{ "s18", NULL, 4, FPU_OFFSET(18), eEncodingIEEE754,eFormatFloat, fpu_s18, { LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM }}, +{ "s19", NULL, 4, FPU_OFFSET(19), eEncodingIEEE754,eFormatFloat, fpu_s19, { LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM }}, +{ "s20", NULL, 4, FPU_OFFSET(20), eEncodingIEEE754,eFormatFloat, fpu_s20, { LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM }}, +{ "s21", NULL, 4, FPU_OFFSET(21), eEncodingIEEE754,eFormatFloat, fpu_s21, { LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM }}, +{ "s22", NULL, 4, FPU_OFFSET(22), eEncodingIEEE754,eFormatFloat, fpu_s22, { LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM }}, +{ "s23", NULL, 4, FPU_OFFSET(23), eEncodingIEEE754,eFormatFloat, fpu_s23, { LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM }}, +{ "s24", NULL, 4, FPU_OFFSET(24), eEncodingIEEE754,eFormatFloat, fpu_s24, { LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM }}, +{ "s25", NULL, 4, FPU_OFFSET(25), eEncodingIEEE754,eFormatFloat, fpu_s25, { LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM }}, +{ "s26", NULL, 4, FPU_OFFSET(26), eEncodingIEEE754,eFormatFloat, fpu_s26, { LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM }}, +{ "s27", NULL, 4, FPU_OFFSET(27), eEncodingIEEE754,eFormatFloat, fpu_s27, { LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM }}, +{ "s28", NULL, 4, FPU_OFFSET(28), eEncodingIEEE754,eFormatFloat, fpu_s28, { LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM }}, +{ "s29", NULL, 4, FPU_OFFSET(29), eEncodingIEEE754,eFormatFloat, fpu_s29, { LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM }}, +{ "s30", NULL, 4, FPU_OFFSET(30), eEncodingIEEE754,eFormatFloat, fpu_s30, { LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM }}, +{ "s31", NULL, 4, FPU_OFFSET(31), eEncodingIEEE754,eFormatFloat, fpu_s31, { LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM }}, +{ "fpscr", NULL, 4, FPU_OFFSET(32), eEncodingUint, eFormatHex, fpu_fpscr, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }}, + +{ "exception",NULL, 4, EXC_OFFSET(0), eEncodingUint, eFormatHex, exc_exception,{ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }}, +{ "fsr", NULL, 4, EXC_OFFSET(1), eEncodingUint, eFormatHex, exc_fsr, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }}, +{ "far", NULL, 4, EXC_OFFSET(2), eEncodingUint, eFormatHex, exc_far, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }}, + +{ DEFINE_DBG (bvr, 0) }, +{ DEFINE_DBG (bvr, 0) }, +{ DEFINE_DBG (bvr, 1) }, +{ DEFINE_DBG (bvr, 2) }, +{ DEFINE_DBG (bvr, 3) }, +{ DEFINE_DBG (bvr, 4) }, +{ DEFINE_DBG (bvr, 5) }, +{ DEFINE_DBG (bvr, 6) }, +{ DEFINE_DBG (bvr, 7) }, +{ DEFINE_DBG (bvr, 8) }, +{ DEFINE_DBG (bvr, 9) }, +{ DEFINE_DBG (bvr, 10) }, +{ DEFINE_DBG (bvr, 11) }, +{ DEFINE_DBG (bvr, 12) }, +{ DEFINE_DBG (bvr, 13) }, +{ DEFINE_DBG (bvr, 14) }, +{ DEFINE_DBG (bvr, 15) }, + +{ DEFINE_DBG (bcr, 0) }, +{ DEFINE_DBG (bcr, 0) }, +{ DEFINE_DBG (bcr, 1) }, +{ DEFINE_DBG (bcr, 2) }, +{ DEFINE_DBG (bcr, 3) }, +{ DEFINE_DBG (bcr, 4) }, +{ DEFINE_DBG (bcr, 5) }, +{ DEFINE_DBG (bcr, 6) }, +{ DEFINE_DBG (bcr, 7) }, +{ DEFINE_DBG (bcr, 8) }, +{ DEFINE_DBG (bcr, 9) }, +{ DEFINE_DBG (bcr, 10) }, +{ DEFINE_DBG (bcr, 11) }, +{ DEFINE_DBG (bcr, 12) }, +{ DEFINE_DBG (bcr, 13) }, +{ DEFINE_DBG (bcr, 14) }, +{ DEFINE_DBG (bcr, 15) }, + +{ DEFINE_DBG (wvr, 0) }, +{ DEFINE_DBG (wvr, 0) }, +{ DEFINE_DBG (wvr, 1) }, +{ DEFINE_DBG (wvr, 2) }, +{ DEFINE_DBG (wvr, 3) }, +{ DEFINE_DBG (wvr, 4) }, +{ DEFINE_DBG (wvr, 5) }, +{ DEFINE_DBG (wvr, 6) }, +{ DEFINE_DBG (wvr, 7) }, +{ DEFINE_DBG (wvr, 8) }, +{ DEFINE_DBG (wvr, 9) }, +{ DEFINE_DBG (wvr, 10) }, +{ DEFINE_DBG (wvr, 11) }, +{ DEFINE_DBG (wvr, 12) }, +{ DEFINE_DBG (wvr, 13) }, +{ DEFINE_DBG (wvr, 14) }, +{ DEFINE_DBG (wvr, 15) }, + +{ DEFINE_DBG (wcr, 0) }, +{ DEFINE_DBG (wcr, 0) }, +{ DEFINE_DBG (wcr, 1) }, +{ DEFINE_DBG (wcr, 2) }, +{ DEFINE_DBG (wcr, 3) }, +{ DEFINE_DBG (wcr, 4) }, +{ DEFINE_DBG (wcr, 5) }, +{ DEFINE_DBG (wcr, 6) }, +{ DEFINE_DBG (wcr, 7) }, +{ DEFINE_DBG (wcr, 8) }, +{ DEFINE_DBG (wcr, 9) }, +{ DEFINE_DBG (wcr, 10) }, +{ DEFINE_DBG (wcr, 11) }, +{ DEFINE_DBG (wcr, 12) }, +{ DEFINE_DBG (wcr, 13) }, +{ DEFINE_DBG (wcr, 14) }, +{ DEFINE_DBG (wcr, 15) } +}; + +// General purpose registers +static uint32_t +g_gpr_regnums[] = +{ + gpr_r0, + gpr_r1, + gpr_r2, + gpr_r3, + gpr_r4, + gpr_r5, + gpr_r6, + gpr_r7, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_sp, + gpr_lr, + gpr_pc, + gpr_cpsr +}; + +// Floating point registers +static uint32_t +g_fpu_regnums[] = +{ + fpu_s0, + fpu_s1, + fpu_s2, + fpu_s3, + fpu_s4, + fpu_s5, + fpu_s6, + fpu_s7, + fpu_s8, + fpu_s9, + fpu_s10, + fpu_s11, + fpu_s12, + fpu_s13, + fpu_s14, + fpu_s15, + fpu_s16, + fpu_s17, + fpu_s18, + fpu_s19, + fpu_s20, + fpu_s21, + fpu_s22, + fpu_s23, + fpu_s24, + fpu_s25, + fpu_s26, + fpu_s27, + fpu_s28, + fpu_s29, + fpu_s30, + fpu_s31, + fpu_fpscr, +}; + +// Exception registers + +static uint32_t +g_exc_regnums[] = +{ + exc_exception, + exc_fsr, + exc_far, +}; + +static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo)); + +void +RegisterContextMach_arm::Invalidate () +{ + InvalidateAllRegisterStates(); +} + + +size_t +RegisterContextMach_arm::GetRegisterCount () +{ + assert(k_num_register_infos == k_num_registers); + return k_num_registers; +} + +const RegisterInfo * +RegisterContextMach_arm::GetRegisterInfoAtIndex (uint32_t reg) +{ + assert(k_num_register_infos == k_num_registers); + if (reg < k_num_registers) + return &g_register_infos[reg]; + return NULL; +} + +size_t +RegisterContextMach_arm::GetRegisterInfosCount () +{ + return k_num_register_infos; +} + +const RegisterInfo * +RegisterContextMach_arm::GetRegisterInfos () +{ + return g_register_infos; +} + + +// Number of registers in each register set +const size_t k_num_gpr_registers = sizeof(g_gpr_regnums) / sizeof(uint32_t); +const size_t k_num_fpu_registers = sizeof(g_fpu_regnums) / sizeof(uint32_t); +const size_t k_num_exc_registers = sizeof(g_exc_regnums) / sizeof(uint32_t); + +//---------------------------------------------------------------------- +// Register set definitions. The first definitions at register set index +// of zero is for all registers, followed by other registers sets. The +// register information for the all register set need not be filled in. +//---------------------------------------------------------------------- +static const RegisterSet g_reg_sets[] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, }, + { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums }, + { "Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums } +}; + +const size_t k_num_regsets = sizeof(g_reg_sets) / sizeof(RegisterSet); + + +size_t +RegisterContextMach_arm::GetRegisterSetCount () +{ + return k_num_regsets; +} + +const RegisterSet * +RegisterContextMach_arm::GetRegisterSet (uint32_t reg_set) +{ + if (reg_set < k_num_regsets) + return &g_reg_sets[reg_set]; + return NULL; +} + + +//---------------------------------------------------------------------- +// Register information defintions for 32 bit i386. +//---------------------------------------------------------------------- +int +RegisterContextMach_arm::GetSetForNativeRegNum (int reg) +{ + if (reg < fpu_s0) + return GPRRegSet; + else if (reg < exc_exception) + return FPURegSet; + else if (reg < k_num_registers) + return EXCRegSet; + return -1; +} + +kern_return_t +RegisterContextMach_arm::ReadGPR (bool force) +{ + int set = GPRRegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = GPRWordCount; + SetError(GPRRegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&gpr, &count)); + } + return GetError(GPRRegSet, Read); +} + +kern_return_t +RegisterContextMach_arm::ReadFPU (bool force) +{ + int set = FPURegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = FPUWordCount; + SetError(FPURegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&fpu, &count)); + } + return GetError(FPURegSet, Read); +} + +kern_return_t +RegisterContextMach_arm::ReadEXC (bool force) +{ + int set = EXCRegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = EXCWordCount; + SetError(EXCRegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&exc, &count)); + } + return GetError(EXCRegSet, Read); +} + +kern_return_t +RegisterContextMach_arm::ReadDBG (bool force) +{ + int set = DBGRegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = DBGWordCount; + SetError(DBGRegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&dbg, &count)); + } + return GetError(DBGRegSet, Read); +} + +kern_return_t +RegisterContextMach_arm::WriteGPR () +{ + int set = GPRRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError(GPRRegSet, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&gpr, GPRWordCount)); + return GetError(GPRRegSet, Write); +} + +kern_return_t +RegisterContextMach_arm::WriteFPU () +{ + int set = FPURegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError(FPURegSet, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&fpu, FPUWordCount)); + return GetError(FPURegSet, Write); +} + +kern_return_t +RegisterContextMach_arm::WriteEXC () +{ + int set = EXCRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError(EXCRegSet, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&exc, EXCWordCount)); + return GetError(EXCRegSet, Write); +} + +kern_return_t +RegisterContextMach_arm::WriteDBG () +{ + int set = DBGRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError(DBGRegSet, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&dbg, DBGWordCount)); + return GetError(DBGRegSet, Write); +} + + +kern_return_t +RegisterContextMach_arm::ReadRegisterSet (uint32_t set, bool force) +{ + switch (set) + { + case GPRRegSet: return ReadGPR(force); + case FPURegSet: return ReadFPU(force); + case EXCRegSet: return ReadEXC(force); + case DBGRegSet: return ReadDBG(force); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + +kern_return_t +RegisterContextMach_arm::WriteRegisterSet (uint32_t set) +{ + // Make sure we have a valid context to set. + if (RegisterSetIsCached(set)) + { + switch (set) + { + case GPRRegSet: return WriteGPR(); + case FPURegSet: return WriteFPU(); + case EXCRegSet: return WriteEXC(); + case DBGRegSet: return WriteDBG(); + default: break; + } + } + return KERN_INVALID_ARGUMENT; +} + +void +RegisterContextMach_arm::LogDBGRegisters (Log *log, const DBG& dbg) +{ + if (log) + { + for (uint32_t i=0; i<16; i++) + log->Printf("BVR%-2u/BCR%-2u = { 0x%8.8x, 0x%8.8x } WVR%-2u/WCR%-2u = { 0x%8.8x, 0x%8.8x }", + i, i, dbg.bvr[i], dbg.bcr[i], + i, i, dbg.wvr[i], dbg.wcr[i]); + } +} + + +bool +RegisterContextMach_arm::ReadRegisterValue (uint32_t reg, Scalar &value) +{ + int set = RegisterContextMach_arm::GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) + { + case gpr_r0: + case gpr_r1: + case gpr_r2: + case gpr_r3: + case gpr_r4: + case gpr_r5: + case gpr_r6: + case gpr_r7: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_sp: + case gpr_lr: + case gpr_pc: + case gpr_cpsr: + value = gpr.r[reg - gpr_r0]; + break; + + case fpu_s0: + case fpu_s1: + case fpu_s2: + case fpu_s3: + case fpu_s4: + case fpu_s5: + case fpu_s6: + case fpu_s7: + case fpu_s8: + case fpu_s9: + case fpu_s10: + case fpu_s11: + case fpu_s12: + case fpu_s13: + case fpu_s14: + case fpu_s15: + case fpu_s16: + case fpu_s17: + case fpu_s18: + case fpu_s19: + case fpu_s20: + case fpu_s21: + case fpu_s22: + case fpu_s23: + case fpu_s24: + case fpu_s25: + case fpu_s26: + case fpu_s27: + case fpu_s28: + case fpu_s29: + case fpu_s30: + case fpu_s31: + value = fpu.floats.s[reg]; + break; + + case fpu_fpscr: + value = fpu.fpscr; + break; + + case exc_exception: + value = exc.exception; + break; + case exc_fsr: + value = exc.fsr; + break; + case exc_far: + value = exc.far; + break; + + default: + return false; + + } + return true; +} + + +bool +RegisterContextMach_arm::WriteRegisterValue (uint32_t reg, const Scalar &value) +{ + int set = GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) + { + case gpr_r0: + case gpr_r1: + case gpr_r2: + case gpr_r3: + case gpr_r4: + case gpr_r5: + case gpr_r6: + case gpr_r7: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_sp: + case gpr_lr: + case gpr_pc: + case gpr_cpsr: + gpr.r[reg - gpr_r0] = value.UInt(0); + break; + + case fpu_s0: + case fpu_s1: + case fpu_s2: + case fpu_s3: + case fpu_s4: + case fpu_s5: + case fpu_s6: + case fpu_s7: + case fpu_s8: + case fpu_s9: + case fpu_s10: + case fpu_s11: + case fpu_s12: + case fpu_s13: + case fpu_s14: + case fpu_s15: + case fpu_s16: + case fpu_s17: + case fpu_s18: + case fpu_s19: + case fpu_s20: + case fpu_s21: + case fpu_s22: + case fpu_s23: + case fpu_s24: + case fpu_s25: + case fpu_s26: + case fpu_s27: + case fpu_s28: + case fpu_s29: + case fpu_s30: + case fpu_s31: + fpu.floats.s[reg] = value.UInt(0); + break; + + case fpu_fpscr: + fpu.fpscr = value.UInt(0); + break; + + case exc_exception: + exc.exception = value.UInt(0); + break; + case exc_fsr: + exc.fsr = value.UInt(0); + break; + case exc_far: + exc.far = value.UInt(0); + break; + + default: + return false; + + } + return WriteRegisterSet(set) == KERN_SUCCESS; +} + +bool +RegisterContextMach_arm::ReadRegisterBytes (uint32_t reg, DataExtractor &data) +{ + int set = RegisterContextMach_arm::GetSetForNativeRegNum (reg); + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info == NULL) + return false; + + switch (reg) + { + case gpr_r0: + case gpr_r1: + case gpr_r2: + case gpr_r3: + case gpr_r4: + case gpr_r5: + case gpr_r6: + case gpr_r7: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_sp: + case gpr_lr: + case gpr_pc: + case gpr_cpsr: + data.SetData(&gpr.r[reg - gpr_r0], reg_info->byte_size, eByteOrderHost); + break; + + case fpu_s0: + case fpu_s1: + case fpu_s2: + case fpu_s3: + case fpu_s4: + case fpu_s5: + case fpu_s6: + case fpu_s7: + case fpu_s8: + case fpu_s9: + case fpu_s10: + case fpu_s11: + case fpu_s12: + case fpu_s13: + case fpu_s14: + case fpu_s15: + case fpu_s16: + case fpu_s17: + case fpu_s18: + case fpu_s19: + case fpu_s20: + case fpu_s21: + case fpu_s22: + case fpu_s23: + case fpu_s24: + case fpu_s25: + case fpu_s26: + case fpu_s27: + case fpu_s28: + case fpu_s29: + case fpu_s30: + case fpu_s31: + data.SetData(&fpu.floats.s[reg - fpu_s0], reg_info->byte_size, eByteOrderHost); + break; + + case fpu_fpscr: + data.SetData(&fpu.fpscr, reg_info->byte_size, eByteOrderHost); + break; + + case exc_exception: + data.SetData(&exc.exception, reg_info->byte_size, eByteOrderHost); + break; + + case exc_fsr: + data.SetData(&exc.fsr, reg_info->byte_size, eByteOrderHost); + break; + + case exc_far: + data.SetData(&exc.far, reg_info->byte_size, eByteOrderHost); + break; + + default: + return false; + } + return true; +} + +bool +RegisterContextMach_arm::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset) +{ + int set = GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + + const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info == NULL && data.ValidOffsetForDataOfSize(data_offset, reg_info->byte_size)) + return false; + + uint32_t offset = data_offset; + switch (reg) + { + case gpr_r0: + case gpr_r1: + case gpr_r2: + case gpr_r3: + case gpr_r4: + case gpr_r5: + case gpr_r6: + case gpr_r7: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_sp: + case gpr_lr: + case gpr_pc: + case gpr_cpsr: + gpr.r[reg - gpr_r0] = data.GetU32 (&offset); + break; + + case fpu_s0: + case fpu_s1: + case fpu_s2: + case fpu_s3: + case fpu_s4: + case fpu_s5: + case fpu_s6: + case fpu_s7: + case fpu_s8: + case fpu_s9: + case fpu_s10: + case fpu_s11: + case fpu_s12: + case fpu_s13: + case fpu_s14: + case fpu_s15: + case fpu_s16: + case fpu_s17: + case fpu_s18: + case fpu_s19: + case fpu_s20: + case fpu_s21: + case fpu_s22: + case fpu_s23: + case fpu_s24: + case fpu_s25: + case fpu_s26: + case fpu_s27: + case fpu_s28: + case fpu_s29: + case fpu_s30: + case fpu_s31: + fpu.floats.s[reg - fpu_s0] = data.GetU32 (&offset); + break; + + case fpu_fpscr: + fpu.fpscr = data.GetU32 (&offset); + break; + + case exc_exception: + fpu.fpscr = data.GetU32 (&offset); + break; + + case exc_fsr: + exc.fsr = data.GetU32 (&offset); + break; + + case exc_far: + exc.far = data.GetU32 (&offset); + break; + + default: + return false; + } + return WriteRegisterSet(set) == KERN_SUCCESS; +} + +bool +RegisterContextMach_arm::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (data_sp && + ReadGPR (false) == KERN_SUCCESS && + ReadFPU (false) == KERN_SUCCESS && + ReadEXC (false) == KERN_SUCCESS) + { + uint8_t *dst = data_sp->GetBytes(); + ::memcpy (dst, &gpr, sizeof(gpr)); + dst += sizeof(gpr); + + ::memcpy (dst, &fpu, sizeof(fpu)); + dst += sizeof(gpr); + + ::memcpy (dst, &exc, sizeof(exc)); + return true; + } + return false; +} + +bool +RegisterContextMach_arm::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) + { + const uint8_t *src = data_sp->GetBytes(); + ::memcpy (&gpr, src, sizeof(gpr)); + src += sizeof(gpr); + + ::memcpy (&fpu, src, sizeof(fpu)); + src += sizeof(gpr); + + ::memcpy (&exc, src, sizeof(exc)); + uint32_t success_count = 0; + if (WriteGPR() == KERN_SUCCESS) + ++success_count; + if (WriteFPU() == KERN_SUCCESS) + ++success_count; + if (WriteEXC() == KERN_SUCCESS) + ++success_count; + return success_count == 3; + } + return false; +} + +uint32_t +RegisterContextMach_arm::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t reg) +{ + if (kind == eRegisterKindGeneric) + { + switch (reg) + { + case LLDB_REGNUM_GENERIC_PC: return gpr_pc; + case LLDB_REGNUM_GENERIC_SP: return gpr_sp; + case LLDB_REGNUM_GENERIC_FP: return gpr_r7; + case LLDB_REGNUM_GENERIC_RA: return gpr_lr; + case LLDB_REGNUM_GENERIC_FLAGS: return gpr_cpsr; + default: + break; + } + } + else if (kind == eRegisterKindDWARF) + { + switch (reg) + { + case dwarf_r0: return gpr_r0; + case dwarf_r1: return gpr_r1; + case dwarf_r2: return gpr_r2; + case dwarf_r3: return gpr_r3; + case dwarf_r4: return gpr_r4; + case dwarf_r5: return gpr_r5; + case dwarf_r6: return gpr_r6; + case dwarf_r7: return gpr_r7; + case dwarf_r8: return gpr_r8; + case dwarf_r9: return gpr_r9; + case dwarf_r10: return gpr_r10; + case dwarf_r11: return gpr_r11; + case dwarf_r12: return gpr_r12; + case dwarf_sp: return gpr_sp; + case dwarf_lr: return gpr_lr; + case dwarf_pc: return gpr_pc; + case dwarf_spsr: return gpr_cpsr; + + case dwarf_s0: return fpu_s0; + case dwarf_s1: return fpu_s1; + case dwarf_s2: return fpu_s2; + case dwarf_s3: return fpu_s3; + case dwarf_s4: return fpu_s4; + case dwarf_s5: return fpu_s5; + case dwarf_s6: return fpu_s6; + case dwarf_s7: return fpu_s7; + case dwarf_s8: return fpu_s8; + case dwarf_s9: return fpu_s9; + case dwarf_s10: return fpu_s10; + case dwarf_s11: return fpu_s11; + case dwarf_s12: return fpu_s12; + case dwarf_s13: return fpu_s13; + case dwarf_s14: return fpu_s14; + case dwarf_s15: return fpu_s15; + case dwarf_s16: return fpu_s16; + case dwarf_s17: return fpu_s17; + case dwarf_s18: return fpu_s18; + case dwarf_s19: return fpu_s19; + case dwarf_s20: return fpu_s20; + case dwarf_s21: return fpu_s21; + case dwarf_s22: return fpu_s22; + case dwarf_s23: return fpu_s23; + case dwarf_s24: return fpu_s24; + case dwarf_s25: return fpu_s25; + case dwarf_s26: return fpu_s26; + case dwarf_s27: return fpu_s27; + case dwarf_s28: return fpu_s28; + case dwarf_s29: return fpu_s29; + case dwarf_s30: return fpu_s30; + case dwarf_s31: return fpu_s31; + + default: + break; + } + } + else if (kind == eRegisterKindGCC) + { + switch (reg) + { + case gcc_r0: return gpr_r0; + case gcc_r1: return gpr_r1; + case gcc_r2: return gpr_r2; + case gcc_r3: return gpr_r3; + case gcc_r4: return gpr_r4; + case gcc_r5: return gpr_r5; + case gcc_r6: return gpr_r6; + case gcc_r7: return gpr_r7; + case gcc_r8: return gpr_r8; + case gcc_r9: return gpr_r9; + case gcc_r10: return gpr_r10; + case gcc_r11: return gpr_r11; + case gcc_r12: return gpr_r12; + case gcc_sp: return gpr_sp; + case gcc_lr: return gpr_lr; + case gcc_pc: return gpr_pc; + case gcc_cpsr: return gpr_cpsr; + } + } + return LLDB_INVALID_REGNUM; +} + + +uint32_t +RegisterContextMach_arm::NumSupportedHardwareBreakpoints () +{ +#if defined (__arm__) + // Set the init value to something that will let us know that we need to + // autodetect how many breakpoints are supported dynamically... + static uint32_t g_num_supported_hw_breakpoints = UINT_MAX + if (g_num_supported_hw_breakpoints == UINT_MAX) + { + // Set this to zero in case we can't tell if there are any HW breakpoints + g_num_supported_hw_breakpoints = 0; + + // Read the DBGDIDR to get the number of available hardware breakpoints + // However, in some of our current armv7 processors, hardware + // breakpoints/watchpoints were not properly connected. So detect those + // cases using a field in a sysctl. For now we are using "hw.cpusubtype" + // field to distinguish CPU architectures. This is a hack until we can + // get <rdar://problem/6372672> fixed, at which point we will switch to + // using a different sysctl string that will tell us how many BRPs + // are available to us directly without having to read DBGDIDR. + uint32_t register_DBGDIDR; + + asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR)); + uint32_t numBRPs = bits(register_DBGDIDR, 27, 24); + // Zero is reserved for the BRP count, so don't increment it if it is zero + if (numBRPs > 0) + numBRPs++; + ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "DBGDIDR=0x%8.8x (number BRP pairs = %u)", register_DBGDIDR, numBRPs); + + if (numBRPs > 0) + { + uint32_t cpu_subtype; + size_t len; + len = sizeof(cpusubtype); + // TODO: remove this hack and change to using hw.optional.xx when implmented + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) + { + ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "hw.cpusubtype=0x%d", cpusubtype); + if (cpusubtype == CPU_SUBTYPE_ARM_V7) + ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "Hardware breakpoints disabled for armv7 (rdar://problem/6372672)"); + else + g_num_supported_hw_breakpoints = numBRPs; + } + } + + } + return g_num_supported_hw_breakpoints; +#else + // TODO: figure out remote case here! + return 6; +#endif +} + +uint32_t +RegisterContextMach_arm::SetHardwareBreakpoint (lldb::addr_t addr, size_t size) +{ + // Make sure our address isn't bogus + if (addr & 1) + return LLDB_INVALID_INDEX32; + + kern_return_t kret = ReadDBG (false); + + if (kret == KERN_SUCCESS) + { + const uint32_t num_hw_breakpoints = NumSupportedHardwareBreakpoints(); + uint32_t i; + for (i=0; i<num_hw_breakpoints; ++i) + { + if ((dbg.bcr[i] & BCR_ENABLE) == 0) + break; // We found an available hw breakpoint slot (in i) + } + + // See if we found an available hw breakpoint slot above + if (i < num_hw_breakpoints) + { + // Make sure bits 1:0 are clear in our address + dbg.bvr[i] = addr & ~((lldb::addr_t)3); + + if (size == 2 || addr & 2) + { + uint32_t byte_addr_select = (addr & 2) ? BAS_IMVA_2_3 : BAS_IMVA_0_1; + + // We have a thumb breakpoint + // We have an ARM breakpoint + dbg.bcr[i] = BCR_M_IMVA_MATCH | // Stop on address mismatch + byte_addr_select | // Set the correct byte address select so we only trigger on the correct opcode + S_USER | // Which modes should this breakpoint stop in? + BCR_ENABLE; // Enable this hardware breakpoint + ProcessMacOSXLog::LogIf(PD_LOG_BREAKPOINTS, "RegisterContextMach_arm::EnableHardwareBreakpoint( addr = %8.8p, size = %u ) - BVR%u/BCR%u = 0x%8.8x / 0x%8.8x (Thumb)", + addr, + size, + i, + i, + dbg.bvr[i], + dbg.bcr[i]); + } + else if (size == 4) + { + // We have an ARM breakpoint + dbg.bcr[i] = BCR_M_IMVA_MATCH | // Stop on address mismatch + BAS_IMVA_ALL | // Stop on any of the four bytes following the IMVA + S_USER | // Which modes should this breakpoint stop in? + BCR_ENABLE; // Enable this hardware breakpoint + ProcessMacOSXLog::LogIf(PD_LOG_BREAKPOINTS, "RegisterContextMach_arm::EnableHardwareBreakpoint( addr = %8.8p, size = %u ) - BVR%u/BCR%u = 0x%8.8x / 0x%8.8x (ARM)", + addr, + size, + i, + i, + dbg.bvr[i], + dbg.bcr[i]); + } + + kret = WriteDBG(); + ProcessMacOSXLog::LogIf(PD_LOG_BREAKPOINTS, "RegisterContextMach_arm::EnableHardwareBreakpoint() WriteDBG() => 0x%8.8x.", kret); + + if (kret == KERN_SUCCESS) + return i; + } + else + { + ProcessMacOSXLog::LogIf(PD_LOG_BREAKPOINTS, "RegisterContextMach_arm::EnableHardwareBreakpoint(addr = %8.8p, size = %u) => all hardware breakpoint resources are being used.", addr, size); + } + } + + return LLDB_INVALID_INDEX32; +} + +bool +RegisterContextMach_arm::ClearHardwareBreakpoint (uint32_t hw_index) +{ + kern_return_t kret = ReadDBG (false); + + const uint32_t num_hw_points = NumSupportedHardwareBreakpoints(); + if (kret == KERN_SUCCESS) + { + if (hw_index < num_hw_points) + { + dbg.bcr[hw_index] = 0; + ProcessMacOSXLog::LogIf(PD_LOG_BREAKPOINTS, "RegisterContextMach_arm::SetHardwareBreakpoint( %u ) - BVR%u = 0x%8.8x BCR%u = 0x%8.8x", + hw_index, + hw_index, + dbg.bvr[hw_index], + hw_index, + dbg.bcr[hw_index]); + + kret = WriteDBG(); + + if (kret == KERN_SUCCESS) + return true; + } + } + return false; +} + +uint32_t +RegisterContextMach_arm::NumSupportedHardwareWatchpoints () +{ +#if defined (__arm__) + // Set the init value to something that will let us know that we need to + // autodetect how many watchpoints are supported dynamically... + static uint32_t g_num_supported_hw_watchpoints = UINT_MAX; + if (g_num_supported_hw_watchpoints == UINT_MAX) + { + // Set this to zero in case we can't tell if there are any HW breakpoints + g_num_supported_hw_watchpoints = 0; + // Read the DBGDIDR to get the number of available hardware breakpoints + // However, in some of our current armv7 processors, hardware + // breakpoints/watchpoints were not properly connected. So detect those + // cases using a field in a sysctl. For now we are using "hw.cpusubtype" + // field to distinguish CPU architectures. This is a hack until we can + // get <rdar://problem/6372672> fixed, at which point we will switch to + // using a different sysctl string that will tell us how many WRPs + // are available to us directly without having to read DBGDIDR. + + uint32_t register_DBGDIDR; + asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR)); + uint32_t numWRPs = bits(register_DBGDIDR, 31, 28) + 1; + ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "DBGDIDR=0x%8.8x (number WRP pairs = %u)", register_DBGDIDR, numWRPs); + + if (numWRPs > 0) + { + uint32_t cpusubtype; + size_t len; + len = sizeof(cpusubtype); + // TODO: remove this hack and change to using hw.optional.xx when implmented + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) + { + ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "hw.cpusubtype=0x%d", cpusubtype); + + if (cpusubtype == CPU_SUBTYPE_ARM_V7) + ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "Hardware watchpoints disabled for armv7 (rdar://problem/6372672)"); + else + g_num_supported_hw_watchpoints = numWRPs; + } + } + + } + return g_num_supported_hw_watchpoints; +#else + // TODO: figure out remote case here! + return 2; +#endif +} + + +uint32_t +RegisterContextMach_arm::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write) +{ + ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::EnableHardwareWatchpoint(addr = %8.8p, size = %u, read = %u, write = %u)", addr, size, read, write); + + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + + // Can't watch zero bytes + if (size == 0) + return LLDB_INVALID_INDEX32; + + // We must watch for either read or write + if (read == false && write == false) + return LLDB_INVALID_INDEX32; + + // Can't watch more than 4 bytes per WVR/WCR pair + if (size > 4) + return LLDB_INVALID_INDEX32; + + // We can only watch up to four bytes that follow a 4 byte aligned address + // per watchpoint register pair. Since we have at most so we can only watch + // until the next 4 byte boundary and we need to make sure we can properly + // encode this. + uint32_t addr_word_offset = addr % 4; + ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::EnableHardwareWatchpoint() - addr_word_offset = 0x%8.8x", addr_word_offset); + + uint32_t byte_mask = ((1u << size) - 1u) << addr_word_offset; + ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::EnableHardwareWatchpoint() - byte_mask = 0x%8.8x", byte_mask); + if (byte_mask > 0xfu) + return LLDB_INVALID_INDEX32; + + // Read the debug state + kern_return_t kret = ReadDBG (false); + + if (kret == KERN_SUCCESS) + { + // Check to make sure we have the needed hardware support + uint32_t i = 0; + + for (i=0; i<num_hw_watchpoints; ++i) + { + if ((dbg.wcr[i] & WCR_ENABLE) == 0) + break; // We found an available hw breakpoint slot (in i) + } + + // See if we found an available hw breakpoint slot above + if (i < num_hw_watchpoints) + { + // Make the byte_mask into a valid Byte Address Select mask + uint32_t byte_address_select = byte_mask << 5; + // Make sure bits 1:0 are clear in our address + dbg.wvr[i] = addr & ~((lldb::addr_t)3); + dbg.wcr[i] = byte_address_select | // Which bytes that follow the IMVA that we will watch + S_USER | // Stop only in user mode + (read ? WCR_LOAD : 0) | // Stop on read access? + (write ? WCR_STORE : 0) | // Stop on write access? + WCR_ENABLE; // Enable this watchpoint; + + kret = WriteDBG(); + ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::EnableHardwareWatchpoint() WriteDBG() => 0x%8.8x.", kret); + + if (kret == KERN_SUCCESS) + return i; + } + else + { + ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::EnableHardwareWatchpoint(): All hardware resources (%u) are in use.", num_hw_watchpoints); + } + } + return LLDB_INVALID_INDEX32; +} + +bool +RegisterContextMach_arm::ClearHardwareWatchpoint (uint32_t hw_index) +{ + kern_return_t kret = ReadDBG (false); + + const uint32_t num_hw_points = NumSupportedHardwareWatchpoints(); + if (kret == KERN_SUCCESS) + { + if (hw_index < num_hw_points) + { + dbg.wcr[hw_index] = 0; + ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::ClearHardwareWatchpoint( %u ) - WVR%u = 0x%8.8x WCR%u = 0x%8.8x", + hw_index, + hw_index, + dbg.wvr[hw_index], + hw_index, + dbg.wcr[hw_index]); + + kret = WriteDBG(); + + if (kret == KERN_SUCCESS) + return true; + } + } + return false; +} + + diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.h b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.h new file mode 100644 index 00000000000..37821cdd536 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.h @@ -0,0 +1,302 @@ +//===-- RegisterContextMach_arm.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_RegisterContextMach_arm_h_ +#define liblldb_RegisterContextMach_arm_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" + +// BCR address match type +#define BCR_M_IMVA_MATCH ((uint32_t)(0u << 21)) +#define BCR_M_CONTEXT_ID_MATCH ((uint32_t)(1u << 21)) +#define BCR_M_IMVA_MISMATCH ((uint32_t)(2u << 21)) +#define BCR_M_RESERVED ((uint32_t)(3u << 21)) + +// Link a BVR/BCR or WVR/WCR pair to another +#define E_ENABLE_LINKING ((uint32_t)(1u << 20)) + +// Byte Address Select +#define BAS_IMVA_PLUS_0 ((uint32_t)(1u << 5)) +#define BAS_IMVA_PLUS_1 ((uint32_t)(1u << 6)) +#define BAS_IMVA_PLUS_2 ((uint32_t)(1u << 7)) +#define BAS_IMVA_PLUS_3 ((uint32_t)(1u << 8)) +#define BAS_IMVA_0_1 ((uint32_t)(3u << 5)) +#define BAS_IMVA_2_3 ((uint32_t)(3u << 7)) +#define BAS_IMVA_ALL ((uint32_t)(0xfu << 5)) + +// Break only in priveleged or user mode +#define S_RSVD ((uint32_t)(0u << 1)) +#define S_PRIV ((uint32_t)(1u << 1)) +#define S_USER ((uint32_t)(2u << 1)) +#define S_PRIV_USER ((S_PRIV) | (S_USER)) + +#define BCR_ENABLE ((uint32_t)(1u)) +#define WCR_ENABLE ((uint32_t)(1u)) + +// Watchpoint load/store +#define WCR_LOAD ((uint32_t)(1u << 3)) +#define WCR_STORE ((uint32_t)(1u << 4)) + +class RegisterContextMach_arm : public lldb_private::RegisterContext +{ +public: + + RegisterContextMach_arm(lldb_private::Thread &thread, lldb_private::StackFrame *frame); + + virtual + ~RegisterContextMach_arm(); + + 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 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 = 0); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + + virtual uint32_t + NumSupportedHardwareBreakpoints (); + + virtual uint32_t + SetHardwareBreakpoint (lldb::addr_t addr, size_t size); + + virtual bool + ClearHardwareBreakpoint (uint32_t hw_idx); + + virtual uint32_t + NumSupportedHardwareWatchpoints (); + + virtual uint32_t + SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write); + + virtual bool + ClearHardwareWatchpoint (uint32_t hw_index); + + struct GPR + { + uint32_t r[16]; // R0-R15 + uint32_t cpsr; // CPSR + }; + + + struct FPU + { + union { + uint32_t s[32]; + uint64_t d[16]; + } floats; + uint32_t fpscr; + }; + +// struct NeonReg +// { +// uint8_t bytes[16]; +// }; +// +// struct VFPv3 +// { +// union { +// uint32_t s[32]; +// uint64_t d[32]; +// NeonReg q[16]; +// } v3; +// uint32_t fpscr; +// }; + + struct EXC + { + uint32_t exception; + uint32_t fsr; /* Fault status */ + uint32_t far; /* Virtual Fault Address */ + }; + + struct DBG + { + uint32_t bvr[16]; + uint32_t bcr[16]; + uint32_t wvr[16]; + uint32_t wcr[16]; + }; + + static void + LogDBGRegisters (lldb_private::Log *log, const DBG& dbg); + +protected: + + typedef enum + { + GPRRegSet = 1, + FPURegSet = 2, + EXCRegSet = 3, + DBGRegSet = 4, + }; + + enum + { + GPRWordCount = sizeof(GPR)/sizeof(uint32_t), + FPUWordCount = sizeof(FPU)/sizeof(uint32_t), + EXCWordCount = sizeof(EXC)/sizeof(uint32_t), + DBGWordCount = sizeof(DBG)/sizeof(uint32_t) + }; + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + GPR gpr; + FPU fpu; + EXC exc; + DBG dbg; + kern_return_t gpr_errs[2]; // Read/Write errors + kern_return_t fpu_errs[2]; // Read/Write errors + kern_return_t exc_errs[2]; // Read/Write errors + kern_return_t dbg_errs[2]; // Read/Write errors + + void + InvalidateAllRegisterStates() + { + SetError (GPRRegSet, Read, -1); + SetError (FPURegSet, Read, -1); + SetError (EXCRegSet, Read, -1); + } + + kern_return_t + GetError (int flavor, uint32_t err_idx) const + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + // When getting all errors, just OR all values together to see if + // we got any kind of error. + case GPRRegSet: return gpr_errs[err_idx]; + case FPURegSet: return fpu_errs[err_idx]; + case EXCRegSet: return exc_errs[err_idx]; + case DBGRegSet: return dbg_errs[err_idx]; + default: break; + } + } + return -1; + } + + bool + SetError (int flavor, uint32_t err_idx, kern_return_t err) + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + case GPRRegSet: + gpr_errs[err_idx] = err; + return true; + + case FPURegSet: + fpu_errs[err_idx] = err; + return true; + + case EXCRegSet: + exc_errs[err_idx] = err; + return true; + + case DBGRegSet: + exc_errs[err_idx] = err; + return true; + + default: break; + } + } + return false; + } + + bool + RegisterSetIsCached (int set) const + { + return GetError(set, Read) == KERN_SUCCESS; + } + + kern_return_t + ReadGPR (bool force); + + kern_return_t + ReadFPU (bool force); + + kern_return_t + ReadEXC (bool force); + + kern_return_t + ReadDBG (bool force); + + kern_return_t + WriteGPR (); + + kern_return_t + WriteFPU (); + + kern_return_t + WriteEXC (); + + kern_return_t + WriteDBG (); + + kern_return_t + ReadRegisterSet (uint32_t set, bool force); + + kern_return_t + WriteRegisterSet (uint32_t set); + + static uint32_t + GetRegisterNumber (uint32_t reg_kind, uint32_t reg_num); + + static int + GetSetForNativeRegNum (int reg_num); + + static size_t + GetRegisterInfosCount (); + + static const lldb::RegisterInfo * + GetRegisterInfos (); +}; + +#endif // liblldb_RegisterContextMach_arm_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.cpp new file mode 100644 index 00000000000..daa4f0d4916 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.cpp @@ -0,0 +1,1202 @@ +//===-- RegisterContextMach_i386.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 +#include <mach/thread_act.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Scalar.h" + +// Project includes +#include "RegisterContextMach_i386.h" +#include "ProcessMacOSXLog.h" + +using namespace lldb; +using namespace lldb_private; + +enum +{ + gpr_eax = 0, + gpr_ebx, + gpr_ecx, + gpr_edx, + gpr_edi, + gpr_esi, + gpr_ebp, + gpr_esp, + gpr_ss, + gpr_eflags, + gpr_eip, + gpr_cs, + gpr_ds, + gpr_es, + gpr_fs, + gpr_gs, + + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + + exc_trapno, + exc_err, + exc_faultvaddr, + + k_num_registers, + + // Aliases + fpu_fctrl = fpu_fcw, + fpu_fstat = fpu_fsw, + fpu_ftag = fpu_ftw, + fpu_fiseg = fpu_cs, + fpu_fioff = fpu_ip, + fpu_foseg = fpu_ds, + fpu_fooff = fpu_dp +}; + +enum +{ + gcc_eax = 0, + gcc_ecx, + gcc_edx, + gcc_ebx, + gcc_ebp, + gcc_esp, + gcc_esi, + gcc_edi, + gcc_eip, + gcc_eflags +}; + +enum +{ + dwarf_eax = 0, + dwarf_ecx, + dwarf_edx, + dwarf_ebx, + dwarf_esp, + dwarf_ebp, + dwarf_esi, + dwarf_edi, + dwarf_eip, + dwarf_eflags, + dwarf_stmm0 = 11, + dwarf_stmm1, + dwarf_stmm2, + dwarf_stmm3, + dwarf_stmm4, + dwarf_stmm5, + dwarf_stmm6, + dwarf_stmm7, + dwarf_xmm0 = 21, + dwarf_xmm1, + dwarf_xmm2, + dwarf_xmm3, + dwarf_xmm4, + dwarf_xmm5, + dwarf_xmm6, + dwarf_xmm7 +}; + +enum +{ + gdb_eax = 0, + gdb_ecx = 1, + gdb_edx = 2, + gdb_ebx = 3, + gdb_esp = 4, + gdb_ebp = 5, + gdb_esi = 6, + gdb_edi = 7, + gdb_eip = 8, + gdb_eflags = 9, + gdb_cs = 10, + gdb_ss = 11, + gdb_ds = 12, + gdb_es = 13, + gdb_fs = 14, + gdb_gs = 15, + gdb_stmm0 = 16, + gdb_stmm1 = 17, + gdb_stmm2 = 18, + gdb_stmm3 = 19, + gdb_stmm4 = 20, + gdb_stmm5 = 21, + gdb_stmm6 = 22, + gdb_stmm7 = 23, + gdb_fctrl = 24, gdb_fcw = gdb_fctrl, + gdb_fstat = 25, gdb_fsw = gdb_fstat, + gdb_ftag = 26, gdb_ftw = gdb_ftag, + gdb_fiseg = 27, gdb_fpu_cs = gdb_fiseg, + gdb_fioff = 28, gdb_ip = gdb_fioff, + gdb_foseg = 29, gdb_fpu_ds = gdb_foseg, + gdb_fooff = 30, gdb_dp = gdb_fooff, + gdb_fop = 31, + gdb_xmm0 = 32, + gdb_xmm1 = 33, + gdb_xmm2 = 34, + gdb_xmm3 = 35, + gdb_xmm4 = 36, + gdb_xmm5 = 37, + gdb_xmm6 = 38, + gdb_xmm7 = 39, + gdb_mxcsr = 40, + gdb_mm0 = 41, + gdb_mm1 = 42, + gdb_mm2 = 43, + gdb_mm3 = 44, + gdb_mm4 = 45, + gdb_mm5 = 46, + gdb_mm6 = 47, + gdb_mm7 = 48 +}; + +RegisterContextMach_i386::RegisterContextMach_i386 (Thread &thread, StackFrame *frame) : + RegisterContext(thread, frame), + gpr(), + fpu(), + exc() +{ + uint32_t i; + for (i=0; i<kNumErrors; i++) + { + gpr_errs[i] = -1; + fpu_errs[i] = -1; + exc_errs[i] = -1; + } +} + +RegisterContextMach_i386::~RegisterContextMach_i386() +{ +} + + + +#define GPR_OFFSET(reg) (offsetof (RegisterContextMach_i386::GPR, reg)) +#define FPU_OFFSET(reg) (offsetof (RegisterContextMach_i386::FPU, reg) + sizeof (RegisterContextMach_i386::GPR)) +#define EXC_OFFSET(reg) (offsetof (RegisterContextMach_i386::EXC, reg) + sizeof (RegisterContextMach_i386::GPR) + sizeof (RegisterContextMach_i386::FPU)) + +// These macros will auto define the register name, alt name, register size, +// register offset, encoding, format and native register. This ensures that +// the register state structures are defined correctly and have the correct +// sizes and offsets. +#define DEFINE_GPR(reg, alt) #reg, alt, sizeof(((RegisterContextMach_i386::GPR *)NULL)->reg), GPR_OFFSET(reg), eEncodingUint, eFormatHex, gpr_##reg +#define DEFINE_FPU_UINT(reg) #reg, NULL, sizeof(((RegisterContextMach_i386::FPU *)NULL)->reg), FPU_OFFSET(reg), eEncodingUint, eFormatHex, fpu_##reg +#define DEFINE_FPU_VECT(reg, i) #reg#i, NULL, sizeof(((RegisterContextMach_i386::FPU *)NULL)->reg[i].bytes), FPU_OFFSET(reg[i]), eEncodingVector, eFormatVectorOfUInt8, fpu_##reg##i, { LLDB_INVALID_REGNUM, dwarf_##reg##i, LLDB_INVALID_REGNUM, gdb_##reg##i } + +#define DEFINE_EXC(reg) #reg, NULL, sizeof(((RegisterContextMach_i386::EXC *)NULL)->reg), EXC_OFFSET(reg), eEncodingUint, eFormatHex, exc_##reg +#define REG_CONTEXT_SIZE (sizeof (RegisterContextMach_i386::GPR) + sizeof (RegisterContextMach_i386::FPU) + sizeof (RegisterContextMach_i386::EXC)) + +static RegisterInfo g_register_infos[] = +{ +// Macro auto defines most stuff GCC REG KIND NUM DWARF REG KIND NUM GENERIC REG KIND NUM GDB REG KIND NUM +// =============================== ======================= =================== ========================== ========================== + { DEFINE_GPR(eax , NULL) , { gcc_eax , dwarf_eax , LLDB_INVALID_REGNUM , gdb_eax }}, + { DEFINE_GPR(ebx , NULL) , { gcc_ebx , dwarf_ebx , LLDB_INVALID_REGNUM , gdb_ebx }}, + { DEFINE_GPR(ecx , NULL) , { gcc_ecx , dwarf_ecx , LLDB_INVALID_REGNUM , gdb_ecx }}, + { DEFINE_GPR(edx , NULL) , { gcc_edx , dwarf_edx , LLDB_INVALID_REGNUM , gdb_edx }}, + { DEFINE_GPR(edi , NULL) , { gcc_edi , dwarf_edi , LLDB_INVALID_REGNUM , gdb_edi }}, + { DEFINE_GPR(esi , NULL) , { gcc_esi , dwarf_esi , LLDB_INVALID_REGNUM , gdb_esi }}, + { DEFINE_GPR(ebp , "fp") , { gcc_ebp , dwarf_ebp , LLDB_REGNUM_GENERIC_FP , gdb_ebp }}, + { DEFINE_GPR(esp , "sp") , { gcc_esp , dwarf_esp , LLDB_REGNUM_GENERIC_SP , gdb_esp }}, + { DEFINE_GPR(ss , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ss }}, + { DEFINE_GPR(eflags , "flags") , { gcc_eflags , dwarf_eflags , LLDB_REGNUM_GENERIC_FLAGS , gdb_eflags }}, + { DEFINE_GPR(eip , "pc") , { gcc_eip , dwarf_eip , LLDB_REGNUM_GENERIC_PC , gdb_eip }}, + { DEFINE_GPR(cs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_cs }}, + { DEFINE_GPR(ds , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ds }}, + { DEFINE_GPR(es , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_es }}, + { DEFINE_GPR(fs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fs }}, + { DEFINE_GPR(gs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gs }}, + + { DEFINE_FPU_UINT(fcw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fcw }}, + { DEFINE_FPU_UINT(fsw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fsw }}, + { DEFINE_FPU_UINT(ftw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ftw }}, + { DEFINE_FPU_UINT(fop) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fop }}, + { DEFINE_FPU_UINT(ip) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ip }}, + { DEFINE_FPU_UINT(cs) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_cs }}, + { DEFINE_FPU_UINT(dp) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_dp }}, + { DEFINE_FPU_UINT(ds) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ds }}, + { DEFINE_FPU_UINT(mxcsr) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_mxcsr }}, + { DEFINE_FPU_UINT(mxcsrmask) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }}, + { DEFINE_FPU_VECT(stmm,0) }, + { DEFINE_FPU_VECT(stmm,1) }, + { DEFINE_FPU_VECT(stmm,2) }, + { DEFINE_FPU_VECT(stmm,3) }, + { DEFINE_FPU_VECT(stmm,4) }, + { DEFINE_FPU_VECT(stmm,5) }, + { DEFINE_FPU_VECT(stmm,6) }, + { DEFINE_FPU_VECT(stmm,7) }, + { DEFINE_FPU_VECT(xmm,0) }, + { DEFINE_FPU_VECT(xmm,1) }, + { DEFINE_FPU_VECT(xmm,2) }, + { DEFINE_FPU_VECT(xmm,3) }, + { DEFINE_FPU_VECT(xmm,4) }, + { DEFINE_FPU_VECT(xmm,5) }, + { DEFINE_FPU_VECT(xmm,6) }, + { DEFINE_FPU_VECT(xmm,7) }, + + { DEFINE_EXC(trapno) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }}, + { DEFINE_EXC(err) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }}, + { DEFINE_EXC(faultvaddr) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }} +}; + +static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo)); + +void +RegisterContextMach_i386::Invalidate () +{ + InvalidateAllRegisterStates(); +} + + +size_t +RegisterContextMach_i386::GetRegisterCount () +{ + assert(k_num_register_infos == k_num_registers); + return k_num_registers; +} + +const RegisterInfo * +RegisterContextMach_i386::GetRegisterInfoAtIndex (uint32_t reg) +{ + assert(k_num_register_infos == k_num_registers); + if (reg < k_num_registers) + return &g_register_infos[reg]; + return NULL; +} + +size_t +RegisterContextMach_i386::GetRegisterInfosCount () +{ + return k_num_register_infos; +} + +const RegisterInfo * +RegisterContextMach_i386::GetRegisterInfos () +{ + return g_register_infos; +} + + +// General purpose registers +static uint32_t +g_gpr_regnums[] = +{ + gpr_eax, + gpr_ebx, + gpr_ecx, + gpr_edx, + gpr_edi, + gpr_esi, + gpr_ebp, + gpr_esp, + gpr_ss, + gpr_eflags, + gpr_eip, + gpr_cs, + gpr_ds, + gpr_es, + gpr_fs, + gpr_gs +}; + +// Floating point registers +static uint32_t +g_fpu_regnums[] = +{ + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7 +}; + +// Exception registers + +static uint32_t +g_exc_regnums[] = +{ + exc_trapno, + exc_err, + exc_faultvaddr +}; + +// Number of registers in each register set +const size_t k_num_gpr_registers = sizeof(g_gpr_regnums) / sizeof(uint32_t); +const size_t k_num_fpu_registers = sizeof(g_fpu_regnums) / sizeof(uint32_t); +const size_t k_num_exc_registers = sizeof(g_exc_regnums) / sizeof(uint32_t); + +//---------------------------------------------------------------------- +// Register set definitions. The first definitions at register set index +// of zero is for all registers, followed by other registers sets. The +// register information for the all register set need not be filled in. +//---------------------------------------------------------------------- +static const RegisterSet g_reg_sets[] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, }, + { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums }, + { "Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums } +}; + +const size_t k_num_regsets = sizeof(g_reg_sets) / sizeof(RegisterSet); + + +size_t +RegisterContextMach_i386::GetRegisterSetCount () +{ + return k_num_regsets; +} + +const RegisterSet * +RegisterContextMach_i386::GetRegisterSet (uint32_t reg_set) +{ + if (reg_set < k_num_regsets) + return &g_reg_sets[reg_set]; + return NULL; +} + + +//---------------------------------------------------------------------- +// Register information defintions for 32 bit i386. +//---------------------------------------------------------------------- +int +RegisterContextMach_i386::GetSetForNativeRegNum (int reg_num) +{ + if (reg_num < fpu_fcw) + return GPRRegSet; + else if (reg_num < exc_trapno) + return FPURegSet; + else if (reg_num < k_num_registers) + return EXCRegSet; + return -1; +} + + +void +RegisterContextMach_i386::LogGPR(Log *log, const char *title) +{ + if (log) + { + if (title) + log->Printf ("%s", title); + for (uint32_t i=0; i<k_num_gpr_registers; i++) + { + uint32_t reg = gpr_eax + i; + log->Printf("%12s = 0x%8.8x", g_register_infos[reg].name, (&gpr.eax)[reg]); + } + } +} + + + +kern_return_t +RegisterContextMach_i386::ReadGPR (bool force) +{ + int set = GPRRegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = GPRWordCount; + SetError(set, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&gpr, &count)); + LogGPR (ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD), "RegisterContextMach_i386::ReadGPR()"); + } + return GetError(set, Read); +} + +kern_return_t +RegisterContextMach_i386::ReadFPU (bool force) +{ + int set = FPURegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = FPUWordCount; + SetError(set, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&fpu, &count)); + } + return GetError(set, Read); +} + +kern_return_t +RegisterContextMach_i386::ReadEXC (bool force) +{ + int set = EXCRegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = EXCWordCount; + SetError(set, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&exc, &count)); + } + return GetError(set, Read); +} + +kern_return_t +RegisterContextMach_i386::WriteGPR () +{ + int set = GPRRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&gpr, GPRWordCount)); + SetError (set, Read, -1); + return GetError(set, Write); +} + +kern_return_t +RegisterContextMach_i386::WriteFPU () +{ + int set = FPURegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&fpu, FPUWordCount)); + SetError (set, Read, -1); + return GetError(set, Write); +} + +kern_return_t +RegisterContextMach_i386::WriteEXC () +{ + int set = EXCRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&exc, EXCWordCount)); + SetError (set, Read, -1); + return GetError(set, Write); +} + +kern_return_t +RegisterContextMach_i386::ReadRegisterSet (uint32_t set, bool force) +{ + switch (set) + { + case GPRRegSet: return ReadGPR(force); + case FPURegSet: return ReadFPU(force); + case EXCRegSet: return ReadEXC(force); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + +kern_return_t +RegisterContextMach_i386::WriteRegisterSet (uint32_t set) +{ + // Make sure we have a valid context to set. + if (RegisterSetIsCached(set)) + { + switch (set) + { + case GPRRegSet: return WriteGPR(); + case FPURegSet: return WriteFPU(); + case EXCRegSet: return WriteEXC(); + default: break; + } + } + return KERN_INVALID_ARGUMENT; +} + +bool +RegisterContextMach_i386::ReadRegisterValue (uint32_t reg, Scalar &value) +{ + int set = RegisterContextMach_i386::GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) + { + case gpr_eax: + case gpr_ebx: + case gpr_ecx: + case gpr_edx: + case gpr_edi: + case gpr_esi: + case gpr_ebp: + case gpr_esp: + case gpr_ss: + case gpr_eflags: + case gpr_eip: + case gpr_cs: + case gpr_ds: + case gpr_es: + case gpr_fs: + case gpr_gs: + value = (&gpr.eax)[reg - gpr_eax]; + break; + + case fpu_fcw: + value = fpu.fcw; + break; + + case fpu_fsw: + value = fpu.fsw; + break; + + case fpu_ftw: + value = fpu.ftw; + break; + + case fpu_fop: + value = fpu.fop; + break; + + case fpu_ip: + value = fpu.ip; + break; + + case fpu_cs: + value = fpu.cs; + break; + + case fpu_dp: + value = fpu.dp; + break; + + case fpu_ds: + value = fpu.ds; + break; + + case fpu_mxcsr: + value = fpu.mxcsr; + break; + + case fpu_mxcsrmask: + value = fpu.mxcsrmask; + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + // These values don't fit into scalar types, + // RegisterContext::ReadRegisterBytes() must be used for these + // registers + //::memcpy (reg_value.value.vector.uint8, fpu.stmm[reg - fpu_stmm0].bytes, 10); + return false; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + //::memcpy (reg_value.value.vector.uint8, fpu.xmm[reg - fpu_xmm0].bytes, 16); + return false; + + case exc_trapno: + value = exc.trapno; + break; + + case exc_err: + value = exc.err; + break; + + case exc_faultvaddr: + value = exc.faultvaddr; + break; + + default: + return false; + } + return true; +} + + +bool +RegisterContextMach_i386::WriteRegisterValue (uint32_t reg, const Scalar &value) +{ + int set = GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) + { + case gpr_eax: + case gpr_ebx: + case gpr_ecx: + case gpr_edx: + case gpr_edi: + case gpr_esi: + case gpr_ebp: + case gpr_esp: + case gpr_ss: + case gpr_eflags: + case gpr_eip: + case gpr_cs: + case gpr_ds: + case gpr_es: + case gpr_fs: + case gpr_gs: + (&gpr.eax)[reg - gpr_eax] = value.UInt(0); + break; + + case fpu_fcw: + fpu.fcw = value.UInt(0); + break; + + case fpu_fsw: + fpu.fsw = value.UInt(0); + break; + + case fpu_ftw: + fpu.ftw = value.UInt(0); + break; + + case fpu_fop: + fpu.fop = value.UInt(0); + break; + + case fpu_ip: + fpu.ip = value.UInt(0); + break; + + case fpu_cs: + fpu.cs = value.UInt(0); + break; + + case fpu_dp: + fpu.dp = value.UInt(0); + break; + + case fpu_ds: + fpu.ds = value.UInt(0); + break; + + case fpu_mxcsr: + fpu.mxcsr = value.UInt(0); + break; + + case fpu_mxcsrmask: + fpu.mxcsrmask = value.UInt(0); + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + //::memcpy (fpu.stmm[reg - fpu_stmm0].bytes, reg_value.value.vector.uint8, 10); + return false; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + //::memcpy (fpu.xmm[reg - fpu_xmm0].bytes, reg_value.value.vector.uint8, 16); + return false; + + case exc_trapno: + exc.trapno = value.UInt(0); + break; + + case exc_err: + exc.err = value.UInt(0); + break; + + case exc_faultvaddr: + exc.faultvaddr = value.UInt(0); + break; + + default: + return false; + } + return WriteRegisterSet(set) == KERN_SUCCESS; +} + +bool +RegisterContextMach_i386::ReadRegisterBytes (uint32_t reg, DataExtractor &data) +{ + int set = RegisterContextMach_i386::GetSetForNativeRegNum (reg); + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info == NULL) + return false; + + switch (reg) + { + case gpr_eax: + case gpr_ebx: + case gpr_ecx: + case gpr_edx: + case gpr_edi: + case gpr_esi: + case gpr_ebp: + case gpr_esp: + case gpr_ss: + case gpr_eflags: + case gpr_eip: + case gpr_cs: + case gpr_ds: + case gpr_es: + case gpr_fs: + case gpr_gs: + data.SetData(&gpr.eax + reg - gpr_eax, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_fcw: + data.SetData(&fpu.fcw, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_fsw: + data.SetData(&fpu.fsw, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_ftw: + data.SetData(&fpu.ftw, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_fop: + data.SetData(&fpu.fop, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_ip: + data.SetData(&fpu.ip, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_cs: + data.SetData(&fpu.cs, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_dp: + data.SetData(&fpu.dp, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_ds: + data.SetData(&fpu.ds, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_mxcsr: + data.SetData(&fpu.mxcsr, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_mxcsrmask: + data.SetData(&fpu.mxcsrmask, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + data.SetData(fpu.stmm[reg - fpu_stmm0].bytes, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + data.SetData(fpu.xmm[reg - fpu_xmm0].bytes, reg_info->byte_size, eByteOrderHost); + break; + + case exc_trapno: + data.SetData(&exc.trapno, reg_info->byte_size, eByteOrderHost); + break; + + case exc_err: + data.SetData(&exc.err, reg_info->byte_size, eByteOrderHost); + break; + + case exc_faultvaddr: + data.SetData(&exc.faultvaddr, reg_info->byte_size, eByteOrderHost); + break; + + default: + return false; + } + return true; +} + +bool +RegisterContextMach_i386::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset) +{ + int set = GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + + const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info == NULL && data.ValidOffsetForDataOfSize(data_offset, reg_info->byte_size)) + return false; + + uint32_t offset = data_offset; + switch (reg) + { + case gpr_eax: + case gpr_ebx: + case gpr_ecx: + case gpr_edx: + case gpr_edi: + case gpr_esi: + case gpr_ebp: + case gpr_esp: + case gpr_ss: + case gpr_eflags: + case gpr_eip: + case gpr_cs: + case gpr_ds: + case gpr_es: + case gpr_fs: + case gpr_gs: + (&gpr.eax)[reg - gpr_eax] = data.GetU32 (&offset); + break; + + case fpu_fcw: + fpu.fcw = data.GetU16(&offset); + break; + + case fpu_fsw: + fpu.fsw = data.GetU16(&offset); + break; + + case fpu_ftw: + fpu.ftw = data.GetU8(&offset); + break; + + case fpu_fop: + fpu.fop = data.GetU16(&offset); + break; + + case fpu_ip: + fpu.ip = data.GetU32(&offset); + break; + + case fpu_cs: + fpu.cs = data.GetU16(&offset); + break; + + case fpu_dp: + fpu.dp = data.GetU32(&offset); + break; + + case fpu_ds: + fpu.ds = data.GetU16(&offset); + break; + + case fpu_mxcsr: + fpu.mxcsr = data.GetU32(&offset); + break; + + case fpu_mxcsrmask: + fpu.mxcsrmask = data.GetU32(&offset); + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + ::memcpy (fpu.stmm[reg - fpu_stmm0].bytes, data.PeekData(offset, reg_info->byte_size), reg_info->byte_size); + return false; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + ::memcpy (fpu.xmm[reg - fpu_xmm0].bytes, data.PeekData(offset, reg_info->byte_size), reg_info->byte_size); + return false; + + case exc_trapno: + exc.trapno = data.GetU32 (&offset); + break; + + case exc_err: + exc.err = data.GetU32 (&offset); + break; + + case exc_faultvaddr: + exc.faultvaddr = data.GetU32 (&offset); + break; + + default: + return false; + } + return WriteRegisterSet(set) == KERN_SUCCESS; +} + +bool +RegisterContextMach_i386::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (data_sp && + ReadGPR (false) == KERN_SUCCESS && + ReadFPU (false) == KERN_SUCCESS && + ReadEXC (false) == KERN_SUCCESS) + { + uint8_t *dst = data_sp->GetBytes(); + ::memcpy (dst, &gpr, sizeof(gpr)); + dst += sizeof(gpr); + + ::memcpy (dst, &fpu, sizeof(fpu)); + dst += sizeof(gpr); + + ::memcpy (dst, &exc, sizeof(exc)); + return true; + } + return false; +} + +bool +RegisterContextMach_i386::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) + { + const uint8_t *src = data_sp->GetBytes(); + ::memcpy (&gpr, src, sizeof(gpr)); + src += sizeof(gpr); + + ::memcpy (&fpu, src, sizeof(fpu)); + src += sizeof(gpr); + + ::memcpy (&exc, src, sizeof(exc)); + uint32_t success_count = 0; + if (WriteGPR() == KERN_SUCCESS) + ++success_count; + if (WriteFPU() == KERN_SUCCESS) + ++success_count; + if (WriteEXC() == KERN_SUCCESS) + ++success_count; + return success_count == 3; + } + return false; +} + + +uint32_t +RegisterContextMach_i386::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t reg) +{ + if (kind == eRegisterKindGeneric) + { + switch (reg) + { + case LLDB_REGNUM_GENERIC_PC: return gpr_eip; + case LLDB_REGNUM_GENERIC_SP: return gpr_esp; + case LLDB_REGNUM_GENERIC_FP: return gpr_ebp; + case LLDB_REGNUM_GENERIC_FLAGS: return gpr_eflags; + case LLDB_REGNUM_GENERIC_RA: + default: + break; + } + } + else if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF) + { + switch (reg) + { + case dwarf_eax: return gpr_eax; + case dwarf_ecx: return gpr_ecx; + case dwarf_edx: return gpr_edx; + case dwarf_ebx: return gpr_ebx; + case dwarf_esp: return gpr_esp; + case dwarf_ebp: return gpr_ebp; + case dwarf_esi: return gpr_esi; + case dwarf_edi: return gpr_edi; + case dwarf_eip: return gpr_eip; + case dwarf_eflags: return gpr_eflags; + case dwarf_stmm0: return fpu_stmm0; + case dwarf_stmm1: return fpu_stmm1; + case dwarf_stmm2: return fpu_stmm2; + case dwarf_stmm3: return fpu_stmm3; + case dwarf_stmm4: return fpu_stmm4; + case dwarf_stmm5: return fpu_stmm5; + case dwarf_stmm6: return fpu_stmm6; + case dwarf_stmm7: return fpu_stmm7; + case dwarf_xmm0: return fpu_xmm0; + case dwarf_xmm1: return fpu_xmm1; + case dwarf_xmm2: return fpu_xmm2; + case dwarf_xmm3: return fpu_xmm3; + case dwarf_xmm4: return fpu_xmm4; + case dwarf_xmm5: return fpu_xmm5; + case dwarf_xmm6: return fpu_xmm6; + case dwarf_xmm7: return fpu_xmm7; + default: + break; + } + } + else if (kind == eRegisterKindGDB) + { + switch (reg) + { + case gdb_eax : return gpr_eax; + case gdb_ebx : return gpr_ebx; + case gdb_ecx : return gpr_ecx; + case gdb_edx : return gpr_edx; + case gdb_esi : return gpr_esi; + case gdb_edi : return gpr_edi; + case gdb_ebp : return gpr_ebp; + case gdb_esp : return gpr_esp; + case gdb_eip : return gpr_eip; + case gdb_eflags : return gpr_eflags; + case gdb_cs : return gpr_cs; + case gdb_ss : return gpr_ss; + case gdb_ds : return gpr_ds; + case gdb_es : return gpr_es; + case gdb_fs : return gpr_fs; + case gdb_gs : return gpr_gs; + case gdb_stmm0 : return fpu_stmm0; + case gdb_stmm1 : return fpu_stmm1; + case gdb_stmm2 : return fpu_stmm2; + case gdb_stmm3 : return fpu_stmm3; + case gdb_stmm4 : return fpu_stmm4; + case gdb_stmm5 : return fpu_stmm5; + case gdb_stmm6 : return fpu_stmm6; + case gdb_stmm7 : return fpu_stmm7; + case gdb_fctrl : return fpu_fctrl; + case gdb_fstat : return fpu_fstat; + case gdb_ftag : return fpu_ftag; + case gdb_fiseg : return fpu_fiseg; + case gdb_fioff : return fpu_fioff; + case gdb_foseg : return fpu_foseg; + case gdb_fooff : return fpu_fooff; + case gdb_fop : return fpu_fop; + case gdb_xmm0 : return fpu_xmm0; + case gdb_xmm1 : return fpu_xmm1; + case gdb_xmm2 : return fpu_xmm2; + case gdb_xmm3 : return fpu_xmm3; + case gdb_xmm4 : return fpu_xmm4; + case gdb_xmm5 : return fpu_xmm5; + case gdb_xmm6 : return fpu_xmm6; + case gdb_xmm7 : return fpu_xmm7; + case gdb_mxcsr : return fpu_mxcsr; + default: + break; + } + } + return LLDB_INVALID_REGNUM; +} + + +bool +RegisterContextMach_i386::HardwareSingleStep (bool enable) +{ + if (ReadGPR(false) != KERN_SUCCESS) + return false; + + const uint32_t trace_bit = 0x100u; + if (enable) + { + // If the trace bit is already set, there is nothing to do + if (gpr.eflags & trace_bit) + return true; + else + gpr.eflags |= trace_bit; + } + else + { + // If the trace bit is already cleared, there is nothing to do + if (gpr.eflags & trace_bit) + gpr.eflags &= ~trace_bit; + else + return true; + } + + return WriteGPR() == KERN_SUCCESS; +} + + + diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.h b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.h new file mode 100644 index 00000000000..580186752a9 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.h @@ -0,0 +1,256 @@ +//===-- RegisterContextMach_i386.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_RegisterContextMach_i386_h_ +#define liblldb_RegisterContextMach_i386_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" + +class RegisterContextMach_i386 : public lldb_private::RegisterContext +{ +public: + + RegisterContextMach_i386(lldb_private::Thread &thread, + lldb_private::StackFrame *frame); + + virtual + ~RegisterContextMach_i386(); + + 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 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 = 0); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + + virtual bool + HardwareSingleStep (bool enable); + + struct GPR + { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t ss; + uint32_t eflags; + uint32_t eip; + uint32_t cs; + uint32_t ds; + uint32_t es; + uint32_t fs; + uint32_t gs; + }; + + struct MMSReg + { + uint8_t bytes[10]; + uint8_t pad[6]; + }; + + struct XMMReg + { + uint8_t bytes[16]; + }; + + struct FPU + { + uint32_t pad[2]; + uint16_t fcw; + uint16_t fsw; + uint8_t ftw; + uint8_t pad1; + uint16_t fop; + uint32_t ip; + uint16_t cs; + uint16_t pad2; + uint32_t dp; + uint16_t ds; + uint16_t pad3; + uint32_t mxcsr; + uint32_t mxcsrmask; + MMSReg stmm[8]; + XMMReg xmm[8]; + uint8_t pad4[14*16]; + int pad5; + }; + + struct EXC + { + uint32_t trapno; + uint32_t err; + uint32_t faultvaddr; + }; + +protected: + + enum + { + GPRRegSet = 1, + FPURegSet = 2, + EXCRegSet = 3 + }; + + enum + { + GPRWordCount = sizeof(GPR)/sizeof(uint32_t), + FPUWordCount = sizeof(FPU)/sizeof(uint32_t), + EXCWordCount = sizeof(EXC)/sizeof(uint32_t) + }; + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + GPR gpr; + FPU fpu; + EXC exc; + kern_return_t gpr_errs[2]; // Read/Write errors + kern_return_t fpu_errs[2]; // Read/Write errors + kern_return_t exc_errs[2]; // Read/Write errors + + void + InvalidateAllRegisterStates() + { + SetError (GPRRegSet, Read, -1); + SetError (FPURegSet, Read, -1); + SetError (EXCRegSet, Read, -1); + } + + kern_return_t + GetError (int flavor, uint32_t err_idx) const + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + // When getting all errors, just OR all values together to see if + // we got any kind of error. + case GPRRegSet: return gpr_errs[err_idx]; + case FPURegSet: return fpu_errs[err_idx]; + case EXCRegSet: return exc_errs[err_idx]; + default: break; + } + } + return -1; + } + + bool + SetError (int flavor, uint32_t err_idx, kern_return_t err) + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + case GPRRegSet: + gpr_errs[err_idx] = err; + return true; + + case FPURegSet: + fpu_errs[err_idx] = err; + return true; + + case EXCRegSet: + exc_errs[err_idx] = err; + return true; + + default: break; + } + } + return false; + } + + bool + RegisterSetIsCached (int set) const + { + return GetError(set, Read) == KERN_SUCCESS; + } + + void + LogGPR (lldb_private::Log *log, const char *title); + + kern_return_t + ReadGPR (bool force); + + kern_return_t + ReadFPU (bool force); + + kern_return_t + ReadEXC (bool force); + + kern_return_t + WriteGPR (); + + kern_return_t + WriteFPU (); + + kern_return_t + WriteEXC (); + + kern_return_t + ReadRegisterSet (uint32_t set, bool force); + + kern_return_t + WriteRegisterSet (uint32_t set); + + static uint32_t + GetRegisterNumber (uint32_t reg_kind, uint32_t reg_num); + + static int + GetSetForNativeRegNum (int reg_num); + + static size_t + GetRegisterInfosCount (); + + static const lldb::RegisterInfo * + GetRegisterInfos (); +}; + +#endif // liblldb_RegisterContextMach_i386_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.cpp new file mode 100644 index 00000000000..a7ed32e649a --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.cpp @@ -0,0 +1,1328 @@ +//===-- RegisterContextMach_x86_64.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 +#include <mach/thread_act.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Scalar.h" + +// Project includes +#include "RegisterContextMach_x86_64.h" +#include "ProcessMacOSXLog.h" + +using namespace lldb; +using namespace lldb_private; + +enum +{ + gpr_rax = 0, + gpr_rbx, + gpr_rcx, + gpr_rdx, + gpr_rdi, + gpr_rsi, + gpr_rbp, + gpr_rsp, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, + gpr_r14, + gpr_r15, + gpr_rip, + gpr_rflags, + gpr_cs, + gpr_fs, + gpr_gs, + + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + fpu_xmm8, + fpu_xmm9, + fpu_xmm10, + fpu_xmm11, + fpu_xmm12, + fpu_xmm13, + fpu_xmm14, + fpu_xmm15, + + exc_trapno, + exc_err, + exc_faultvaddr, + + k_num_registers, + + // Aliases + fpu_fctrl = fpu_fcw, + fpu_fstat = fpu_fsw, + fpu_ftag = fpu_ftw, + fpu_fiseg = fpu_cs, + fpu_fioff = fpu_ip, + fpu_foseg = fpu_ds, + fpu_fooff = fpu_dp, +}; + +enum gcc_dwarf_regnums +{ + gcc_dwarf_gpr_rax = 0, + gcc_dwarf_gpr_rdx, + gcc_dwarf_gpr_rcx, + gcc_dwarf_gpr_rbx, + gcc_dwarf_gpr_rsi, + gcc_dwarf_gpr_rdi, + gcc_dwarf_gpr_rbp, + gcc_dwarf_gpr_rsp, + gcc_dwarf_gpr_r8, + gcc_dwarf_gpr_r9, + gcc_dwarf_gpr_r10, + gcc_dwarf_gpr_r11, + gcc_dwarf_gpr_r12, + gcc_dwarf_gpr_r13, + gcc_dwarf_gpr_r14, + gcc_dwarf_gpr_r15, + gcc_dwarf_gpr_rip, + gcc_dwarf_fpu_xmm0, + gcc_dwarf_fpu_xmm1, + gcc_dwarf_fpu_xmm2, + gcc_dwarf_fpu_xmm3, + gcc_dwarf_fpu_xmm4, + gcc_dwarf_fpu_xmm5, + gcc_dwarf_fpu_xmm6, + gcc_dwarf_fpu_xmm7, + gcc_dwarf_fpu_xmm8, + gcc_dwarf_fpu_xmm9, + gcc_dwarf_fpu_xmm10, + gcc_dwarf_fpu_xmm11, + gcc_dwarf_fpu_xmm12, + gcc_dwarf_fpu_xmm13, + gcc_dwarf_fpu_xmm14, + gcc_dwarf_fpu_xmm15, + gcc_dwarf_fpu_stmm0, + gcc_dwarf_fpu_stmm1, + gcc_dwarf_fpu_stmm2, + gcc_dwarf_fpu_stmm3, + gcc_dwarf_fpu_stmm4, + gcc_dwarf_fpu_stmm5, + gcc_dwarf_fpu_stmm6, + gcc_dwarf_fpu_stmm7, + +}; + +enum gdb_regnums +{ + gdb_gpr_rax = 0, + gdb_gpr_rbx = 1, + gdb_gpr_rcx = 2, + gdb_gpr_rdx = 3, + gdb_gpr_rsi = 4, + gdb_gpr_rdi = 5, + gdb_gpr_rbp = 6, + gdb_gpr_rsp = 7, + gdb_gpr_r8 = 8, + gdb_gpr_r9 = 9, + gdb_gpr_r10 = 10, + gdb_gpr_r11 = 11, + gdb_gpr_r12 = 12, + gdb_gpr_r13 = 13, + gdb_gpr_r14 = 14, + gdb_gpr_r15 = 15, + gdb_gpr_rip = 16, + gdb_gpr_rflags = 17, + gdb_gpr_cs = 18, + gdb_gpr_ss = 19, + gdb_gpr_ds = 20, + gdb_gpr_es = 21, + gdb_gpr_fs = 22, + gdb_gpr_gs = 23, + gdb_fpu_stmm0 = 24, + gdb_fpu_stmm1 = 25, + gdb_fpu_stmm2 = 26, + gdb_fpu_stmm3 = 27, + gdb_fpu_stmm4 = 28, + gdb_fpu_stmm5 = 29, + gdb_fpu_stmm6 = 30, + gdb_fpu_stmm7 = 31, + gdb_fpu_fctrl = 32, gdb_fpu_fcw = gdb_fpu_fctrl, + gdb_fpu_fstat = 33, gdb_fpu_fsw = gdb_fpu_fstat, + gdb_fpu_ftag = 34, gdb_fpu_ftw = gdb_fpu_ftag, + gdb_fpu_fiseg = 35, gdb_fpu_cs = gdb_fpu_fiseg, + gdb_fpu_fioff = 36, gdb_fpu_ip = gdb_fpu_fioff, + gdb_fpu_foseg = 37, gdb_fpu_ds = gdb_fpu_foseg, + gdb_fpu_fooff = 38, gdb_fpu_dp = gdb_fpu_fooff, + gdb_fpu_fop = 39, + gdb_fpu_xmm0 = 40, + gdb_fpu_xmm1 = 41, + gdb_fpu_xmm2 = 42, + gdb_fpu_xmm3 = 43, + gdb_fpu_xmm4 = 44, + gdb_fpu_xmm5 = 45, + gdb_fpu_xmm6 = 46, + gdb_fpu_xmm7 = 47, + gdb_fpu_xmm8 = 48, + gdb_fpu_xmm9 = 49, + gdb_fpu_xmm10 = 50, + gdb_fpu_xmm11 = 51, + gdb_fpu_xmm12 = 52, + gdb_fpu_xmm13 = 53, + gdb_fpu_xmm14 = 54, + gdb_fpu_xmm15 = 55, + gdb_fpu_mxcsr = 56, +}; + +RegisterContextMach_x86_64::RegisterContextMach_x86_64 (Thread &thread, StackFrame *frame) : + RegisterContext (thread, frame), + gpr(), + fpu(), + exc() +{ + uint32_t i; + for (i=0; i<kNumErrors; i++) + { + gpr_errs[i] = -1; + fpu_errs[i] = -1; + exc_errs[i] = -1; + } +} + +RegisterContextMach_x86_64::~RegisterContextMach_x86_64() +{ +} + +#define GPR_OFFSET(reg) (offsetof (RegisterContextMach_x86_64::GPR, reg)) +#define FPU_OFFSET(reg) (offsetof (RegisterContextMach_x86_64::FPU, reg) + sizeof (RegisterContextMach_x86_64::GPR)) +#define EXC_OFFSET(reg) (offsetof (RegisterContextMach_x86_64::EXC, reg) + sizeof (RegisterContextMach_x86_64::GPR) + sizeof (RegisterContextMach_x86_64::FPU)) + +// These macros will auto define the register name, alt name, register size, +// register offset, encoding, format and native register. This ensures that +// the register state structures are defined correctly and have the correct +// sizes and offsets. +#define DEFINE_GPR(reg, alt) #reg, alt, sizeof(((RegisterContextMach_x86_64::GPR *)NULL)->reg), GPR_OFFSET(reg), eEncodingUint, eFormatHex, gpr_##reg +#define DEFINE_FPU_UINT(reg) #reg, NULL, sizeof(((RegisterContextMach_x86_64::FPU *)NULL)->reg), FPU_OFFSET(reg), eEncodingUint, eFormatHex, fpu_##reg +#define DEFINE_FPU_VECT(reg, i) #reg#i, NULL, sizeof(((RegisterContextMach_x86_64::FPU *)NULL)->reg[i].bytes), FPU_OFFSET(reg[i]), eEncodingVector, eFormatVectorOfUInt8, fpu_##reg##i, { gcc_dwarf_fpu_##reg##i, gcc_dwarf_fpu_##reg##i, LLDB_INVALID_REGNUM, gdb_fpu_##reg##i } +#define DEFINE_EXC(reg) #reg, NULL, sizeof(((RegisterContextMach_x86_64::EXC *)NULL)->reg), EXC_OFFSET(reg), eEncodingUint, eFormatHex, exc_##reg + +#define REG_CONTEXT_SIZE (sizeof (RegisterContextMach_x86_64::GPR) + sizeof (RegisterContextMach_x86_64::FPU) + sizeof (RegisterContextMach_x86_64::EXC)) + +// General purpose registers for 64 bit +static RegisterInfo g_register_infos[] = +{ +// Macro auto defines most stuff GCC REG KIND NUM DWARF REG KIND NUM GENERIC REG KIND NUM GDB REG KIND NUM +// =============================== ======================= =================== ========================== ========================== + { DEFINE_GPR (rax , NULL) , { gcc_dwarf_gpr_rax , gcc_dwarf_gpr_rax , LLDB_INVALID_REGNUM , gdb_gpr_rax }}, + { DEFINE_GPR (rbx , NULL) , { gcc_dwarf_gpr_rbx , gcc_dwarf_gpr_rbx , LLDB_INVALID_REGNUM , gdb_gpr_rbx }}, + { DEFINE_GPR (rcx , NULL) , { gcc_dwarf_gpr_rcx , gcc_dwarf_gpr_rcx , LLDB_INVALID_REGNUM , gdb_gpr_rcx }}, + { DEFINE_GPR (rdx , NULL) , { gcc_dwarf_gpr_rdx , gcc_dwarf_gpr_rdx , LLDB_INVALID_REGNUM , gdb_gpr_rdx }}, + { DEFINE_GPR (rdi , NULL) , { gcc_dwarf_gpr_rdi , gcc_dwarf_gpr_rdi , LLDB_INVALID_REGNUM , gdb_gpr_rdi }}, + { DEFINE_GPR (rsi , NULL) , { gcc_dwarf_gpr_rsi , gcc_dwarf_gpr_rsi , LLDB_INVALID_REGNUM , gdb_gpr_rsi }}, + { DEFINE_GPR (rbp , "fp") , { gcc_dwarf_gpr_rbp , gcc_dwarf_gpr_rbp , LLDB_REGNUM_GENERIC_FP , gdb_gpr_rbp }}, + { DEFINE_GPR (rsp , "sp") , { gcc_dwarf_gpr_rsp , gcc_dwarf_gpr_rsp , LLDB_REGNUM_GENERIC_SP , gdb_gpr_rsp }}, + { DEFINE_GPR (r8 , NULL) , { gcc_dwarf_gpr_r8 , gcc_dwarf_gpr_r8 , LLDB_INVALID_REGNUM , gdb_gpr_r8 }}, + { DEFINE_GPR (r9 , NULL) , { gcc_dwarf_gpr_r9 , gcc_dwarf_gpr_r9 , LLDB_INVALID_REGNUM , gdb_gpr_r9 }}, + { DEFINE_GPR (r10 , NULL) , { gcc_dwarf_gpr_r10 , gcc_dwarf_gpr_r10 , LLDB_INVALID_REGNUM , gdb_gpr_r10 }}, + { DEFINE_GPR (r11 , NULL) , { gcc_dwarf_gpr_r11 , gcc_dwarf_gpr_r11 , LLDB_INVALID_REGNUM , gdb_gpr_r11 }}, + { DEFINE_GPR (r12 , NULL) , { gcc_dwarf_gpr_r12 , gcc_dwarf_gpr_r12 , LLDB_INVALID_REGNUM , gdb_gpr_r12 }}, + { DEFINE_GPR (r13 , NULL) , { gcc_dwarf_gpr_r13 , gcc_dwarf_gpr_r13 , LLDB_INVALID_REGNUM , gdb_gpr_r13 }}, + { DEFINE_GPR (r14 , NULL) , { gcc_dwarf_gpr_r14 , gcc_dwarf_gpr_r14 , LLDB_INVALID_REGNUM , gdb_gpr_r14 }}, + { DEFINE_GPR (r15 , NULL) , { gcc_dwarf_gpr_r15 , gcc_dwarf_gpr_r15 , LLDB_INVALID_REGNUM , gdb_gpr_r15 }}, + { DEFINE_GPR (rip , "pc") , { gcc_dwarf_gpr_rip , gcc_dwarf_gpr_rip , LLDB_REGNUM_GENERIC_PC , gdb_gpr_rip }}, + { DEFINE_GPR (rflags, "flags") , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_REGNUM_GENERIC_FLAGS , gdb_gpr_rflags}}, + { DEFINE_GPR (cs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gpr_cs }}, + { DEFINE_GPR (fs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gpr_fs }}, + { DEFINE_GPR (gs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gpr_gs }}, + + { DEFINE_FPU_UINT(fcw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_fcw }}, + { DEFINE_FPU_UINT(fsw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_fsw }}, + { DEFINE_FPU_UINT(ftw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_ftw }}, + { DEFINE_FPU_UINT(fop) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_fop }}, + { DEFINE_FPU_UINT(ip) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_ip }}, + { DEFINE_FPU_UINT(cs) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_cs }}, + { DEFINE_FPU_UINT(dp) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_dp }}, + { DEFINE_FPU_UINT(ds) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_ds }}, + { DEFINE_FPU_UINT(mxcsr) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_mxcsr }}, + { DEFINE_FPU_UINT(mxcsrmask) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }}, + { DEFINE_FPU_VECT(stmm,0) }, + { DEFINE_FPU_VECT(stmm,1) }, + { DEFINE_FPU_VECT(stmm,2) }, + { DEFINE_FPU_VECT(stmm,3) }, + { DEFINE_FPU_VECT(stmm,4) }, + { DEFINE_FPU_VECT(stmm,5) }, + { DEFINE_FPU_VECT(stmm,6) }, + { DEFINE_FPU_VECT(stmm,7) }, + { DEFINE_FPU_VECT(xmm,0) }, + { DEFINE_FPU_VECT(xmm,1) }, + { DEFINE_FPU_VECT(xmm,2) }, + { DEFINE_FPU_VECT(xmm,3) }, + { DEFINE_FPU_VECT(xmm,4) }, + { DEFINE_FPU_VECT(xmm,5) }, + { DEFINE_FPU_VECT(xmm,6) }, + { DEFINE_FPU_VECT(xmm,7) }, + { DEFINE_FPU_VECT(xmm,8) }, + { DEFINE_FPU_VECT(xmm,9) }, + { DEFINE_FPU_VECT(xmm,10) }, + { DEFINE_FPU_VECT(xmm,11) }, + { DEFINE_FPU_VECT(xmm,12) }, + { DEFINE_FPU_VECT(xmm,13) }, + { DEFINE_FPU_VECT(xmm,14) }, + { DEFINE_FPU_VECT(xmm,15) }, + + { DEFINE_EXC(trapno) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }}, + { DEFINE_EXC(err) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }}, + { DEFINE_EXC(faultvaddr) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }} +}; + +static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo)); + + +void +RegisterContextMach_x86_64::Invalidate () +{ + InvalidateAllRegisterStates(); +} + + +size_t +RegisterContextMach_x86_64::GetRegisterCount () +{ + assert(k_num_register_infos == k_num_registers); + return k_num_registers; +} + + +const RegisterInfo * +RegisterContextMach_x86_64::GetRegisterInfoAtIndex (uint32_t reg) +{ + assert(k_num_register_infos == k_num_registers); + if (reg < k_num_registers) + return &g_register_infos[reg]; + return NULL; +} + + +size_t +RegisterContextMach_x86_64::GetRegisterInfosCount () +{ + return k_num_register_infos; +} + +const RegisterInfo * +RegisterContextMach_x86_64::GetRegisterInfos () +{ + return g_register_infos; +} + + + +static uint32_t g_gpr_regnums[] = +{ + gpr_rax, + gpr_rbx, + gpr_rcx, + gpr_rdx, + gpr_rdi, + gpr_rsi, + gpr_rbp, + gpr_rsp, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, + gpr_r14, + gpr_r15, + gpr_rip, + gpr_rflags, + gpr_cs, + gpr_fs, + gpr_gs +}; + +static uint32_t g_fpu_regnums[] = +{ + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + fpu_xmm8, + fpu_xmm9, + fpu_xmm10, + fpu_xmm11, + fpu_xmm12, + fpu_xmm13, + fpu_xmm14, + fpu_xmm15 +}; + +static uint32_t +g_exc_regnums[] = +{ + exc_trapno, + exc_err, + exc_faultvaddr +}; + +// Number of registers in each register set +const size_t k_num_gpr_registers = sizeof(g_gpr_regnums) / sizeof(uint32_t); +const size_t k_num_fpu_registers = sizeof(g_fpu_regnums) / sizeof(uint32_t); +const size_t k_num_exc_registers = sizeof(g_exc_regnums) / sizeof(uint32_t); + +//---------------------------------------------------------------------- +// Register set definitions. The first definitions at register set index +// of zero is for all registers, followed by other registers sets. The +// register information for the all register set need not be filled in. +//---------------------------------------------------------------------- +static const RegisterSet g_reg_sets[] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, }, + { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums }, + { "Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums } +}; + +const size_t k_num_regsets = sizeof(g_reg_sets) / sizeof(RegisterSet); + + +size_t +RegisterContextMach_x86_64::GetRegisterSetCount () +{ + return k_num_regsets; +} + +const RegisterSet * +RegisterContextMach_x86_64::GetRegisterSet (uint32_t reg_set) +{ + if (reg_set < k_num_regsets) + return &g_reg_sets[reg_set]; + return NULL; +} + +int +RegisterContextMach_x86_64::GetSetForNativeRegNum (int reg_num) +{ + if (reg_num < fpu_fcw) + return GPRRegSet; + else if (reg_num < exc_trapno) + return FPURegSet; + else if (reg_num < k_num_registers) + return EXCRegSet; + return -1; +} + +void +RegisterContextMach_x86_64::LogGPR(Log *log, const char *format, ...) +{ + if (log) + { + if (format) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } + for (uint32_t i=0; i<k_num_gpr_registers; i++) + { + uint32_t reg = gpr_rax + i; + log->Printf("%12s = 0x%16.16llx", g_register_infos[reg].name, (&gpr.rax)[reg]); + } + } +} + +kern_return_t +RegisterContextMach_x86_64::ReadGPR (bool force) +{ + int set = GPRRegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = GPRWordCount; + SetError(GPRRegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&gpr, &count)); + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD); + if (log) + LogGPR (log, "RegisterContextMach_x86_64::ReadGPR(thread = 0x%4.4x)", GetThreadID()); + } + return GetError(GPRRegSet, Read); +} + +kern_return_t +RegisterContextMach_x86_64::ReadFPU (bool force) +{ + int set = FPURegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = FPUWordCount; + SetError(FPURegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&fpu, &count)); + } + return GetError(FPURegSet, Read); +} + +kern_return_t +RegisterContextMach_x86_64::ReadEXC (bool force) +{ + int set = EXCRegSet; + if (force || !RegisterSetIsCached(set)) + { + mach_msg_type_number_t count = EXCWordCount; + SetError(EXCRegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&exc, &count)); + } + return GetError(EXCRegSet, Read); +} + +kern_return_t +RegisterContextMach_x86_64::WriteGPR () +{ + int set = GPRRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD); + if (log) + LogGPR (log, "RegisterContextMach_x86_64::WriteGPR (thread = 0x%4.4x)", GetThreadID()); + SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&gpr, GPRWordCount)); + SetError (set, Read, -1); + return GetError (set, Write); +} + +kern_return_t +RegisterContextMach_x86_64::WriteFPU () +{ + int set = FPURegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&fpu, FPUWordCount)); + SetError (set, Read, -1); + return GetError (set, Write); +} + +kern_return_t +RegisterContextMach_x86_64::WriteEXC () +{ + int set = EXCRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&exc, EXCWordCount)); + SetError (set, Read, -1); + return GetError (set, Write); +} + +kern_return_t +RegisterContextMach_x86_64::ReadRegisterSet(uint32_t set, bool force) +{ + switch (set) + { + case GPRRegSet: return ReadGPR (force); + case FPURegSet: return ReadFPU (force); + case EXCRegSet: return ReadEXC (force); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + +kern_return_t +RegisterContextMach_x86_64::WriteRegisterSet(uint32_t set) +{ + // Make sure we have a valid context to set. + switch (set) + { + case GPRRegSet: return WriteGPR (); + case FPURegSet: return WriteFPU (); + case EXCRegSet: return WriteEXC (); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + + +bool +RegisterContextMach_x86_64::ReadRegisterValue (uint32_t reg, Scalar &value) +{ + int set = RegisterContextMach_x86_64::GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) + { + case gpr_rax: + case gpr_rbx: + case gpr_rcx: + case gpr_rdx: + case gpr_rdi: + case gpr_rsi: + case gpr_rbp: + case gpr_rsp: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_r13: + case gpr_r14: + case gpr_r15: + case gpr_rip: + case gpr_rflags: + case gpr_cs: + case gpr_fs: + case gpr_gs: + value = (&gpr.rax)[reg - gpr_rax]; + break; + + case fpu_fcw: + value = fpu.fcw; + break; + + case fpu_fsw: + value = fpu.fsw; + break; + + case fpu_ftw: + value = fpu.ftw; + break; + + case fpu_fop: + value = fpu.fop; + break; + + case fpu_ip: + value = fpu.ip; + break; + + case fpu_cs: + value = fpu.cs; + break; + + case fpu_dp: + value = fpu.dp; + break; + + case fpu_ds: + value = fpu.ds; + break; + + case fpu_mxcsr: + value = fpu.mxcsr; + break; + + case fpu_mxcsrmask: + value = fpu.mxcsrmask; + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + // These values don't fit into scalar types, + // RegisterContext::ReadRegisterBytes() must be used for these + // registers + //::memcpy (reg_value.value.vector.uint8, fpu.stmm[reg - fpu_stmm0].bytes, 10); + return false; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + case fpu_xmm8: + case fpu_xmm9: + case fpu_xmm10: + case fpu_xmm11: + case fpu_xmm12: + case fpu_xmm13: + case fpu_xmm14: + case fpu_xmm15: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + //::memcpy (reg_value.value.vector.uint8, fpu.xmm[reg - fpu_xmm0].bytes, 16); + return false; + + case exc_trapno: + value = exc.trapno; + break; + + case exc_err: + value = exc.err; + break; + + case exc_faultvaddr: + value = exc.faultvaddr; + break; + + default: + return false; + } + return true; +} + + +bool +RegisterContextMach_x86_64::WriteRegisterValue (uint32_t reg, const Scalar &value) +{ + int set = RegisterContextMach_x86_64::GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) + { + case gpr_rax: + case gpr_rbx: + case gpr_rcx: + case gpr_rdx: + case gpr_rdi: + case gpr_rsi: + case gpr_rbp: + case gpr_rsp: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_r13: + case gpr_r14: + case gpr_r15: + case gpr_rip: + case gpr_rflags: + case gpr_cs: + case gpr_fs: + case gpr_gs: + (&gpr.rax)[reg - gpr_rax] = value.ULongLong(0); + break; + + case fpu_fcw: + fpu.fcw = value.UInt(0); + break; + + case fpu_fsw: + fpu.fsw = value.UInt(0); + break; + + case fpu_ftw: + fpu.ftw = value.UInt(0); + break; + + case fpu_fop: + fpu.fop = value.UInt(0); + break; + + case fpu_ip: + fpu.ip = value.UInt(0); + break; + + case fpu_cs: + fpu.cs = value.UInt(0); + break; + + case fpu_dp: + fpu.dp = value.UInt(0); + break; + + case fpu_ds: + fpu.ds = value.UInt(0); + break; + + case fpu_mxcsr: + fpu.mxcsr = value.UInt(0); + break; + + case fpu_mxcsrmask: + fpu.mxcsrmask = value.UInt(0); + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + //::memcpy (fpu.stmm[reg - fpu_stmm0].bytes, reg_value.value.vector.uint8, 10); + return false; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + case fpu_xmm8: + case fpu_xmm9: + case fpu_xmm10: + case fpu_xmm11: + case fpu_xmm12: + case fpu_xmm13: + case fpu_xmm14: + case fpu_xmm15: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + //::memcpy (fpu.xmm[reg - fpu_xmm0].bytes, reg_value.value.vector.uint8, 16); + return false; + + case exc_trapno: + exc.trapno = value.UInt(0); + break; + + case exc_err: + exc.err = value.UInt(0); + break; + + case exc_faultvaddr: + exc.faultvaddr = value.UInt(0); + break; + + default: + return false; + } + return WriteRegisterSet(set) == KERN_SUCCESS; +} + +bool +RegisterContextMach_x86_64::ReadRegisterBytes (uint32_t reg, DataExtractor &data) +{ + int set = RegisterContextMach_x86_64::GetSetForNativeRegNum (reg); + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info == NULL) + return false; + + switch (reg) + { + case gpr_rax: + case gpr_rbx: + case gpr_rcx: + case gpr_rdx: + case gpr_rdi: + case gpr_rsi: + case gpr_rbp: + case gpr_rsp: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_r13: + case gpr_r14: + case gpr_r15: + case gpr_rip: + case gpr_rflags: + case gpr_cs: + case gpr_fs: + case gpr_gs: + data.SetData(&gpr.rax + reg - gpr_rax, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_fcw: + data.SetData(&fpu.fcw, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_fsw: + data.SetData(&fpu.fsw, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_ftw: + data.SetData(&fpu.ftw, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_fop: + data.SetData(&fpu.fop, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_ip: + data.SetData(&fpu.ip, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_cs: + data.SetData(&fpu.cs, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_dp: + data.SetData(&fpu.dp, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_ds: + data.SetData(&fpu.ds, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_mxcsr: + data.SetData(&fpu.mxcsr, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_mxcsrmask: + data.SetData(&fpu.mxcsrmask, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + data.SetData(fpu.stmm[reg - fpu_stmm0].bytes, reg_info->byte_size, eByteOrderHost); + break; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + case fpu_xmm8: + case fpu_xmm9: + case fpu_xmm10: + case fpu_xmm11: + case fpu_xmm12: + case fpu_xmm13: + case fpu_xmm14: + case fpu_xmm15: + data.SetData(fpu.xmm[reg - fpu_xmm0].bytes, reg_info->byte_size, eByteOrderHost); + break; + + case exc_trapno: + data.SetData(&exc.trapno, reg_info->byte_size, eByteOrderHost); + break; + + case exc_err: + data.SetData(&exc.err, reg_info->byte_size, eByteOrderHost); + break; + + case exc_faultvaddr: + data.SetData(&exc.faultvaddr, reg_info->byte_size, eByteOrderHost); + break; + + default: + return false; + } + return true; +} + +bool +RegisterContextMach_x86_64::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset) +{ + int set = RegisterContextMach_x86_64::GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + + const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info == NULL && data.ValidOffsetForDataOfSize(data_offset, reg_info->byte_size)) + return false; + + uint32_t offset = data_offset; + switch (reg) + { + case gpr_rax: + case gpr_rbx: + case gpr_rcx: + case gpr_rdx: + case gpr_rdi: + case gpr_rsi: + case gpr_rbp: + case gpr_rsp: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_r13: + case gpr_r14: + case gpr_r15: + case gpr_rip: + case gpr_rflags: + case gpr_cs: + case gpr_fs: + case gpr_gs: + (&gpr.rax)[reg - gpr_rax] = data.GetU32 (&offset); + break; + + case fpu_fcw: + fpu.fcw = data.GetU16(&offset); + break; + + case fpu_fsw: + fpu.fsw = data.GetU16(&offset); + break; + + case fpu_ftw: + fpu.ftw = data.GetU8(&offset); + break; + + case fpu_fop: + fpu.fop = data.GetU16(&offset); + break; + + case fpu_ip: + fpu.ip = data.GetU32(&offset); + break; + + case fpu_cs: + fpu.cs = data.GetU16(&offset); + break; + + case fpu_dp: + fpu.dp = data.GetU32(&offset); + break; + + case fpu_ds: + fpu.ds = data.GetU16(&offset); + break; + + case fpu_mxcsr: + fpu.mxcsr = data.GetU32(&offset); + break; + + case fpu_mxcsrmask: + fpu.mxcsrmask = data.GetU32(&offset); + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + ::memcpy (fpu.stmm[reg - fpu_stmm0].bytes, data.PeekData(offset, reg_info->byte_size), reg_info->byte_size); + return false; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + case fpu_xmm8: + case fpu_xmm9: + case fpu_xmm10: + case fpu_xmm11: + case fpu_xmm12: + case fpu_xmm13: + case fpu_xmm14: + case fpu_xmm15: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + ::memcpy (fpu.xmm[reg - fpu_xmm0].bytes, data.PeekData(offset, reg_info->byte_size), reg_info->byte_size); + return false; + + case exc_trapno: + exc.trapno = data.GetU32 (&offset); + break; + + case exc_err: + exc.err = data.GetU32 (&offset); + break; + + case exc_faultvaddr: + exc.faultvaddr = data.GetU32 (&offset); + break; + + default: + return false; + } + return WriteRegisterSet(set) == KERN_SUCCESS; +} + +bool +RegisterContextMach_x86_64::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (data_sp && + ReadGPR (false) == KERN_SUCCESS && + ReadFPU (false) == KERN_SUCCESS && + ReadEXC (false) == KERN_SUCCESS) + { + uint8_t *dst = data_sp->GetBytes(); + ::memcpy (dst, &gpr, sizeof(gpr)); + dst += sizeof(gpr); + + ::memcpy (dst, &fpu, sizeof(fpu)); + dst += sizeof(gpr); + + ::memcpy (dst, &exc, sizeof(exc)); + return true; + } + return false; +} + +bool +RegisterContextMach_x86_64::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) + { + const uint8_t *src = data_sp->GetBytes(); + ::memcpy (&gpr, src, sizeof(gpr)); + src += sizeof(gpr); + + ::memcpy (&fpu, src, sizeof(fpu)); + src += sizeof(gpr); + + ::memcpy (&exc, src, sizeof(exc)); + uint32_t success_count = 0; + if (WriteGPR() == KERN_SUCCESS) + ++success_count; + if (WriteFPU() == KERN_SUCCESS) + ++success_count; + if (WriteEXC() == KERN_SUCCESS) + ++success_count; + return success_count == 3; + } + return false; +} + + +uint32_t +RegisterContextMach_x86_64::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t reg) +{ + if (kind == eRegisterKindGeneric) + { + switch (reg) + { + case LLDB_REGNUM_GENERIC_PC: return gpr_rip; + case LLDB_REGNUM_GENERIC_SP: return gpr_rsp; + case LLDB_REGNUM_GENERIC_FP: return gpr_rbp; + case LLDB_REGNUM_GENERIC_FLAGS: return gpr_rflags; + case LLDB_REGNUM_GENERIC_RA: + default: + break; + } + } + else if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF) + { + switch (reg) + { + case gcc_dwarf_gpr_rax: return gpr_rax; + case gcc_dwarf_gpr_rdx: return gpr_rdx; + case gcc_dwarf_gpr_rcx: return gpr_rcx; + case gcc_dwarf_gpr_rbx: return gpr_rbx; + case gcc_dwarf_gpr_rsi: return gpr_rsi; + case gcc_dwarf_gpr_rdi: return gpr_rdi; + case gcc_dwarf_gpr_rbp: return gpr_rbp; + case gcc_dwarf_gpr_rsp: return gpr_rsp; + case gcc_dwarf_gpr_r8: return gpr_r8; + case gcc_dwarf_gpr_r9: return gpr_r9; + case gcc_dwarf_gpr_r10: return gpr_r10; + case gcc_dwarf_gpr_r11: return gpr_r11; + case gcc_dwarf_gpr_r12: return gpr_r12; + case gcc_dwarf_gpr_r13: return gpr_r13; + case gcc_dwarf_gpr_r14: return gpr_r14; + case gcc_dwarf_gpr_r15: return gpr_r15; + case gcc_dwarf_gpr_rip: return gpr_rip; + case gcc_dwarf_fpu_xmm0: return fpu_xmm0; + case gcc_dwarf_fpu_xmm1: return fpu_xmm1; + case gcc_dwarf_fpu_xmm2: return fpu_xmm2; + case gcc_dwarf_fpu_xmm3: return fpu_xmm3; + case gcc_dwarf_fpu_xmm4: return fpu_xmm4; + case gcc_dwarf_fpu_xmm5: return fpu_xmm5; + case gcc_dwarf_fpu_xmm6: return fpu_xmm6; + case gcc_dwarf_fpu_xmm7: return fpu_xmm7; + case gcc_dwarf_fpu_xmm8: return fpu_xmm8; + case gcc_dwarf_fpu_xmm9: return fpu_xmm9; + case gcc_dwarf_fpu_xmm10: return fpu_xmm10; + case gcc_dwarf_fpu_xmm11: return fpu_xmm11; + case gcc_dwarf_fpu_xmm12: return fpu_xmm12; + case gcc_dwarf_fpu_xmm13: return fpu_xmm13; + case gcc_dwarf_fpu_xmm14: return fpu_xmm14; + case gcc_dwarf_fpu_xmm15: return fpu_xmm15; + case gcc_dwarf_fpu_stmm0: return fpu_stmm0; + case gcc_dwarf_fpu_stmm1: return fpu_stmm1; + case gcc_dwarf_fpu_stmm2: return fpu_stmm2; + case gcc_dwarf_fpu_stmm3: return fpu_stmm3; + case gcc_dwarf_fpu_stmm4: return fpu_stmm4; + case gcc_dwarf_fpu_stmm5: return fpu_stmm5; + case gcc_dwarf_fpu_stmm6: return fpu_stmm6; + case gcc_dwarf_fpu_stmm7: return fpu_stmm7; + default: + break; + } + } + else if (kind == eRegisterKindGDB) + { + switch (reg) + { + case gdb_gpr_rax : return gpr_rax; + case gdb_gpr_rbx : return gpr_rbx; + case gdb_gpr_rcx : return gpr_rcx; + case gdb_gpr_rdx : return gpr_rdx; + case gdb_gpr_rsi : return gpr_rsi; + case gdb_gpr_rdi : return gpr_rdi; + case gdb_gpr_rbp : return gpr_rbp; + case gdb_gpr_rsp : return gpr_rsp; + case gdb_gpr_r8 : return gpr_r8; + case gdb_gpr_r9 : return gpr_r9; + case gdb_gpr_r10 : return gpr_r10; + case gdb_gpr_r11 : return gpr_r11; + case gdb_gpr_r12 : return gpr_r12; + case gdb_gpr_r13 : return gpr_r13; + case gdb_gpr_r14 : return gpr_r14; + case gdb_gpr_r15 : return gpr_r15; + case gdb_gpr_rip : return gpr_rip; + case gdb_gpr_rflags : return gpr_rflags; + case gdb_gpr_cs : return gpr_cs; + case gdb_gpr_ss : return gpr_gs; // HACK: For now for "ss", just copy what is in "gs" + case gdb_gpr_ds : return gpr_gs; // HACK: For now for "ds", just copy what is in "gs" + case gdb_gpr_es : return gpr_gs; // HACK: For now for "es", just copy what is in "gs" + case gdb_gpr_fs : return gpr_fs; + case gdb_gpr_gs : return gpr_gs; + case gdb_fpu_stmm0 : return fpu_stmm0; + case gdb_fpu_stmm1 : return fpu_stmm1; + case gdb_fpu_stmm2 : return fpu_stmm2; + case gdb_fpu_stmm3 : return fpu_stmm3; + case gdb_fpu_stmm4 : return fpu_stmm4; + case gdb_fpu_stmm5 : return fpu_stmm5; + case gdb_fpu_stmm6 : return fpu_stmm6; + case gdb_fpu_stmm7 : return fpu_stmm7; + case gdb_fpu_fctrl : return fpu_fctrl; + case gdb_fpu_fstat : return fpu_fstat; + case gdb_fpu_ftag : return fpu_ftag; + case gdb_fpu_fiseg : return fpu_fiseg; + case gdb_fpu_fioff : return fpu_fioff; + case gdb_fpu_foseg : return fpu_foseg; + case gdb_fpu_fooff : return fpu_fooff; + case gdb_fpu_fop : return fpu_fop; + case gdb_fpu_xmm0 : return fpu_xmm0; + case gdb_fpu_xmm1 : return fpu_xmm1; + case gdb_fpu_xmm2 : return fpu_xmm2; + case gdb_fpu_xmm3 : return fpu_xmm3; + case gdb_fpu_xmm4 : return fpu_xmm4; + case gdb_fpu_xmm5 : return fpu_xmm5; + case gdb_fpu_xmm6 : return fpu_xmm6; + case gdb_fpu_xmm7 : return fpu_xmm7; + case gdb_fpu_xmm8 : return fpu_xmm8; + case gdb_fpu_xmm9 : return fpu_xmm9; + case gdb_fpu_xmm10 : return fpu_xmm10; + case gdb_fpu_xmm11 : return fpu_xmm11; + case gdb_fpu_xmm12 : return fpu_xmm12; + case gdb_fpu_xmm13 : return fpu_xmm13; + case gdb_fpu_xmm14 : return fpu_xmm14; + case gdb_fpu_xmm15 : return fpu_xmm15; + case gdb_fpu_mxcsr : return fpu_mxcsr; + default: + break; + } + } + return LLDB_INVALID_REGNUM; +} + +bool +RegisterContextMach_x86_64::HardwareSingleStep (bool enable) +{ + if (ReadGPR(true) != KERN_SUCCESS) + return false; + + const uint64_t trace_bit = 0x100ull; + if (enable) + { + + if (gpr.rflags & trace_bit) + return true; // trace bit is already set, there is nothing to do + else + gpr.rflags |= trace_bit; + } + else + { + if (gpr.rflags & trace_bit) + gpr.rflags &= ~trace_bit; + else + return true; // trace bit is clear, there is nothing to do + } + + return WriteGPR() == KERN_SUCCESS; +} + diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.h b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.h new file mode 100644 index 00000000000..4f33bbdc2db --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.h @@ -0,0 +1,261 @@ +//===-- RegisterContextMach_x86_64.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_RegisterContextMach_x86_64_h_ +#define liblldb_RegisterContextMach_x86_64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" + +class RegisterContextMach_x86_64 : public lldb_private::RegisterContext +{ +public: + RegisterContextMach_x86_64 (lldb_private::Thread &thread, + lldb_private::StackFrame *frame); + + virtual + ~RegisterContextMach_x86_64(); + + 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 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 = 0); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + + virtual bool + HardwareSingleStep (bool enable); + + struct GPR + { + uint64_t rax; + uint64_t rbx; + uint64_t rcx; + uint64_t rdx; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rsp; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rip; + uint64_t rflags; + uint64_t cs; + uint64_t fs; + uint64_t gs; + }; + + struct MMSReg + { + uint8_t bytes[10]; + uint8_t pad[6]; + }; + + struct XMMReg + { + uint8_t bytes[16]; + }; + + struct FPU + { + uint32_t pad[2]; + uint16_t fcw; // "fctrl" + uint16_t fsw; // "fstat" + uint8_t ftw; // "ftag" + uint8_t pad1; + uint16_t fop; // "fop" + uint32_t ip; // "fioff" + uint16_t cs; // "fiseg" + uint16_t pad2; + uint32_t dp; // "fooff" + uint16_t ds; // "foseg" + uint16_t pad3; + uint32_t mxcsr; + uint32_t mxcsrmask; + MMSReg stmm[8]; + XMMReg xmm[16]; + uint8_t pad4[6*16]; + int pad5; + }; + + struct EXC + { + uint32_t trapno; + uint32_t err; + uint64_t faultvaddr; + }; + +protected: + + typedef enum + { + GPRRegSet = 4, + FPURegSet = 5, + EXCRegSet = 6 + }; + + enum + { + GPRWordCount = sizeof(GPR)/sizeof(uint32_t), + FPUWordCount = sizeof(FPU)/sizeof(uint32_t), + EXCWordCount = sizeof(EXC)/sizeof(uint32_t) + }; + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + GPR gpr; + FPU fpu; + EXC exc; + kern_return_t gpr_errs[2]; // Read/Write errors + kern_return_t fpu_errs[2]; // Read/Write errors + kern_return_t exc_errs[2]; // Read/Write errors + + void + InvalidateAllRegisterStates() + { + SetError (GPRRegSet, Read, -1); + SetError (FPURegSet, Read, -1); + SetError (EXCRegSet, Read, -1); + } + + kern_return_t + GetError (int flavor, uint32_t err_idx) const + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + // When getting all errors, just OR all values together to see if + // we got any kind of error. + case GPRRegSet: return gpr_errs[err_idx]; + case FPURegSet: return fpu_errs[err_idx]; + case EXCRegSet: return exc_errs[err_idx]; + default: break; + } + } + return -1; + } + + bool + SetError (int flavor, uint32_t err_idx, kern_return_t err) + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + case GPRRegSet: + gpr_errs[err_idx] = err; + return true; + + case FPURegSet: + fpu_errs[err_idx] = err; + return true; + + case EXCRegSet: + exc_errs[err_idx] = err; + return true; + + default: break; + } + } + return false; + } + + bool + RegisterSetIsCached (int set) const + { + return GetError(set, Read) == KERN_SUCCESS; + } + + void + LogGPR (lldb_private::Log *log, const char *format, ...); + + kern_return_t + ReadGPR (bool force); + + kern_return_t + ReadFPU (bool force); + + kern_return_t + ReadEXC (bool force); + + kern_return_t + WriteGPR (); + + kern_return_t + WriteFPU (); + + kern_return_t + WriteEXC (); + + kern_return_t + ReadRegisterSet (uint32_t set, bool force); + + kern_return_t + WriteRegisterSet (uint32_t set); + + static uint32_t + GetRegisterNumber (uint32_t reg_kind, uint32_t reg_num); + + static int + GetSetForNativeRegNum (int reg_num); + + static size_t + GetRegisterInfosCount (); + + static const lldb::RegisterInfo * + GetRegisterInfos (); + +}; + +#endif // liblldb_RegisterContextMach_x86_64_h_ diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.cpp new file mode 100644 index 00000000000..46d84a853d0 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.cpp @@ -0,0 +1,769 @@ +//===-- ThreadMacOSX.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "ThreadMacOSX.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "ProcessMacOSX.h" +#include "ProcessMacOSXLog.h" +#include "MachThreadContext.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Breakpoint/WatchpointLocation.h" +#include "lldb/Core/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Thread Registers +//---------------------------------------------------------------------- + +ThreadMacOSX::ThreadMacOSX (ProcessMacOSX &process, lldb::tid_t tid) : + Thread(process, tid), + m_fp_pc_pairs(), + m_basic_info(), + m_suspend_count(0), + m_stop_exception(), + m_context() +{ + ProcessMacOSX::CreateArchCalback create_arch_callback = process.GetArchCreateCallback(); + assert(create_arch_callback != NULL); + m_context.reset(create_arch_callback(process.GetArchSpec(), *this)); + assert(m_context.get() != NULL); + m_context->InitializeInstance(); + ::bzero (&m_basic_info, sizeof (m_basic_info)); + ::bzero (&m_ident_info, sizeof (m_ident_info)); + ::bzero (&m_proc_threadinfo, sizeof (m_proc_threadinfo)); + ProcessMacOSXLog::LogIf(PD_LOG_THREAD | PD_LOG_VERBOSE, "ThreadMacOSX::ThreadMacOSX ( pid = %i, tid = 0x%4.4x, )", m_process.GetID(), GetID()); +} + +ThreadMacOSX::~ThreadMacOSX () +{ +} + +#if defined (__i386__) || defined (__x86_64__) + #define MACH_SOFTWARE_BREAKPOINT_DATA_0 EXC_I386_BPT + #define MACH_TRAP_DATA_0 EXC_I386_SGL +#elif defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) + #define MACH_SOFTWARE_BREAKPOINT_DATA_0 EXC_PPC_BREAKPOINT + +#elif defined (__arm__) + #define MACH_SOFTWARE_BREAKPOINT_DATA_0 EXC_ARM_BREAKPOINT +#endif + + +bool +ThreadMacOSX::GetRawStopReason (Thread::StopInfo *stop_info ) +{ + stop_info->SetThread(this); + + bool success = GetStopException().GetStopInfo(stop_info); + + +#if defined (MACH_SOFTWARE_BREAKPOINT_DATA_0) || defined (MACH_TRAP_DATA_0) + if (stop_info->GetStopReason() == eStopReasonException) + { + if (stop_info->GetExceptionType() == EXC_BREAKPOINT && stop_info->GetExceptionDataCount() == 2) + { + const lldb::addr_t data_0 = stop_info->GetExceptionDataAtIndex(0); +#if defined (MACH_SOFTWARE_BREAKPOINT_DATA_0) + if (data_0 == MACH_SOFTWARE_BREAKPOINT_DATA_0) + { + lldb::addr_t pc = GetRegisterContext()->GetPC(); + lldb::user_id_t break_id = m_process.GetBreakpointSiteList().FindIDByAddress(pc); + if (break_id != LLDB_INVALID_BREAK_ID) + { + stop_info->Clear (); + stop_info->SetStopReasonWithBreakpointSiteID (break_id); + return success; + } + } +#endif +#if defined (MACH_TRAP_DATA_0) + if (data_0 == MACH_TRAP_DATA_0) + { + stop_info->Clear (); + stop_info->SetStopReasonToTrace (); + return success; + } +#endif + } + } +#endif + + if (stop_info->GetStopReason() == eStopReasonException) + { + if (stop_info->GetExceptionType() == EXC_SOFTWARE && + stop_info->GetExceptionDataCount() == 2 && + stop_info->GetExceptionDataAtIndex(0) == EXC_SOFT_SIGNAL) + { + int signo = stop_info->GetExceptionDataAtIndex(1); + stop_info->Clear (); + stop_info->SetStopReasonWithSignal (signo); + } + } + else + { + stop_info->SetStopReasonToNone(); + } + + return success; +} + +const char * +ThreadMacOSX::GetInfo () +{ + return GetBasicInfoAsString(); +} + +bool +ThreadMacOSX::GetIdentifierInfo () +{ +#ifdef THREAD_IDENTIFIER_INFO_COUNT + if (m_ident_info.thread_id == 0) + { + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + return ::thread_info (GetID(), THREAD_IDENTIFIER_INFO, (thread_info_t) &m_ident_info, &count) == KERN_SUCCESS; + } +#else + //m_error.SetErrorString("Thread_info doesn't support THREAD_IDENTIFIER_INFO."); +#endif + + return false; +} + +const char * +ThreadMacOSX::GetDispatchQueueName() +{ + if (GetIdentifierInfo ()) + { + if (m_ident_info.dispatch_qaddr == 0) + return NULL; + + uint8_t memory_buffer[8]; + DataExtractor data(memory_buffer, sizeof(memory_buffer), m_process.GetByteOrder(), m_process.GetAddressByteSize()); + ModuleSP module_sp(m_process.GetTarget().GetImages().FindFirstModuleForFileSpec (FileSpec("libSystem.B.dylib"))); + if (module_sp.get() == NULL) + return NULL; + + lldb::addr_t dispatch_queue_offsets_addr = LLDB_INVALID_ADDRESS; + const Symbol *dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (ConstString("dispatch_queue_offsets"), eSymbolTypeData); + if (dispatch_queue_offsets_symbol) + dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol->GetValue().GetLoadAddress(&GetProcess()); + + if (dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS) + return NULL; + + // Excerpt from src/queue_private.h + struct dispatch_queue_offsets_s + { + uint16_t dqo_version; + uint16_t dqo_label; + uint16_t dqo_label_size; + } dispatch_queue_offsets; + + Error error; + if (m_process.ReadMemory (dispatch_queue_offsets_addr, memory_buffer, sizeof(dispatch_queue_offsets), error) == sizeof(dispatch_queue_offsets)) + { + uint32_t data_offset = 0; + if (data.GetU16(&data_offset, &dispatch_queue_offsets.dqo_version, sizeof(dispatch_queue_offsets)/sizeof(uint16_t))) + { + if (m_process.ReadMemory (m_ident_info.dispatch_qaddr, &memory_buffer, data.GetAddressByteSize(), error) == data.GetAddressByteSize()) + { + data_offset = 0; + lldb::addr_t queue_addr = data.GetAddress(&data_offset); + lldb::addr_t label_addr = queue_addr + dispatch_queue_offsets.dqo_label; + const size_t chunk_size = 32; + uint32_t label_pos = 0; + m_dispatch_queue_name.resize(chunk_size, '\0'); + while (1) + { + size_t bytes_read = m_process.ReadMemory (label_addr + label_pos, &m_dispatch_queue_name[label_pos], chunk_size, error); + + if (bytes_read <= 0) + break; + + if (m_dispatch_queue_name.find('\0', label_pos) != std::string::npos) + break; + label_pos += bytes_read; + } + m_dispatch_queue_name.erase(m_dispatch_queue_name.find('\0')); + } + } + } + } + + if (m_dispatch_queue_name.empty()) + return NULL; + return m_dispatch_queue_name.c_str(); +} + +const char * +ThreadMacOSX::GetName () +{ + if (GetIdentifierInfo ()) + ::proc_pidinfo (m_process.GetID(), PROC_PIDTHREADINFO, m_ident_info.thread_handle, &m_proc_threadinfo, sizeof (m_proc_threadinfo)); + + // No thread name, lets return the queue name instead + if (m_proc_threadinfo.pth_name[0] == '\0') + return GetDispatchQueueName(); + + // Return the thread name if there was one + if (m_proc_threadinfo.pth_name[0]) + return m_proc_threadinfo.pth_name; + return NULL; +} + +bool +ThreadMacOSX::WillResume (StateType resume_state) +{ + ThreadWillResume(resume_state); + Thread::WillResume(resume_state); + return true; +} + +void +ThreadMacOSX::RefreshStateAfterStop() +{ + // Invalidate all registers in our register context + GetRegisterContext()->Invalidate(); + + m_context->RefreshStateAfterStop(); + + // We may have suspended this thread so the primary thread could step + // without worrying about race conditions, so lets restore our suspend + // count. + RestoreSuspendCount(); + + // Update the basic information for a thread for suspend count reasons. + ThreadMacOSX::GetBasicInfo(GetID(), &m_basic_info); + m_suspend_count = m_basic_info.suspend_count; + m_basic_info_string.clear(); +} + +uint32_t +ThreadMacOSX::GetStackFrameCount() +{ + if (m_fp_pc_pairs.empty()) + GetStackFrameData(m_fp_pc_pairs); + return m_fp_pc_pairs.size(); +} + +// Make sure that GetStackFrameAtIndex() does NOT call GetStackFrameCount() when +// getting the stack frame at index zero! This way GetStackFrameCount() (via +// GetStackFRameData()) can call this function to get the first frame in order +// to provide the first frame to a lower call for efficiency sake (avoid +// redundant lookups in the frame symbol context). +lldb::StackFrameSP +ThreadMacOSX::GetStackFrameAtIndex (uint32_t idx) +{ + StackFrameSP frame_sp(m_frames.GetFrameAtIndex(idx)); + + if (frame_sp) + return frame_sp; + + // Don't try and fetch a frame while process is running + // Calling IsRunning isn't right here, because IsRunning reads the Public + // state but we need to be able to read the stack frames in the ShouldStop + // methods, which happen before the Public state has been updated. +// if (m_process.IsRunning()) +// return frame_sp; + + // Special case the first frame (idx == 0) so that we don't need to + // know how many stack frames there are to get it. If we need any other + // frames, then we do need to know if "idx" is a valid index. + if (idx == 0) + { + // If this is the first frame, we want to share the thread register + // context with the stack frame at index zero. + GetRegisterContext(); + assert (m_reg_context_sp.get()); + frame_sp.reset (new StackFrame (idx, *this, m_reg_context_sp, m_reg_context_sp->GetFP(), m_reg_context_sp->GetPC())); + } + else if (idx < GetStackFrameCount()) + { + assert (idx < m_fp_pc_pairs.size()); + frame_sp.reset (new StackFrame (idx, *this, m_fp_pc_pairs[idx].first, m_fp_pc_pairs[idx].second)); + } + m_frames.SetFrameAtIndex(idx, frame_sp); + return frame_sp; +} + +void +ThreadMacOSX::ClearStackFrames () +{ + m_fp_pc_pairs.clear(); + Thread::ClearStackFrames(); +} + + + +int32_t +ThreadMacOSX::Suspend() +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_THREAD); + if (log && log->GetMask().IsSet(PD_LOG_VERBOSE)) + log->Printf ("ThreadMacOSX::%s ( )", __FUNCTION__); + lldb::tid_t tid = GetID (); + if (ThreadIDIsValid(tid)) + { + Error err(::thread_suspend (tid), eErrorTypeMachKernel); + if (err.Success()) + m_suspend_count++; + if (log || err.Fail()) + err.PutToLog(log, "::thread_suspend (%4.4x)", tid); + } + return GetSuspendCount(); +} + +int32_t +ThreadMacOSX::Resume() +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_THREAD); + if (log && log->GetMask().IsSet(PD_LOG_VERBOSE)) + log->Printf ("ThreadMacOSX::%s ()", __FUNCTION__); + lldb::tid_t tid = GetID (); + if (ThreadIDIsValid(tid)) + { + while (m_suspend_count > 0) + { + Error err(::thread_resume (tid), eErrorTypeMachKernel); + if (err.Success()) + m_suspend_count--; + if (log || err.Fail()) + err.PutToLog(log, "::thread_resume (%4.4x)", tid); + } + } + return GetSuspendCount(); +} + +bool +ThreadMacOSX::RestoreSuspendCount() +{ + Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_THREAD); + if (log && log->GetMask().IsSet(PD_LOG_VERBOSE)) + log->Printf ("ThreadMacOSX::%s ( )", __FUNCTION__); + Error err; + lldb::tid_t tid = GetID (); + if (ThreadIDIsValid(tid) == false) + return false; + else if (m_suspend_count > m_basic_info.suspend_count) + { + while (m_suspend_count > m_basic_info.suspend_count) + { + err = ::thread_resume (tid); + if (err.Success()) + --m_suspend_count; + if (log || err.Fail()) + err.PutToLog(log, "::thread_resume (%4.4x)", tid); + } + } + else if (m_suspend_count < m_basic_info.suspend_count) + { + while (m_suspend_count < m_basic_info.suspend_count) + { + err = ::thread_suspend (tid); + if (err.Success()) + --m_suspend_count; + if (log || err.Fail()) + err.PutToLog(log, "::thread_suspend (%4.4x)", tid); + } + } + return m_suspend_count == m_basic_info.suspend_count; +} + + +const char * +ThreadMacOSX::GetBasicInfoAsString () +{ + if (m_basic_info_string.empty()) + { + StreamString sstr; + struct thread_basic_info basicInfo; + + lldb::tid_t tid = GetID (); + if (GetBasicInfo(tid, &basicInfo)) + { +// char run_state_str[32]; +// size_t run_state_str_size = sizeof(run_state_str); +// switch (basicInfo.run_state) +// { +// case TH_STATE_RUNNING: strncpy(run_state_str, "running", run_state_str_size); break; +// case TH_STATE_STOPPED: strncpy(run_state_str, "stopped", run_state_str_size); break; +// case TH_STATE_WAITING: strncpy(run_state_str, "waiting", run_state_str_size); break; +// case TH_STATE_UNINTERRUPTIBLE: strncpy(run_state_str, "uninterruptible", run_state_str_size); break; +// case TH_STATE_HALTED: strncpy(run_state_str, "halted", run_state_str_size); break; +// default: snprintf(run_state_str, run_state_str_size, "%d", basicInfo.run_state); break; // ??? +// } + float user = (float)basicInfo.user_time.seconds + (float)basicInfo.user_time.microseconds / 1000000.0f; + float system = (float)basicInfo.user_time.seconds + (float)basicInfo.user_time.microseconds / 1000000.0f; + sstr.Printf("Thread 0x%4.4x: user=%f system=%f cpu=%d sleep_time=%d", + InferiorThreadID(), + user, + system, + basicInfo.cpu_usage, + basicInfo.sleep_time); + m_basic_info_string.assign (sstr.GetData(), sstr.GetSize()); + } + } + if (m_basic_info_string.empty()) + return NULL; + return m_basic_info_string.c_str(); +} + + +//const uint8_t * +//ThreadMacOSX::SoftwareBreakpointOpcode (size_t break_op_size) const +//{ +// return m_context->SoftwareBreakpointOpcode(break_op_size); +//} + + +lldb::tid_t +ThreadMacOSX::InferiorThreadID() const +{ + mach_msg_type_number_t i; + mach_port_name_array_t names; + mach_port_type_array_t types; + mach_msg_type_number_t ncount, tcount; + lldb::tid_t inferior_tid = LLDB_INVALID_THREAD_ID; + task_t my_task = ::mach_task_self(); + task_t task = GetMacOSXProcess().Task().GetTaskPort(); + + kern_return_t kret = ::mach_port_names (task, &names, &ncount, &types, &tcount); + if (kret == KERN_SUCCESS) + { + lldb::tid_t tid = GetID (); + + for (i = 0; i < ncount; i++) + { + mach_port_t my_name; + mach_msg_type_name_t my_type; + + kret = ::mach_port_extract_right (task, names[i], MACH_MSG_TYPE_COPY_SEND, &my_name, &my_type); + if (kret == KERN_SUCCESS) + { + ::mach_port_deallocate (my_task, my_name); + if (my_name == tid) + { + inferior_tid = names[i]; + break; + } + } + } + // Free up the names and types + ::vm_deallocate (my_task, (vm_address_t) names, ncount * sizeof (mach_port_name_t)); + ::vm_deallocate (my_task, (vm_address_t) types, tcount * sizeof (mach_port_type_t)); + } + return inferior_tid; +} + +bool +ThreadMacOSX::GetBasicInfo(lldb::tid_t thread, struct thread_basic_info *basicInfoPtr) +{ + if (ThreadIDIsValid(thread)) + { + unsigned int info_count = THREAD_BASIC_INFO_COUNT; + kern_return_t err = ::thread_info (thread, THREAD_BASIC_INFO, (thread_info_t) basicInfoPtr, &info_count); + if (err == KERN_SUCCESS) + return true; + } + ::memset (basicInfoPtr, 0, sizeof (struct thread_basic_info)); + return false; +} + + +bool +ThreadMacOSX::ThreadIDIsValid (lldb::tid_t thread) +{ + return thread != 0; +} + +void +ThreadMacOSX::Dump(Log *log, uint32_t index) +{ + const char * thread_run_state = NULL; + + switch (m_basic_info.run_state) + { + case TH_STATE_RUNNING: thread_run_state = "running"; break; // 1 thread is running normally + case TH_STATE_STOPPED: thread_run_state = "stopped"; break; // 2 thread is stopped + case TH_STATE_WAITING: thread_run_state = "waiting"; break; // 3 thread is waiting normally + case TH_STATE_UNINTERRUPTIBLE: thread_run_state = "uninter"; break; // 4 thread is in an uninterruptible wait + case TH_STATE_HALTED: thread_run_state = "halted "; break; // 5 thread is halted at a + default: thread_run_state = "???"; break; + } + + RegisterContext *reg_context = GetRegisterContext(); + log->Printf ("thread[%u] %4.4x (%u): pc: 0x%8.8llx sp: 0x%8.8llx breakID: %d user: %d.%06.6d system: %d.%06.6d cpu: %d policy: %d run_state: %d (%s) flags: %d suspend_count: %d (current %d) sleep_time: %d", + index, + GetID (), + reg_context->GetPC (LLDB_INVALID_ADDRESS), + reg_context->GetSP (LLDB_INVALID_ADDRESS), + m_basic_info.user_time.seconds, m_basic_info.user_time.microseconds, + m_basic_info.system_time.seconds, m_basic_info.system_time.microseconds, + m_basic_info.cpu_usage, + m_basic_info.policy, + m_basic_info.run_state, + thread_run_state, + m_basic_info.flags, + m_basic_info.suspend_count, m_suspend_count, + m_basic_info.sleep_time); + //DumpRegisterState(0); +} + +void +ThreadMacOSX::ThreadWillResume (StateType resume_state) +{ + // Update the thread state to be the state we wanted when the task resumes + SetState (resume_state); + switch (resume_state) + { + case eStateSuspended: + Suspend(); + break; + + case eStateRunning: + case eStateStepping: + Resume(); + break; + } + m_context->ThreadWillResume(); +} + +void +ThreadMacOSX::DidResume () +{ + // TODO: cache current stack frames for next time in case we can match things up?? + ClearStackFrames(); + m_stop_exception.Clear(); + Thread::DidResume(); +} + +bool +ThreadMacOSX::ShouldStop(bool &step_more) +{ +// TODO: REmove this after all is working, Process should be managing this +// for us. +// +// // See if this thread is at a breakpoint? +// lldb::user_id_t breakID = CurrentBreakpoint(); +// +// if (LLDB_BREAK_ID_IS_VALID(breakID)) +// { +// // This thread is sitting at a breakpoint, ask the breakpoint +// // if we should be stopping here. +// if (Process()->Breakpoints().ShouldStop(ProcessID(), ThreadID(), breakID)) +// return true; +// else +// { +// // The breakpoint said we shouldn't stop, but we may have gotten +// // a signal or the user may have requested to stop in some other +// // way. Stop if we have a valid exception (this thread won't if +// // another thread was the reason this process stopped) and that +// // exception, is NOT a breakpoint exception (a common case would +// // be a SIGINT signal). +// if (GetStopException().IsValid() && !GetStopException().IsBreakpoint()) +// return true; +// } +// } +// else +// { + if (m_context->StepNotComplete()) + { + step_more = true; + return false; + } +// // The thread state is used to let us know what the thread was +// // trying to do. ThreadMacOSX::ThreadWillResume() will set the +// // thread state to various values depending if the thread was +// // the current thread and if it was to be single stepped, or +// // resumed. +// if (GetState() == eStateRunning) +// { +// // If our state is running, then we should continue as we are in +// // the process of stepping over a breakpoint. +// return false; +// } +// else +// { +// // Stop if we have any kind of valid exception for this +// // thread. +// if (GetStopException().IsValid()) +// return true; +// } +// } +// return false; + return true; +} + +bool +ThreadMacOSX::NotifyException(MachException::Data& exc) +{ + if (m_stop_exception.IsValid()) + { + // We may have more than one exception for a thread, but we need to + // only remember the one that we will say is the reason we stopped. + // We may have been single stepping and also gotten a signal exception, + // so just remember the most pertinent one. + if (m_stop_exception.IsBreakpoint()) + m_stop_exception = exc; + } + else + { + m_stop_exception = exc; + } +// bool handled = + m_context->NotifyException(exc); +// if (!handled) +// { +// handled = true; +// lldb::addr_t pc = GetPC(); +// lldb::user_id_t breakID = m_process.Breakpoints().FindIDCyAddress(pc); +// SetCurrentBreakpoint(breakID); +// switch (exc.exc_type) +// { +// case EXC_BAD_ACCESS: +// break; +// case EXC_BAD_INSTRUCTION: +// break; +// case EXC_ARITHMETIC: +// break; +// case EXC_EMULATION: +// break; +// case EXC_SOFTWARE: +// break; +// case EXC_BREAKPOINT: +// break; +// case EXC_SYSCALL: +// break; +// case EXC_MACH_SYSCALL: +// break; +// case EXC_RPC_ALERT: +// break; +// } +// } +// return handled; + return true; +} + +RegisterContext * +ThreadMacOSX::GetRegisterContext () +{ + if (m_reg_context_sp.get() == NULL) + m_reg_context_sp.reset (CreateRegisterContextForFrame (NULL)); + return m_reg_context_sp.get(); +} + +RegisterContext * +ThreadMacOSX::CreateRegisterContextForFrame (StackFrame *frame) +{ + return m_context->CreateRegisterContext (frame); +} + +uint32_t +ThreadMacOSX::SetHardwareBreakpoint (const BreakpointSite *bp) +{ + if (bp != NULL) + return GetRegisterContext()->SetHardwareBreakpoint(bp->GetLoadAddress(), bp->GetByteSize()); + return LLDB_INVALID_INDEX32; +} + +uint32_t +ThreadMacOSX::SetHardwareWatchpoint (const WatchpointLocation *wp) +{ + if (wp != NULL) + return GetRegisterContext()->SetHardwareWatchpoint(wp->GetLoadAddress(), wp->GetByteSize(), wp->WatchpointRead(), wp->WatchpointWrite()); + return LLDB_INVALID_INDEX32; +} + + +bool +ThreadMacOSX::SaveFrameZeroState (RegisterCheckpoint &checkpoint) +{ + lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0)); + if (frame_sp) + { + checkpoint.SetStackID(frame_sp->GetStackID()); + return frame_sp->GetRegisterContext()->ReadAllRegisterValues (checkpoint.GetData()); + } + return false; +} + +bool +ThreadMacOSX::RestoreSaveFrameZero (const RegisterCheckpoint &checkpoint) +{ + lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0)); + if (frame_sp) + { + bool ret = frame_sp->GetRegisterContext()->WriteAllRegisterValues (checkpoint.GetData()); + + // Clear out all stack frames as our world just changed. + ClearStackFrames(); + frame_sp->GetRegisterContext()->Invalidate(); + + return ret; + } + return false; +} + +bool +ThreadMacOSX::ClearHardwareBreakpoint (const BreakpointSite *bp) +{ + if (bp != NULL && bp->IsHardware()) + return GetRegisterContext()->ClearHardwareBreakpoint(bp->GetHardwareIndex()); + return false; +} + +bool +ThreadMacOSX::ClearHardwareWatchpoint (const WatchpointLocation *wp) +{ + if (wp != NULL && wp->IsHardware()) + return GetRegisterContext()->ClearHardwareWatchpoint(wp->GetHardwareIndex()); + return false; +} + +size_t +ThreadMacOSX::GetStackFrameData(std::vector<std::pair<lldb::addr_t, lldb::addr_t> >& fp_pc_pairs) +{ + lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0)); + return m_context->GetStackFrameData(frame_sp.get(), fp_pc_pairs); +} + + +//void +//ThreadMacOSX::NotifyBreakpointChanged (const BreakpointSite *bp) +//{ +// if (bp) +// { +// lldb::user_id_t breakID = bp->GetID(); +// if (bp->IsEnabled()) +// { +// if (bp->Address() == GetPC()) +// { +// SetCurrentBreakpoint(breakID); +// } +// } +// else +// { +// if (CurrentBreakpoint() == breakID) +// { +// SetCurrentBreakpoint(LLDB_INVALID_BREAK_ID); +// } +// } +// } +//} +// + + diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.h b/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.h new file mode 100644 index 00000000000..0039f3639f1 --- /dev/null +++ b/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.h @@ -0,0 +1,159 @@ +//===-- ThreadMacOSX.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_ThreadMacOSX_h_ +#define liblldb_ThreadMacOSX_h_ + +#include <libproc.h> + +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "MachException.h" + +class ProcessMacOSX; +class MachThreadContext; + +class ThreadMacOSX : public lldb_private::Thread +{ +public: + ThreadMacOSX (ProcessMacOSX &process, lldb::tid_t tid); + + virtual + ~ThreadMacOSX (); + + virtual bool + WillResume (lldb::StateType resume_state); + + virtual void + RefreshStateAfterStop(); + + virtual const char * + GetInfo (); + + virtual const char * + GetName (); + + virtual lldb_private::RegisterContext * + GetRegisterContext (); + + virtual lldb_private::RegisterContext * + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + virtual bool + SaveFrameZeroState (RegisterCheckpoint &checkpoint); + + virtual bool + RestoreSaveFrameZero (const RegisterCheckpoint &checkpoint); + + virtual uint32_t + GetStackFrameCount(); + + virtual lldb::StackFrameSP + GetStackFrameAtIndex (uint32_t idx); + + virtual void + ClearStackFrames (); + + ProcessMacOSX & + GetMacOSXProcess () + { + return (ProcessMacOSX &)m_process; + } + + const ProcessMacOSX & + GetMacOSXProcess () const + { + return (ProcessMacOSX &)m_process; + } + + void + Dump (lldb_private::Log *log, uint32_t index); + + lldb::tid_t + InferiorThreadID () const; + + static bool + ThreadIDIsValid (lldb::tid_t thread); + + int32_t + Resume (); + + int32_t + Suspend (); + + int32_t + GetSuspendCount () const { return m_suspend_count; } + + bool + RestoreSuspendCount (); + + uint32_t + SetHardwareBreakpoint (const lldb_private::BreakpointSite *bp); + + uint32_t + SetHardwareWatchpoint (const lldb_private::WatchpointLocation *wp); + + bool + ClearHardwareBreakpoint (const lldb_private::BreakpointSite *bp); + + bool + ClearHardwareWatchpoint (const lldb_private::WatchpointLocation *wp); + + void + ThreadWillResume (lldb::StateType resume_state); + + virtual void + DidResume (); + + bool + ShouldStop (bool &step_more); + + bool + NotifyException (MachException::Data& exc); + + const MachException::Data& + GetStopException () { return m_stop_exception; } + + const char * + GetBasicInfoAsString (); + + size_t + GetStackFrameData (std::vector<std::pair<lldb::addr_t, lldb::addr_t> >& fp_pc_pairs); + + virtual bool + GetRawStopReason (lldb_private::Thread::StopInfo *stop_info); + +protected: + bool + GetIdentifierInfo (); + + const char * + GetDispatchQueueName(); + + static bool + GetBasicInfo (lldb::tid_t threadID, struct thread_basic_info *basic_info); + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + std::vector<std::pair<lldb::addr_t, lldb::addr_t> > m_fp_pc_pairs; + struct thread_basic_info m_basic_info; // Basic information for a thread used to see if a thread is valid + std::string m_basic_info_string;// Basic thread info as a C string. +#ifdef THREAD_IDENTIFIER_INFO_COUNT + thread_identifier_info_data_t m_ident_info; + struct proc_threadinfo m_proc_threadinfo; + std::string m_dispatch_queue_name; +#endif + int32_t m_suspend_count; // The current suspend count + MachException::Data m_stop_exception; // The best exception that describes why this thread is stopped + std::auto_ptr<MachThreadContext> m_context; // The arch specific thread context for this thread (register state and more) + +}; + +#endif // liblldb_ThreadMacOSX_h_ 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 + diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp new file mode 100644 index 00000000000..cac2101e4c0 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -0,0 +1,813 @@ +//===-- GDBRemoteCommunication.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "GDBRemoteCommunication.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/Args.h" +#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/TimeValue.h" + +// Project includes +#include "StringExtractorGDBRemote.h" +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// GDBRemoteCommunication constructor +//---------------------------------------------------------------------- +GDBRemoteCommunication::GDBRemoteCommunication() : + Communication("gdb-remote.packets"), + m_send_acks (true), + m_rx_packet_listener ("gdbremote.rx_packet"), + m_sequence_mutex (Mutex::eMutexTypeRecursive), + m_is_running (false), + m_async_mutex (Mutex::eMutexTypeRecursive), + m_async_packet_predicate (false), + m_async_packet (), + m_async_response (), + m_async_timeout (UINT32_MAX), + m_async_signal (-1), + m_arch(), + m_os(), + m_vendor(), + m_byte_order(eByteOrderHost), + m_pointer_byte_size(0) +{ + m_rx_packet_listener.StartListeningForEvents(this, + Communication::eBroadcastBitPacketAvailable | + Communication::eBroadcastBitReadThreadDidExit); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +GDBRemoteCommunication::~GDBRemoteCommunication() +{ + m_rx_packet_listener.StopListeningForEvents(this, + Communication::eBroadcastBitPacketAvailable | + Communication::eBroadcastBitReadThreadDidExit); + if (IsConnected()) + { + StopReadThread(); + Disconnect(); + } +} + + +char +GDBRemoteCommunication::CalculcateChecksum (const char *payload, size_t payload_length) +{ + int checksum = 0; + + // We only need to compute the checksum if we are sending acks + if (m_send_acks) + { + for (int i = 0; i < payload_length; ++i) + checksum += payload[i]; + } + return checksum & 255; +} + +size_t +GDBRemoteCommunication::SendAck (char ack_char) +{ + Mutex::Locker locker(m_sequence_mutex); + ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "send packet: %c", ack_char); + ConnectionStatus status = eConnectionStatusSuccess; + return Write (&ack_char, 1, status, NULL) == 1; +} + +size_t +GDBRemoteCommunication::SendPacketAndWaitForResponse +( + const char *payload, + StringExtractorGDBRemote &response, + uint32_t timeout_seconds, + bool send_async +) +{ + return SendPacketAndWaitForResponse (payload, + ::strlen (payload), + response, + timeout_seconds, + send_async); +} + +size_t +GDBRemoteCommunication::SendPacketAndWaitForResponse +( + const char *payload, + size_t payload_length, + StringExtractorGDBRemote &response, + uint32_t timeout_seconds, + bool send_async +) +{ + Mutex::Locker locker; + TimeValue timeout_time; + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds (timeout_seconds); + + if (locker.TryLock (m_sequence_mutex.GetMutex())) + { + if (SendPacketNoLock (payload, strlen(payload))) + return WaitForPacketNoLock (response, &timeout_time); + } + else + { + if (send_async) + { + Mutex::Locker async_locker (m_async_mutex); + m_async_packet.assign(payload, payload_length); + m_async_timeout = timeout_seconds; + m_async_packet_predicate.SetValue (true, eBroadcastNever); + + bool timed_out = false; + if (SendInterrupt(1, &timed_out)) + { + if (m_async_packet_predicate.WaitForValueEqualTo (false, &timeout_time, &timed_out)) + { + response = m_async_response; + return response.GetStringRef().size(); + } + } +// if (timed_out) +// m_error.SetErrorString("Timeout."); +// else +// m_error.SetErrorString("Unknown error."); + } + else + { +// m_error.SetErrorString("Sequence mutex is locked."); + } + } + return 0; +} + +//template<typename _Tp> +//class ScopedValueChanger +//{ +//public: +// // Take a value reference and the value to assing it to when this class +// // instance goes out of scope. +// ScopedValueChanger (_Tp &value_ref, _Tp value) : +// m_value_ref (value_ref), +// m_value (value) +// { +// } +// +// // This object is going out of scope, change the value pointed to by +// // m_value_ref to the value we got during construction which was stored in +// // m_value; +// ~ScopedValueChanger () +// { +// m_value_ref = m_value; +// } +//protected: +// _Tp &m_value_ref; // A reference to the value we wil change when this object destructs +// _Tp m_value; // The value to assign to m_value_ref when this goes out of scope. +//}; + +StateType +GDBRemoteCommunication::SendContinuePacketAndWaitForResponse +( + ProcessGDBRemote *process, + const char *payload, + size_t packet_length, + StringExtractorGDBRemote &response +) +{ + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS); + if (log) + log->Printf ("GDBRemoteCommunication::%s ()", __FUNCTION__); + + Mutex::Locker locker(m_sequence_mutex); + m_is_running.SetValue (true, eBroadcastNever); + +// ScopedValueChanger<bool> restore_running_to_false (m_is_running, false); + StateType state = eStateRunning; + + if (SendPacket(payload, packet_length) == 0) + state = eStateInvalid; + + while (state == eStateRunning) + { + if (log) + log->Printf ("GDBRemoteCommunication::%s () WaitForPacket(...)", __FUNCTION__); + + if (WaitForPacket (response, (TimeValue*)NULL)) + { + if (response.Empty()) + state = eStateInvalid; + else + { + const char stop_type = response.GetChar(); + if (log) + log->Printf ("GDBRemoteCommunication::%s () got '%c' packet", __FUNCTION__, stop_type); + switch (stop_type) + { + case 'T': + case 'S': + if (m_async_signal != -1) + { + // Save off the async signal we are supposed to send + const int async_signal = m_async_signal; + // Clear the async signal member so we don't end up + // sending the signal multiple times... + m_async_signal = -1; + // Check which signal we stopped with + uint8_t signo = response.GetHexU8(255); + if (signo == async_signal) + { + // We already stopped with a signal that we wanted + // to stop with, so we are done + response.SetFilePos (0); + } + else + { + // We stopped with a different signal that the one + // we wanted to stop with, so now we must resume + // with the signal we want + char signal_packet[32]; + int signal_packet_len = 0; + signal_packet_len = ::snprintf (signal_packet, + sizeof (signal_packet), + "C%2.2x", + async_signal); + + if (SendPacket(signal_packet, signal_packet_len) == 0) + { + state = eStateInvalid; + break; + } + else + continue; + } + } + else if (m_async_packet_predicate.GetValue()) + { + // We are supposed to send an asynchronous packet while + // we are running. + m_async_response.Clear(); + if (!m_async_packet.empty()) + { + SendPacketAndWaitForResponse (m_async_packet.data(), + m_async_packet.size(), + m_async_response, + m_async_timeout, + false); + } + // Let the other thread that was trying to send the async + // packet know that the packet has been sent. + m_async_packet_predicate.SetValue(false, eBroadcastAlways); + + // Continue again + if (SendPacket("c", 1) == 0) + { + state = eStateInvalid; + break; + } + else + continue; + } + // Stop with signal and thread info + state = eStateStopped; + break; + + case 'W': + // process exited + state = eStateExited; + break; + + case 'O': + // STDOUT + { + std::string inferior_stdout; + inferior_stdout.reserve(response.GetBytesLeft () / 2); + char ch; + while ((ch = response.GetHexU8()) != '\0') + inferior_stdout.append(1, ch); + process->AppendSTDOUT (inferior_stdout.c_str(), inferior_stdout.size()); + } + break; + + case 'E': + // ERROR + state = eStateInvalid; + break; + + default: + if (log) + log->Printf ("GDBRemoteCommunication::%s () got unrecognized async packet: '%s'", __FUNCTION__, stop_type); + break; + } + } + } + else + { + if (log) + log->Printf ("GDBRemoteCommunication::%s () WaitForPacket(...) => false", __FUNCTION__); + state = eStateInvalid; + } + } + if (log) + log->Printf ("GDBRemoteCommunication::%s () => %s", __FUNCTION__, StateAsCString(state)); + response.SetFilePos(0); + m_is_running.SetValue (false, eBroadcastOnChange); + return state; +} + +size_t +GDBRemoteCommunication::SendPacket (const char *payload) +{ + Mutex::Locker locker(m_sequence_mutex); + return SendPacketNoLock (payload, ::strlen (payload)); +} + +size_t +GDBRemoteCommunication::SendPacket (const char *payload, size_t payload_length) +{ + Mutex::Locker locker(m_sequence_mutex); + return SendPacketNoLock (payload, payload_length); +} + +size_t +GDBRemoteCommunication::SendPacketNoLock (const char *payload, size_t payload_length) +{ + if (IsConnected()) + { + StreamString packet(0, 4, eByteOrderBig); + + packet.PutChar('$'); + packet.Write (payload, payload_length); + packet.PutChar('#'); + packet.PutHex8(CalculcateChecksum (payload, payload_length)); + + ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "send packet: %s", packet.GetData()); + ConnectionStatus status = eConnectionStatusSuccess; + size_t bytes_written = Write (packet.GetData(), packet.GetSize(), status, NULL); + if (bytes_written == packet.GetSize()) + { + if (m_send_acks) + GetAck (1) == '+'; + } + return bytes_written; + } + //m_error.SetErrorString("Not connected."); + return 0; +} + +char +GDBRemoteCommunication::GetAck (uint32_t timeout_seconds) +{ + StringExtractorGDBRemote response; + if (WaitForPacket (response, timeout_seconds) == 1) + return response.GetChar(); + return 0; +} + +bool +GDBRemoteCommunication::GetSequenceMutex (Mutex::Locker& locker) +{ + return locker.TryLock (m_sequence_mutex.GetMutex()); +} + +bool +GDBRemoteCommunication::SendAsyncSignal (int signo) +{ + m_async_signal = signo; + bool timed_out = false; + if (SendInterrupt(1, &timed_out)) + return true; + m_async_signal = -1; + return false; +} + +bool +GDBRemoteCommunication::SendInterrupt (uint32_t seconds_to_wait_for_stop, bool *timed_out) +{ + if (timed_out) + *timed_out = false; + + if (IsConnected() && IsRunning()) + { + // Only send an interrupt if our debugserver is running... + if (m_sequence_mutex.TryLock() != 0) + { + // Someone has the mutex locked waiting for a response or for the + // inferior to stop, so send the interrupt on the down low... + char ctrl_c = '\x03'; + ConnectionStatus status = eConnectionStatusSuccess; + TimeValue timeout; + if (seconds_to_wait_for_stop) + { + timeout = TimeValue::Now(); + timeout.OffsetWithSeconds (seconds_to_wait_for_stop); + } + ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "send packet: \\x03"); + if (Write (&ctrl_c, 1, status, NULL) > 0) + { + if (seconds_to_wait_for_stop) + m_is_running.WaitForValueEqualTo (false, &timeout, timed_out); + return true; + } + } + } + return false; +} + +size_t +GDBRemoteCommunication::WaitForPacket (StringExtractorGDBRemote &response, uint32_t timeout_seconds) +{ + TimeValue timeout_time; + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds (timeout_seconds); + return WaitForPacketNoLock (response, &timeout_time); +} + +size_t +GDBRemoteCommunication::WaitForPacket (StringExtractorGDBRemote &response, TimeValue* timeout_time_ptr) +{ + Mutex::Locker locker(m_sequence_mutex); + return WaitForPacketNoLock (response, timeout_time_ptr); +} + +size_t +GDBRemoteCommunication::WaitForPacketNoLock (StringExtractorGDBRemote &response, TimeValue* timeout_time_ptr) +{ + bool checksum_error = false; + response.Clear (); + + EventSP event_sp; + + if (m_rx_packet_listener.WaitForEvent (timeout_time_ptr, event_sp)) + { + const uint32_t event_type = event_sp->GetType(); + if (event_type | Communication::eBroadcastBitPacketAvailable) + { + const EventDataBytes *event_bytes = EventDataBytes::GetEventDataFromEvent(event_sp.get()); + if (event_bytes) + { + const char * packet_data = (const char *)event_bytes->GetBytes(); + ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "read packet: %s", packet_data); + const size_t packet_size = event_bytes->GetByteSize(); + if (packet_data && packet_size > 0) + { + std::string &response_str = response.GetStringRef(); + if (packet_data[0] == '$') + { + assert (packet_size >= 4); // Must have at least '$#CC' where CC is checksum + assert (packet_data[packet_size-3] == '#'); + assert (::isxdigit (packet_data[packet_size-2])); // Must be checksum hex byte + assert (::isxdigit (packet_data[packet_size-1])); // Must be checksum hex byte + response_str.assign (packet_data + 1, packet_size - 4); + if (m_send_acks) + { + char packet_checksum = strtol (&packet_data[packet_size-2], NULL, 16); + char actual_checksum = CalculcateChecksum (response_str.data(), response_str.size()); + checksum_error = packet_checksum != actual_checksum; + // Send the ack or nack if needed + if (checksum_error) + SendAck('-'); + else + SendAck('+'); + } + } + else + { + response_str.assign (packet_data, packet_size); + } + return response_str.size(); + } + } + } + else if (Communication::eBroadcastBitReadThreadDidExit) + { + // Our read thread exited on us so just fall through and return zero... + } + } + return 0; +} + +void +GDBRemoteCommunication::AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast) +{ + // Put the packet data into the buffer in a thread safe fashion + Mutex::Locker locker(m_bytes_mutex); + m_bytes.append ((const char *)src, src_len); + + // Parse up the packets into gdb remote packets + while (!m_bytes.empty()) + { + // end_idx must be one past the last valid packet byte. Start + // it off with an invalid value that is the same as the current + // index. + size_t end_idx = 0; + + switch (m_bytes[0]) + { + case '+': // Look for ack + case '-': // Look for cancel + case '\x03': // ^C to halt target + end_idx = 1; // The command is one byte long... + break; + + case '$': + // Look for a standard gdb packet? + end_idx = m_bytes.find('#'); + if (end_idx != std::string::npos) + { + if (end_idx + 2 < m_bytes.size()) + { + end_idx += 3; + } + else + { + // Checksum bytes aren't all here yet + end_idx = std::string::npos; + } + } + break; + + default: + break; + } + + if (end_idx == std::string::npos) + { + //ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS | GDBR_LOG_VERBOSE, "GDBRemoteCommunication::%s packet not yet complete: '%s'",__FUNCTION__, m_bytes.c_str()); + return; + } + else if (end_idx > 0) + { + // We have a valid packet... + assert (end_idx <= m_bytes.size()); + std::auto_ptr<EventDataBytes> event_bytes_ap (new EventDataBytes (&m_bytes[0], end_idx)); + ProcessGDBRemoteLog::LogIf (GDBR_LOG_COMM, "got full packet: %s", event_bytes_ap->GetBytes()); + BroadcastEvent (eBroadcastBitPacketAvailable, event_bytes_ap.release()); + m_bytes.erase(0, end_idx); + } + else + { + assert (1 <= m_bytes.size()); + ProcessGDBRemoteLog::LogIf (GDBR_LOG_COMM, "GDBRemoteCommunication::%s tossing junk byte at %c",__FUNCTION__, m_bytes[0]); + m_bytes.erase(0, 1); + } + } +} + +lldb::pid_t +GDBRemoteCommunication::GetCurrentProcessID (uint32_t timeout_seconds) +{ + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qC", strlen("qC"), response, timeout_seconds, false)) + { + if (response.GetChar() == 'Q') + if (response.GetChar() == 'C') + return response.GetHexMaxU32 (false, LLDB_INVALID_PROCESS_ID); + } + return LLDB_INVALID_PROCESS_ID; +} + +bool +GDBRemoteCommunication::GetLaunchSuccess (uint32_t timeout_seconds, std::string &error_str) +{ + error_str.clear(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qLaunchSuccess", strlen("qLaunchSuccess"), response, timeout_seconds, false)) + { + if (response.IsOKPacket()) + return true; + if (response.GetChar() == 'E') + { + // A string the describes what failed when launching... + error_str = response.GetStringRef().substr(1); + } + else + { + error_str.assign ("unknown error occurred launching process"); + } + } + else + { + error_str.assign ("failed to send the qLaunchSuccess packet"); + } + return false; +} + +int +GDBRemoteCommunication::SendArgumentsPacket (char const *argv[], uint32_t timeout_seconds) +{ + if (argv && argv[0]) + { + StreamString packet; + packet.PutChar('A'); + const char *arg; + for (uint32_t i = 0; (arg = argv[i]) != NULL; ++i) + { + const int arg_len = strlen(arg); + if (i > 0) + packet.PutChar(','); + packet.Printf("%i,%i,", arg_len * 2, i); + packet.PutBytesAsRawHex8(arg, arg_len, eByteOrderHost, eByteOrderHost); + } + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, timeout_seconds, false)) + { + if (response.IsOKPacket()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + } + return -1; +} + +int +GDBRemoteCommunication::SendEnvironmentPacket (char const *name_equal_value, uint32_t timeout_seconds) +{ + if (name_equal_value && name_equal_value[0]) + { + StreamString packet; + packet.Printf("QEnvironment:%s", name_equal_value); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, timeout_seconds, false)) + { + if (response.IsOKPacket()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + } + return -1; +} + +bool +GDBRemoteCommunication::GetHostInfo (uint32_t timeout_seconds) +{ + m_arch.Clear(); + m_os.Clear(); + m_vendor.Clear(); + m_byte_order = eByteOrderHost; + m_pointer_byte_size = 0; + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse ("qHostInfo", response, timeout_seconds, false)) + { + if (response.IsUnsupportedPacket()) + return false; + + + std::string name; + std::string value; + while (response.GetNameColonValue(name, value)) + { + if (name.compare("cputype") == 0) + { + // exception type in big endian hex + m_arch.SetCPUType(Args::StringToUInt32 (value.c_str(), LLDB_INVALID_CPUTYPE, 0)); + } + else if (name.compare("cpusubtype") == 0) + { + // exception count in big endian hex + m_arch.SetCPUSubtype(Args::StringToUInt32 (value.c_str(), 0, 0)); + } + else if (name.compare("ostype") == 0) + { + // exception data in big endian hex + m_os.SetCString(value.c_str()); + } + else if (name.compare("vendor") == 0) + { + m_vendor.SetCString(value.c_str()); + } + else if (name.compare("endian") == 0) + { + if (value.compare("little") == 0) + m_byte_order = eByteOrderLittle; + else if (value.compare("big") == 0) + m_byte_order = eByteOrderBig; + else if (value.compare("pdp") == 0) + m_byte_order = eByteOrderPDP; + } + else if (name.compare("ptrsize") == 0) + { + m_pointer_byte_size = Args::StringToUInt32 (value.c_str(), 0, 0); + } + } + } + return HostInfoIsValid(); +} + +int +GDBRemoteCommunication::SendAttach +( + lldb::pid_t pid, + uint32_t timeout_seconds, + StringExtractorGDBRemote& response +) +{ + if (pid != LLDB_INVALID_PROCESS_ID) + { + StreamString packet; + packet.Printf("vAttach;%x", pid); + + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, timeout_seconds, false)) + { + if (response.IsErrorPacket()) + return response.GetError(); + return 0; + } + } + return -1; +} + +const lldb_private::ArchSpec & +GDBRemoteCommunication::GetHostArchitecture () +{ + if (!HostInfoIsValid ()) + GetHostInfo (1); + return m_arch; +} + +const lldb_private::ConstString & +GDBRemoteCommunication::GetOSString () +{ + if (!HostInfoIsValid ()) + GetHostInfo (1); + return m_os; +} + +const lldb_private::ConstString & +GDBRemoteCommunication::GetVendorString() +{ + if (!HostInfoIsValid ()) + GetHostInfo (1); + return m_vendor; +} + +lldb::ByteOrder +GDBRemoteCommunication::GetByteOrder () +{ + if (!HostInfoIsValid ()) + GetHostInfo (1); + return m_byte_order; +} + +uint32_t +GDBRemoteCommunication::GetAddressByteSize () +{ + if (!HostInfoIsValid ()) + GetHostInfo (1); + return m_pointer_byte_size; +} + +addr_t +GDBRemoteCommunication::AllocateMemory (size_t size, uint32_t permissions, uint32_t timeout_seconds) +{ + char packet[64]; + ::snprintf (packet, sizeof(packet), "_M%zx,%s%s%s", size, + permissions & lldb::ePermissionsReadable ? "r" : "", + permissions & lldb::ePermissionsWritable ? "w" : "", + permissions & lldb::ePermissionsExecutable ? "x" : ""); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet, response, timeout_seconds, false)) + { + if (!response.IsErrorPacket()) + return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); + } + return LLDB_INVALID_ADDRESS; +} + +bool +GDBRemoteCommunication::DeallocateMemory (addr_t addr, uint32_t timeout_seconds) +{ + char packet[64]; + snprintf(packet, sizeof(packet), "_m%llx", (uint64_t)addr); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet, response, timeout_seconds, false)) + { + if (!response.IsOKPacket()) + return true; + } + return false; +} diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h new file mode 100644 index 00000000000..051fa445ff1 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -0,0 +1,270 @@ +//===-- GDBRemoteCommunication.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_GDBRemoteCommunication_h_ +#define liblldb_GDBRemoteCommunication_h_ + +// C Includes +// C++ Includes +#include <list> +#include <string> + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Listener.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Predicate.h" + +#include "StringExtractorGDBRemote.h" + +class ProcessGDBRemote; + +class GDBRemoteCommunication : + public lldb_private::Communication +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + GDBRemoteCommunication(); + + virtual + ~GDBRemoteCommunication(); + + size_t + SendPacket (const char *payload); + + size_t + SendPacket (const char *payload, + size_t payload_length); + + size_t + SendPacketAndWaitForResponse (const char *send_payload, + StringExtractorGDBRemote &response, + uint32_t timeout_seconds, + bool send_async); + + size_t + SendPacketAndWaitForResponse (const char *send_payload, + size_t send_length, + StringExtractorGDBRemote &response, + uint32_t timeout_seconds, + bool send_async); + + lldb::StateType + SendContinuePacketAndWaitForResponse (ProcessGDBRemote *process, + const char *packet_payload, + size_t packet_length, + StringExtractorGDBRemote &response); + + // Wait for a packet within 'nsec' seconds + size_t + WaitForPacket (StringExtractorGDBRemote &response, + uint32_t nsec); + + // Wait for a packet with an absolute timeout time. If 'timeout' is NULL + // wait indefinitely. + size_t + WaitForPacket (StringExtractorGDBRemote &response, + lldb_private::TimeValue* timeout); + + char + GetAck (uint32_t timeout_seconds); + + size_t + SendAck (char ack_char); + + char + CalculcateChecksum (const char *payload, + size_t payload_length); + + void + SetAckMode (bool enabled) + { + m_send_acks = enabled; + } + + bool + SendAsyncSignal (int signo); + + bool + SendInterrupt (uint32_t seconds_to_wait_for_stop, bool *timed_out = NULL); + + bool + GetSequenceMutex(lldb_private::Mutex::Locker& locker); + + //------------------------------------------------------------------ + // Communication overrides + //------------------------------------------------------------------ + virtual void + AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast); + + + lldb::pid_t + GetCurrentProcessID (uint32_t timeout_seconds); + + bool + GetLaunchSuccess (uint32_t timeout_seconds, std::string &error_str); + + //------------------------------------------------------------------ + /// Sends a GDB remote protocol 'A' packet that delivers program + /// arguments to the remote server. + /// + /// @param[in] argv + /// A NULL terminated array of const C strings to use as the + /// arguments. + /// + /// @param[in] timeout_seconds + /// The number of seconds to wait for a response from the remote + /// server. + /// + /// @return + /// Zero if the response was "OK", a positive value if the + /// the response was "Exx" where xx are two hex digits, or + /// -1 if the call is unsupported or any other unexpected + /// response was received. + //------------------------------------------------------------------ + int + SendArgumentsPacket (char const *argv[], uint32_t timeout_seconds); + + //------------------------------------------------------------------ + /// Sends a "QEnvironment:NAME=VALUE" packet that will build up the + /// environment that will get used when launching an application + /// in conjunction with the 'A' packet. This function can be called + /// multiple times in a row in order to pass on the desired + /// environment that the inferior should be launched with. + /// + /// @param[in] name_equal_value + /// A NULL terminated C string that contains a single enironment + /// in the format "NAME=VALUE". + /// + /// @param[in] timeout_seconds + /// The number of seconds to wait for a response from the remote + /// server. + /// + /// @return + /// Zero if the response was "OK", a positive value if the + /// the response was "Exx" where xx are two hex digits, or + /// -1 if the call is unsupported or any other unexpected + /// response was received. + //------------------------------------------------------------------ + int + SendEnvironmentPacket (char const *name_equal_value, + uint32_t timeout_seconds); + + //------------------------------------------------------------------ + /// Sends a "vAttach:PID" where PID is in hex. + /// + /// @param[in] pid + /// A process ID for the remote gdb server to attach to. + /// + /// @param[in] timeout_seconds + /// The number of seconds to wait for a response from the remote + /// server. + /// + /// @param[out] response + /// The response received from the gdb server. If the return + /// value is zero, \a response will contain a stop reply + /// packet. + /// + /// @return + /// Zero if the attach was successful, or an error indicating + /// an error code. + //------------------------------------------------------------------ + int + SendAttach (lldb::pid_t pid, + uint32_t timeout_seconds, + StringExtractorGDBRemote& response); + + + lldb::addr_t + AllocateMemory (size_t size, uint32_t permissions, uint32_t timeout_seconds); + + bool + DeallocateMemory (lldb::addr_t addr, uint32_t timeout_seconds); + + bool + IsRunning() const + { + return m_is_running.GetValue(); + } + + bool + GetHostInfo (uint32_t timeout_seconds); + + bool + HostInfoIsValid () const + { + return m_pointer_byte_size != 0; + } + + const lldb_private::ArchSpec & + GetHostArchitecture (); + + const lldb_private::ConstString & + GetOSString (); + + const lldb_private::ConstString & + GetVendorString(); + + lldb::ByteOrder + GetByteOrder (); + + uint32_t + GetAddressByteSize (); + +protected: + typedef std::list<std::string> packet_collection; + + size_t + SendPacketNoLock (const char *payload, + size_t payload_length); + + size_t + WaitForPacketNoLock (StringExtractorGDBRemote &response, + lldb_private::TimeValue* timeout_time_ptr); + + //------------------------------------------------------------------ + // Classes that inherit from GDBRemoteCommunication can see and modify these + //------------------------------------------------------------------ + bool m_send_acks; + lldb_private::Listener m_rx_packet_listener; + lldb_private::Mutex m_sequence_mutex; // Restrict access to sending/receiving packets to a single thread at a time + lldb_private::Predicate<bool> m_is_running; + + // If we need to send a packet while the target is running, the m_async_XXX + // member variables take care of making this happen. + lldb_private::Mutex m_async_mutex; + lldb_private::Predicate<bool> m_async_packet_predicate; + std::string m_async_packet; + StringExtractorGDBRemote m_async_response; + uint32_t m_async_timeout; + int m_async_signal; // We were asked to deliver a signal to the inferior process. + + lldb_private::ArchSpec m_arch; // Results from the qHostInfo call + uint32_t m_cpusubtype; // Results from the qHostInfo call + lldb_private::ConstString m_os; // Results from the qHostInfo call + lldb_private::ConstString m_vendor; // Results from the qHostInfo call + lldb::ByteOrder m_byte_order; // Results from the qHostInfo call + uint32_t m_pointer_byte_size; // Results from the qHostInfo call + + +private: + //------------------------------------------------------------------ + // For GDBRemoteCommunication only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (GDBRemoteCommunication); +}; + +#endif // liblldb_GDBRemoteCommunication_h_ diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp new file mode 100644 index 00000000000..a64e74dc963 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp @@ -0,0 +1,508 @@ +//===-- GDBRemoteRegisterContext.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "GDBRemoteRegisterContext.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" +// Project includes +#include "StringExtractorGDBRemote.h" +#include "ProcessGDBRemote.h" +#include "ThreadGDBRemote.h" +#include "ARM_GCC_Registers.h" +#include "ARM_DWARF_Registers.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// GDBRemoteRegisterContext constructor +//---------------------------------------------------------------------- +GDBRemoteRegisterContext::GDBRemoteRegisterContext +( + ThreadGDBRemote &thread, + StackFrame *frame, + GDBRemoteDynamicRegisterInfo ®_info, + bool read_all_at_once +) : + RegisterContext (thread, frame), + m_reg_info (reg_info), + m_reg_valid (), + m_reg_data (), + m_read_all_at_once (read_all_at_once) +{ + // Resize our vector of bools to contain one bool for every register. + // We will use these boolean values to know when a register value + // is valid in m_reg_data. + m_reg_valid.resize (reg_info.GetNumRegisters()); + + // Make a heap based buffer that is big enough to store all registers + DataBufferSP reg_data_sp(new DataBufferHeap (reg_info.GetRegisterDataByteSize(), 0)); + m_reg_data.SetData (reg_data_sp); + +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +GDBRemoteRegisterContext::~GDBRemoteRegisterContext() +{ +} + +ProcessGDBRemote & +GDBRemoteRegisterContext::GetGDBProcess() +{ + return static_cast<ProcessGDBRemote &>(m_thread.GetProcess()); +} + +ThreadGDBRemote & +GDBRemoteRegisterContext::GetGDBThread() +{ + return static_cast<ThreadGDBRemote &>(m_thread); +} + +void +GDBRemoteRegisterContext::Invalidate () +{ + SetAllRegisterValid (false); +} + +void +GDBRemoteRegisterContext::SetAllRegisterValid (bool b) +{ + std::vector<bool>::iterator pos, end = m_reg_valid.end(); + for (pos = m_reg_valid.begin(); pos != end; ++pos) + *pos = b; +} + +size_t +GDBRemoteRegisterContext::GetRegisterCount () +{ + return m_reg_info.GetNumRegisters (); +} + +const lldb::RegisterInfo * +GDBRemoteRegisterContext::GetRegisterInfoAtIndex (uint32_t reg) +{ + return m_reg_info.GetRegisterInfoAtIndex (reg); +} + +size_t +GDBRemoteRegisterContext::GetRegisterSetCount () +{ + return m_reg_info.GetNumRegisterSets (); +} + + + +const lldb::RegisterSet * +GDBRemoteRegisterContext::GetRegisterSet (uint32_t reg_set) +{ + return m_reg_info.GetRegisterSet (reg_set); +} + + + +bool +GDBRemoteRegisterContext::ReadRegisterValue (uint32_t reg, Scalar &value) +{ + // Read the register + if (ReadRegisterBytes (reg, m_reg_data)) + { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg); + uint32_t offset = reg_info->byte_offset; + switch (reg_info->encoding) + { + case eEncodingUint: + switch (reg_info->byte_size) + { + case 1: + case 2: + case 4: + value = m_reg_data.GetMaxU32 (&offset, reg_info->byte_size); + return true; + + case 8: + value = m_reg_data.GetMaxU64 (&offset, reg_info->byte_size); + return true; + } + break; + + case eEncodingSint: + switch (reg_info->byte_size) + { + case 1: + case 2: + case 4: + value = (int32_t)m_reg_data.GetMaxU32 (&offset, reg_info->byte_size); + return true; + + case 8: + value = m_reg_data.GetMaxS64 (&offset, reg_info->byte_size); + return true; + } + break; + + case eEncodingIEEE754: + switch (reg_info->byte_size) + { + case sizeof (float): + value = m_reg_data.GetFloat (&offset); + return true; + + case sizeof (double): + value = m_reg_data.GetDouble (&offset); + return true; + + case sizeof (long double): + value = m_reg_data.GetLongDouble (&offset); + return true; + } + break; + } + } + return false; +} + + +bool +GDBRemoteRegisterContext::ReadRegisterBytes (uint32_t reg, DataExtractor &data) +{ + GDBRemoteCommunication &gdb_comm = GetGDBProcess().GetGDBRemote(); +// FIXME: This check isn't right because IsRunning checks the Public state, but this +// is work you need to do - for instance in ShouldStop & friends - before the public +// state has been changed. +// if (gdb_comm.IsRunning()) +// return false; + + if (m_reg_valid_stop_id != m_thread.GetProcess().GetStopID()) + { + Invalidate(); + m_reg_valid_stop_id = m_thread.GetProcess().GetStopID(); + } + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg); + assert (reg_info); + if (m_reg_valid[reg] == false) + { + Mutex::Locker locker; + if (gdb_comm.GetSequenceMutex (locker)) + { + if (GetGDBProcess().SetCurrentGDBRemoteThread(m_thread.GetID())) + { + char packet[32]; + StringExtractorGDBRemote response; + int packet_len; + if (m_read_all_at_once) + { + // Get all registers in one packet + packet_len = ::snprintf (packet, sizeof(packet), "g"); + assert (packet_len < (sizeof(packet) - 1)); + if (gdb_comm.SendPacketAndWaitForResponse(packet, response, 1, false)) + { + if (response.IsNormalPacket()) + if (response.GetHexBytes ((void *)m_reg_data.GetDataStart(), m_reg_data.GetByteSize(), '\xcc') == m_reg_data.GetByteSize()) + SetAllRegisterValid (true); + } + } + else + { + // Get each register individually + packet_len = ::snprintf (packet, sizeof(packet), "p%x", reg, false); + assert (packet_len < (sizeof(packet) - 1)); + if (gdb_comm.SendPacketAndWaitForResponse(packet, response, 1, false)) + if (response.GetHexBytes ((uint8_t*)m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size), reg_info->byte_size, '\xcc') == reg_info->byte_size) + m_reg_valid[reg] = true; + } + } + } + } + + bool reg_is_valid = m_reg_valid[reg]; + if (reg_is_valid) + { + if (&data != &m_reg_data) + { + // If we aren't extracting into our own buffer (which + // only happens when this function is called from + // ReadRegisterValue(uint32_t, Scalar&)) then + // we transfer bytes from our buffer into the data + // buffer that was passed in + data.SetByteOrder (m_reg_data.GetByteOrder()); + data.SetData (m_reg_data, reg_info->byte_offset, reg_info->byte_size); + } + } + return reg_is_valid; +} + + +bool +GDBRemoteRegisterContext::WriteRegisterValue (uint32_t reg, const Scalar &value) +{ + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info) + { + DataExtractor data; + if (value.GetData (data, reg_info->byte_size)) + return WriteRegisterBytes (reg, data, 0); + } + return false; +} + + +bool +GDBRemoteRegisterContext::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset) +{ + GDBRemoteCommunication &gdb_comm = GetGDBProcess().GetGDBRemote(); +// FIXME: This check isn't right because IsRunning checks the Public state, but this +// is work you need to do - for instance in ShouldStop & friends - before the public +// state has been changed. +// if (gdb_comm.IsRunning()) +// return false; + + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg); + + if (reg_info) + { + // Grab a pointer to where we are going to put this register + uint8_t *dst = (uint8_t *)m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size); + + if (dst == NULL) + return false; + + // Grab a pointer to where we are going to grab the new value from + const uint8_t *src = data.PeekData(0, reg_info->byte_size); + + if (src == NULL) + return false; + + if (data.GetByteOrder() == m_reg_data.GetByteOrder()) + { + // No swapping, just copy the bytes + ::memcpy (dst, src, reg_info->byte_size); + } + else + { + // Swap the bytes + for (uint32_t i=0; i<reg_info->byte_size; ++i) + dst[i] = src[reg_info->byte_size - 1 - i]; + } + + Mutex::Locker locker; + if (gdb_comm.GetSequenceMutex (locker)) + { + if (GetGDBProcess().SetCurrentGDBRemoteThread(m_thread.GetID())) + { + uint32_t offset, end_offset; + StreamString packet; + StringExtractorGDBRemote response; + if (m_read_all_at_once) + { + // Get all registers in one packet + packet.PutChar ('G'); + offset = 0; + end_offset = m_reg_data.GetByteSize(); + + packet.PutBytesAsRawHex8 (m_reg_data.GetDataStart(), + m_reg_data.GetByteSize(), + eByteOrderHost, + eByteOrderHost); + + // Invalidate all register values + Invalidate (); + + if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(), + packet.GetString().size(), + response, + 1, + false)) + { + SetAllRegisterValid (false); + if (response.IsOKPacket()) + { + return true; + } + } + } + else + { + // Get each register individually + packet.Printf ("P%x=", reg); + packet.PutBytesAsRawHex8 (m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size), + reg_info->byte_size, + eByteOrderHost, + eByteOrderHost); + + // Invalidate just this register + m_reg_valid[reg] = false; + if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(), + packet.GetString().size(), + response, + 1, + false)) + { + if (response.IsOKPacket()) + { + return true; + } + } + } + } + } + } + return false; +} + + +bool +GDBRemoteRegisterContext::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + GDBRemoteCommunication &gdb_comm = GetGDBProcess().GetGDBRemote(); + StringExtractorGDBRemote response; + if (gdb_comm.SendPacketAndWaitForResponse("g", response, 1, false)) + { + if (response.IsErrorPacket()) + return false; + + response.GetStringRef().insert(0, 1, 'G'); + data_sp.reset (new DataBufferHeap(response.GetStringRef().data(), + response.GetStringRef().size())); + return true; + } + return false; +} + +bool +GDBRemoteRegisterContext::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + GDBRemoteCommunication &gdb_comm = GetGDBProcess().GetGDBRemote(); + StringExtractorGDBRemote response; + if (gdb_comm.SendPacketAndWaitForResponse((const char *)data_sp->GetBytes(), + data_sp->GetByteSize(), + response, + 1, + false)) + { + if (response.IsOKPacket()) + return true; + } + return false; +} + + +uint32_t +GDBRemoteRegisterContext::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) +{ + return m_reg_info.ConvertRegisterKindToRegisterNumber (kind, num); +} + +void +GDBRemoteDynamicRegisterInfo::HardcodeARMRegisters() +{ + static lldb::RegisterInfo + g_register_infos[] = + { + // NAME ALT SZ OFF ENCODING FORMAT NUM COMPILER DWARF GENERIC + // ====== ======= == ==== ============= ============ === =============== =============== ========= + { "r0", NULL, 4, 0, eEncodingUint, eFormatHex, 0, { gcc_r0, dwarf_r0, LLDB_INVALID_REGNUM }}, + { "r1", NULL, 4, 4, eEncodingUint, eFormatHex, 1, { gcc_r1, dwarf_r1, LLDB_INVALID_REGNUM }}, + { "r2", NULL, 4, 8, eEncodingUint, eFormatHex, 2, { gcc_r2, dwarf_r2, LLDB_INVALID_REGNUM }}, + { "r3", NULL, 4, 12, eEncodingUint, eFormatHex, 3, { gcc_r3, dwarf_r3, LLDB_INVALID_REGNUM }}, + { "r4", NULL, 4, 16, eEncodingUint, eFormatHex, 4, { gcc_r4, dwarf_r4, LLDB_INVALID_REGNUM }}, + { "r5", NULL, 4, 20, eEncodingUint, eFormatHex, 5, { gcc_r5, dwarf_r5, LLDB_INVALID_REGNUM }}, + { "r6", NULL, 4, 24, eEncodingUint, eFormatHex, 6, { gcc_r6, dwarf_r6, LLDB_INVALID_REGNUM }}, + { "r7", NULL, 4, 28, eEncodingUint, eFormatHex, 7, { gcc_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP }}, + { "r8", NULL, 4, 32, eEncodingUint, eFormatHex, 8, { gcc_r8, dwarf_r8, LLDB_INVALID_REGNUM }}, + { "r9", NULL, 4, 36, eEncodingUint, eFormatHex, 9, { gcc_r9, dwarf_r9, LLDB_INVALID_REGNUM }}, + { "r10", NULL, 4, 40, eEncodingUint, eFormatHex, 10, { gcc_r10, dwarf_r10, LLDB_INVALID_REGNUM }}, + { "r11", NULL, 4, 44, eEncodingUint, eFormatHex, 11, { gcc_r11, dwarf_r11, LLDB_INVALID_REGNUM }}, + { "r12", NULL, 4, 48, eEncodingUint, eFormatHex, 12, { gcc_r12, dwarf_r12, LLDB_INVALID_REGNUM }}, + { "sp", "r13", 4, 52, eEncodingUint, eFormatHex, 13, { gcc_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP }}, + { "lr", "r14", 4, 56, eEncodingUint, eFormatHex, 14, { gcc_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA }}, + { "pc", "r15", 4, 60, eEncodingUint, eFormatHex, 15, { gcc_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC }}, + { NULL, NULL, 12, 64, eEncodingIEEE754, eFormatFloat, 16, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { NULL, NULL, 12, 76, eEncodingIEEE754, eFormatFloat, 17, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { NULL, NULL, 12, 88, eEncodingIEEE754, eFormatFloat, 18, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { NULL, NULL, 12, 100, eEncodingIEEE754, eFormatFloat, 19, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { NULL, NULL, 12, 112, eEncodingIEEE754, eFormatFloat, 20, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { NULL, NULL, 12, 124, eEncodingIEEE754, eFormatFloat, 21, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { NULL, NULL, 12, 136, eEncodingIEEE754, eFormatFloat, 22, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { NULL, NULL, 12, 148, eEncodingIEEE754, eFormatFloat, 23, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { NULL, NULL, 12, 160, eEncodingIEEE754, eFormatFloat, 24, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }}, + { "cpsr", "psr", 4, 172, eEncodingUint, eFormatHex, 25, { gcc_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS }}, + { "s0", NULL, 4, 176, eEncodingIEEE754, eFormatFloat, 26, { LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM }}, + { "s1", NULL, 4, 180, eEncodingIEEE754, eFormatFloat, 27, { LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM }}, + { "s2", NULL, 4, 184, eEncodingIEEE754, eFormatFloat, 28, { LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM }}, + { "s3", NULL, 4, 188, eEncodingIEEE754, eFormatFloat, 29, { LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM }}, + { "s4", NULL, 4, 192, eEncodingIEEE754, eFormatFloat, 30, { LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM }}, + { "s5", NULL, 4, 196, eEncodingIEEE754, eFormatFloat, 31, { LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM }}, + { "s6", NULL, 4, 200, eEncodingIEEE754, eFormatFloat, 32, { LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM }}, + { "s7", NULL, 4, 204, eEncodingIEEE754, eFormatFloat, 33, { LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM }}, + { "s8", NULL, 4, 208, eEncodingIEEE754, eFormatFloat, 34, { LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM }}, + { "s9", NULL, 4, 212, eEncodingIEEE754, eFormatFloat, 35, { LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM }}, + { "s10", NULL, 4, 216, eEncodingIEEE754, eFormatFloat, 36, { LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM }}, + { "s11", NULL, 4, 220, eEncodingIEEE754, eFormatFloat, 37, { LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM }}, + { "s12", NULL, 4, 224, eEncodingIEEE754, eFormatFloat, 38, { LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM }}, + { "s13", NULL, 4, 228, eEncodingIEEE754, eFormatFloat, 39, { LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM }}, + { "s14", NULL, 4, 232, eEncodingIEEE754, eFormatFloat, 40, { LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM }}, + { "s15", NULL, 4, 236, eEncodingIEEE754, eFormatFloat, 41, { LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM }}, + { "s16", NULL, 4, 240, eEncodingIEEE754, eFormatFloat, 42, { LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM }}, + { "s17", NULL, 4, 244, eEncodingIEEE754, eFormatFloat, 43, { LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM }}, + { "s18", NULL, 4, 248, eEncodingIEEE754, eFormatFloat, 44, { LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM }}, + { "s19", NULL, 4, 252, eEncodingIEEE754, eFormatFloat, 45, { LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM }}, + { "s20", NULL, 4, 256, eEncodingIEEE754, eFormatFloat, 46, { LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM }}, + { "s21", NULL, 4, 260, eEncodingIEEE754, eFormatFloat, 47, { LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM }}, + { "s22", NULL, 4, 264, eEncodingIEEE754, eFormatFloat, 48, { LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM }}, + { "s23", NULL, 4, 268, eEncodingIEEE754, eFormatFloat, 49, { LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM }}, + { "s24", NULL, 4, 272, eEncodingIEEE754, eFormatFloat, 50, { LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM }}, + { "s25", NULL, 4, 276, eEncodingIEEE754, eFormatFloat, 51, { LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM }}, + { "s26", NULL, 4, 280, eEncodingIEEE754, eFormatFloat, 52, { LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM }}, + { "s27", NULL, 4, 284, eEncodingIEEE754, eFormatFloat, 53, { LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM }}, + { "s28", NULL, 4, 288, eEncodingIEEE754, eFormatFloat, 54, { LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM }}, + { "s29", NULL, 4, 292, eEncodingIEEE754, eFormatFloat, 55, { LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM }}, + { "s30", NULL, 4, 296, eEncodingIEEE754, eFormatFloat, 56, { LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM }}, + { "s31", NULL, 4, 300, eEncodingIEEE754, eFormatFloat, 57, { LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM }}, + { "fpscr", NULL, 4, 304, eEncodingUint, eFormatHex, 58, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM }}, + { "d16", NULL, 8, 308, eEncodingIEEE754, eFormatFloat, 59, { LLDB_INVALID_REGNUM, dwarf_d16, LLDB_INVALID_REGNUM }}, + { "d17", NULL, 8, 316, eEncodingIEEE754, eFormatFloat, 60, { LLDB_INVALID_REGNUM, dwarf_d17, LLDB_INVALID_REGNUM }}, + { "d18", NULL, 8, 324, eEncodingIEEE754, eFormatFloat, 61, { LLDB_INVALID_REGNUM, dwarf_d18, LLDB_INVALID_REGNUM }}, + { "d19", NULL, 8, 332, eEncodingIEEE754, eFormatFloat, 62, { LLDB_INVALID_REGNUM, dwarf_d19, LLDB_INVALID_REGNUM }}, + { "d20", NULL, 8, 340, eEncodingIEEE754, eFormatFloat, 63, { LLDB_INVALID_REGNUM, dwarf_d20, LLDB_INVALID_REGNUM }}, + { "d21", NULL, 8, 348, eEncodingIEEE754, eFormatFloat, 64, { LLDB_INVALID_REGNUM, dwarf_d21, LLDB_INVALID_REGNUM }}, + { "d22", NULL, 8, 356, eEncodingIEEE754, eFormatFloat, 65, { LLDB_INVALID_REGNUM, dwarf_d22, LLDB_INVALID_REGNUM }}, + { "d23", NULL, 8, 364, eEncodingIEEE754, eFormatFloat, 66, { LLDB_INVALID_REGNUM, dwarf_d23, LLDB_INVALID_REGNUM }}, + { "d24", NULL, 8, 372, eEncodingIEEE754, eFormatFloat, 67, { LLDB_INVALID_REGNUM, dwarf_d24, LLDB_INVALID_REGNUM }}, + { "d25", NULL, 8, 380, eEncodingIEEE754, eFormatFloat, 68, { LLDB_INVALID_REGNUM, dwarf_d25, LLDB_INVALID_REGNUM }}, + { "d26", NULL, 8, 388, eEncodingIEEE754, eFormatFloat, 69, { LLDB_INVALID_REGNUM, dwarf_d26, LLDB_INVALID_REGNUM }}, + { "d27", NULL, 8, 396, eEncodingIEEE754, eFormatFloat, 70, { LLDB_INVALID_REGNUM, dwarf_d27, LLDB_INVALID_REGNUM }}, + { "d28", NULL, 8, 404, eEncodingIEEE754, eFormatFloat, 71, { LLDB_INVALID_REGNUM, dwarf_d28, LLDB_INVALID_REGNUM }}, + { "d29", NULL, 8, 412, eEncodingIEEE754, eFormatFloat, 72, { LLDB_INVALID_REGNUM, dwarf_d29, LLDB_INVALID_REGNUM }}, + { "d30", NULL, 8, 420, eEncodingIEEE754, eFormatFloat, 73, { LLDB_INVALID_REGNUM, dwarf_d30, LLDB_INVALID_REGNUM }}, + { "d31", NULL, 8, 428, eEncodingIEEE754, eFormatFloat, 74, { LLDB_INVALID_REGNUM, dwarf_d31, LLDB_INVALID_REGNUM }}, + }; + static const uint32_t num_registers = sizeof (g_register_infos)/sizeof (lldb::RegisterInfo); + static ConstString gpr_reg_set ("General Purpose Registers"); + static ConstString vfp_reg_set ("Floating Point Registers"); + for (uint32_t i=0; i<num_registers; ++i) + { + ConstString name; + ConstString alt_name; + if (g_register_infos[i].name && g_register_infos[i].name[0]) + name.SetCString(g_register_infos[i].name); + if (g_register_infos[i].alt_name && g_register_infos[i].alt_name[0]) + alt_name.SetCString(g_register_infos[i].alt_name); + + AddRegister (g_register_infos[i], name, alt_name, i < 26 ? gpr_reg_set : vfp_reg_set); + } +} + diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h new file mode 100644 index 00000000000..67acdefdce0 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h @@ -0,0 +1,250 @@ +//===-- GDBRemoteRegisterContext.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_GDBRemoteRegisterContext_h_ +#define lldb_GDBRemoteRegisterContext_h_ + +// C Includes +// C++ Includes +#include <vector> + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Target/RegisterContext.h" + + +class ThreadGDBRemote; +class ProcessGDBRemote; + +class GDBRemoteDynamicRegisterInfo +{ +public: + GDBRemoteDynamicRegisterInfo () : + m_regs (), + m_sets (), + m_set_reg_nums (), + m_reg_names (), + m_reg_alt_names (), + m_set_names (), + m_reg_data_byte_size (0) + { + } + + ~GDBRemoteDynamicRegisterInfo () + { + } + + void + AddRegister (lldb::RegisterInfo ®_info, lldb_private::ConstString ®_name, lldb_private::ConstString ®_alt_name, lldb_private::ConstString &set_name) + { + const uint32_t reg_num = m_regs.size(); + m_reg_names.push_back (reg_name); + m_reg_alt_names.push_back (reg_alt_name); + reg_info.name = reg_name.AsCString(); + assert (reg_info.name); + reg_info.alt_name = reg_alt_name.AsCString(NULL); + m_regs.push_back (reg_info); + uint32_t set = GetRegisterSetIndexByName (set_name, true); + assert (set < m_sets.size()); + assert (set < m_set_reg_nums.size()); + assert (set < m_set_names.size()); + m_set_reg_nums[set].push_back(reg_num); + size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size; + if (m_reg_data_byte_size < end_reg_offset) + m_reg_data_byte_size = end_reg_offset; + } + + void + Finalize () + { + for (uint32_t set = 0; set < m_sets.size(); ++set) + { + assert (m_sets.size() == m_set_reg_nums.size()); + m_sets[set].num_registers = m_set_reg_nums[set].size(); + m_sets[set].registers = &m_set_reg_nums[set][0]; + } + } + + size_t + GetNumRegisters() const + { + return m_regs.size(); + } + + size_t + GetNumRegisterSets() const + { + return m_sets.size(); + } + + size_t + GetRegisterDataByteSize() const + { + return m_reg_data_byte_size; + } + + const lldb::RegisterInfo * + GetRegisterInfoAtIndex (uint32_t i) const + { + if (i < m_regs.size()) + return &m_regs[i]; + return NULL; + } + + const lldb::RegisterSet * + GetRegisterSet (uint32_t i) const + { + if (i < m_sets.size()) + return &m_sets[i]; + return NULL; + } + + uint32_t + GetRegisterSetIndexByName (lldb_private::ConstString &set_name, bool can_create) + { + name_collection::iterator pos, end = m_set_names.end(); + for (pos = m_set_names.begin(); pos != end; ++pos) + { + if (*pos == set_name) + return std::distance (m_set_names.begin(), pos); + } + + m_set_names.push_back(set_name); + m_set_reg_nums.resize(m_set_reg_nums.size()+1); + lldb::RegisterSet new_set = { set_name.AsCString(), NULL, 0, NULL }; + m_sets.push_back (new_set); + return m_sets.size() - 1; + } + + uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) const + { + reg_collection::const_iterator pos, end = m_regs.end(); + for (pos = m_regs.begin(); pos != end; ++pos) + { + if (pos->kinds[kind] == num) + return std::distance (m_regs.begin(), pos); + } + + return LLDB_INVALID_REGNUM; + } + void + Clear() + { + m_regs.clear(); + m_sets.clear(); + m_set_reg_nums.clear(); + m_reg_names.clear(); + m_reg_alt_names.clear(); + m_set_names.clear(); + } + + void + HardcodeARMRegisters(); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from GDBRemoteRegisterContext can see and modify these + //------------------------------------------------------------------ + typedef std::vector <lldb::RegisterInfo> reg_collection; + typedef std::vector <lldb::RegisterSet> set_collection; + typedef std::vector <uint32_t> reg_num_collection; + typedef std::vector <reg_num_collection> set_reg_num_collection; + typedef std::vector <lldb_private::ConstString> name_collection; + + reg_collection m_regs; + set_collection m_sets; + set_reg_num_collection m_set_reg_nums; + name_collection m_reg_names; + name_collection m_reg_alt_names; + name_collection m_set_names; + size_t m_reg_data_byte_size; // The number of bytes required to store all registers +}; + +class GDBRemoteRegisterContext : public lldb_private::RegisterContext +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + GDBRemoteRegisterContext (ThreadGDBRemote &thread, + lldb_private::StackFrame *frame, + GDBRemoteDynamicRegisterInfo ®_info, + bool read_all_at_once); + + virtual + ~GDBRemoteRegisterContext (); + + //------------------------------------------------------------------ + // 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); + +protected: + + void + SetAllRegisterValid (bool b); + + ProcessGDBRemote & + GetGDBProcess(); + + ThreadGDBRemote & + GetGDBThread(); + + GDBRemoteDynamicRegisterInfo &m_reg_info; + std::vector<bool> m_reg_valid; + uint32_t m_reg_valid_stop_id; + lldb_private::DataExtractor m_reg_data; + bool m_read_all_at_once; + +private: + //------------------------------------------------------------------ + // For GDBRemoteRegisterContext only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (GDBRemoteRegisterContext); +}; + +#endif // lldb_GDBRemoteRegisterContext_h_ diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBServer.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBServer.cpp new file mode 100644 index 00000000000..a88ec7b09d4 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/GDBServer.cpp @@ -0,0 +1,1148 @@ +//===-- GDBServer.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <sys/socket.h> +#include <sys/types.h> +#include <errno.h> +#include <getopt.h> +#include <netinet/in.h> +#include <sys/select.h> +#include <sys/sysctl.h> +#include <string> +#include <vector> +#include <asl.h> + +#include "GDBServerLog.h" +#include "GDBRemoteSession.h" + +using namespace lldb; + +//---------------------------------------------------------------------- +// Run loop modes which determine which run loop function will be called +//---------------------------------------------------------------------- +typedef enum +{ + eDCGSRunLoopModeInvalid = 0, + eDCGSRunLoopModeGetStartModeFromRemoteProtocol, + eDCGSRunLoopModeInferiorAttaching, + eDCGSRunLoopModeInferiorLaunching, + eDCGSRunLoopModeInferiorExecuting, + eDCGSRunLoopModeInferiorKillOrDetach, + eDCGSRunLoopModeExit +} GSRunLoopMode; + +typedef enum +{ + eLaunchFlavorDefault = 0, + eLaunchFlavorPosixSpawn, +#if defined (__arm__) + eLaunchFlavorSpringBoard, +#endif + eLaunchFlavorForkExec, +} GSLaunchFlavor; + +typedef lldb::shared_ptr<GDBRemoteSession> GDBRemoteSP; + +typedef struct HandleBroadcastEventInfo +{ + TargetSP target_sp; + GDBRemoteSP remote_sp; + GSRunLoopMode mode; + + Target * + GetTarget () + { + return target_sp.get(); + } + + Process * + GetProcess() + { + if (target_sp.get()) + return target_sp->GetProcess().get(); + return NULL; + } + + GDBRemoteSession * + GetRemote () + { + return remote_sp.get(); + } + +}; + + +//---------------------------------------------------------------------- +// Global Variables +//---------------------------------------------------------------------- +static int g_lockdown_opt = 0; +static int g_applist_opt = 0; +static GSLaunchFlavor g_launch_flavor = eLaunchFlavorDefault; +int g_isatty = 0; + +//---------------------------------------------------------------------- +// Run Loop function prototypes +//---------------------------------------------------------------------- +void GSRunLoopGetStartModeFromRemote (HandleBroadcastEventInfo *info); +void GSRunLoopInferiorExecuting (HandleBroadcastEventInfo *info); + + +//---------------------------------------------------------------------- +// Get our program path and arguments from the remote connection. +// We will need to start up the remote connection without a PID, get the +// arguments, wait for the new process to finish launching and hit its +// entry point, and then return the run loop mode that should come next. +//---------------------------------------------------------------------- +void +GSRunLoopGetStartModeFromRemote (HandleBroadcastEventInfo *info) +{ + std::string packet; + + Target *target = info->GetTarget(); + GDBRemoteSession *remote = info->GetRemote(); + if (target != NULL && remote != NULL) + { + // Spin waiting to get the A packet. + while (1) + { + gdb_err_t err = gdb_err; + GDBRemoteSession::PacketEnum type; + + err = remote->HandleReceivedPacket (&type); + + // check if we tried to attach to a process + if (type == GDBRemoteSession::vattach || type == GDBRemoteSession::vattachwait) + { + if (err == gdb_success) + { + info->mode = eDCGSRunLoopModeInferiorExecuting; + return; + } + else + { + Log::STDERR ("error: attach failed."); + info->mode = eDCGSRunLoopModeExit; + return; + } + } + + if (err == gdb_success) + { + // If we got our arguments we are ready to launch using the arguments + // and any environment variables we received. + if (type == GDBRemoteSession::set_argv) + { + info->mode = eDCGSRunLoopModeInferiorLaunching; + return; + } + } + else if (err == gdb_not_connected) + { + Log::STDERR ("error: connection lost."); + info->mode = eDCGSRunLoopModeExit; + return; + } + else + { + // a catch all for any other gdb remote packets that failed + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Error getting packet.",__FUNCTION__); + continue; + } + + GDBServerLog::LogIf (GS_LOG_MINIMAL, "#### %s", __FUNCTION__); + } + } + info->mode = eDCGSRunLoopModeExit; +} + + +//---------------------------------------------------------------------- +// This run loop mode will wait for the process to launch and hit its +// entry point. It will currently ignore all events except for the +// process state changed event, where it watches for the process stopped +// or crash process state. +//---------------------------------------------------------------------- +GSRunLoopMode +GSRunLoopLaunchInferior (HandleBroadcastEventInfo *info) +{ + // The Process stuff takes a c array, the GSContext has a vector... + // So make up a c array. + Target *target = info->GetTarget(); + GDBRemoteSession *remote = info->GetRemote(); + Process* process = info->GetProcess(); + + if (process == NULL) + return eDCGSRunLoopModeExit; + + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Launching '%s'...", __FUNCTION__, target->GetExecutableModule()->GetFileSpec().GetFilename().AsCString()); + + // Our launch type hasn't been set to anything concrete, so we need to + // figure our how we are going to launch automatically. + + GSLaunchFlavor launch_flavor = g_launch_flavor; + if (launch_flavor == eLaunchFlavorDefault) + { + // Our default launch method is posix spawn + launch_flavor = eLaunchFlavorPosixSpawn; + +#if defined (__arm__) + // Check if we have an app bundle, if so launch using SpringBoard. + if (strstr(inferior_argv[0], ".app")) + { + launch_flavor = eLaunchFlavorSpringBoard; + } +#endif + } + + //ctx.SetLaunchFlavor(launch_flavor); + + const char *stdio_file = NULL; + lldb::pid_t pid = process->Launch (remote->GetARGV(), remote->GetENVP(), stdio_file, stdio_file, stdio_file); + + if (pid == LLDB_INVALID_PROCESS_ID) + { + Log::STDERR ("error: process launch failed: %s", process->GetError().AsCString()); + } + else + { + if (remote->IsConnected()) + { + // It we are connected already, the next thing gdb will do is ask + // whether the launch succeeded, and if not, whether there is an + // error code. So we need to fetch one packet from gdb before we wait + // on the stop from the target. + gdb_err_t err = gdb_err; + GDBRemoteSession::PacketEnum type; + + err = remote->HandleReceivedPacket (&type); + + if (err != gdb_success) + { + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Error getting packet.", __FUNCTION__); + return eDCGSRunLoopModeExit; + } + if (type != GDBRemoteSession::query_launch_success) + { + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Didn't get the expected qLaunchSuccess packet.", __FUNCTION__); + } + } + } + + Listener listener("GSRunLoopLaunchInferior"); + listener.StartListeningForEvents (process, Process::eBroadcastBitStateChanged); + while (process->GetID() != LLDB_INVALID_PROCESS_ID) + { + uint32_t event_mask = 0; + while (listener.WaitForEvent(NULL, &event_mask)) + { + if (event_mask & Process::eBroadcastBitStateChanged) + { + Event event; + StateType event_state; + while ((event_state = process->GetNextEvent (&event))) + if (StateIsStoppedState(event_state)) + { + GDBServerLog::LogIf (GS_LOG_EVENTS, "%s process %4.4x stopped with state %s", __FUNCTION__, pid, StateAsCString(event_state)); + + switch (event_state) + { + default: + case eStateInvalid: + case eStateUnloaded: + case eStateAttaching: + case eStateLaunching: + case eStateSuspended: + break; // Ignore + + case eStateRunning: + case eStateStepping: + // Still waiting to stop at entry point... + break; + + case eStateStopped: + case eStateCrashed: + return eDCGSRunLoopModeInferiorExecuting; + + case eStateDetached: + case eStateExited: + pid = LLDB_INVALID_PROCESS_ID; + return eDCGSRunLoopModeExit; + } + } + + if (event_state = eStateInvalid) + break; + } + } + } + + return eDCGSRunLoopModeExit; +} + + +//---------------------------------------------------------------------- +// This run loop mode will wait for the process to launch and hit its +// entry point. It will currently ignore all events except for the +// process state changed event, where it watches for the process stopped +// or crash process state. +//---------------------------------------------------------------------- +GSRunLoopMode +GSRunLoopLaunchAttaching (HandleBroadcastEventInfo *info, lldb::pid_t& pid) +{ + Process* process = info->GetProcess(); + + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Attaching to pid %i...", __FUNCTION__, pid); + pid = process->Attach(pid); + + if (pid == LLDB_INVALID_PROCESS_ID) + return eDCGSRunLoopModeExit; + return eDCGSRunLoopModeInferiorExecuting; +} + +//---------------------------------------------------------------------- +// Watch for signals: +// SIGINT: so we can halt our inferior. (disabled for now) +// SIGPIPE: in case our child process dies +//---------------------------------------------------------------------- +lldb::pid_t g_pid; +int g_sigpipe_received = 0; +void +signal_handler(int signo) +{ + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (%s)", __FUNCTION__, Host::GetSignalAsCString(signo)); + + switch (signo) + { +// case SIGINT: +// DNBProcessKill (g_pid, signo); +// break; + + case SIGPIPE: + g_sigpipe_received = 1; + break; + } +} + +// Return the new run loop mode based off of the current process state +void +HandleProcessStateChange (HandleBroadcastEventInfo *info, bool initialize) +{ + Process *process = info->GetProcess(); + if (process == NULL) + { + info->mode = eDCGSRunLoopModeExit; + return; + } + + if (process->GetID() == LLDB_INVALID_PROCESS_ID) + { + GDBServerLog::LogIf (GS_LOG_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__); + info->mode = eDCGSRunLoopModeExit; + return; + } + StateType pid_state = process->GetState (); + + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (info, initialize=%i) pid_state = %s", __FUNCTION__, (int)initialize, StateAsCString(pid_state)); + + switch (pid_state) + { + case eStateInvalid: + case eStateUnloaded: + // Something bad happened + info->mode = eDCGSRunLoopModeExit; + return; + + case eStateAttaching: + case eStateLaunching: + info->mode = eDCGSRunLoopModeInferiorExecuting; + return; + + case eStateSuspended: + case eStateCrashed: + case eStateStopped: + if (initialize == false) + { + // Compare the last stop count to our current notion of a stop count + // to make sure we don't notify more than once for a given stop. + static uint32_t g_prev_stop_id = 0; + uint32_t stop_id = process->GetStopID(); + bool pid_stop_count_changed = g_prev_stop_id != stop_id; + if (pid_stop_count_changed) + { + info->GetRemote()->FlushSTDIO(); + + if (stop_id == 1) + { + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? no, first stop...", __FUNCTION__, (int)initialize, StateAsCString (pid_state), stop_id, g_prev_stop_id); + } + else + { + + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? YES!!!", __FUNCTION__, (int)initialize, StateAsCString (pid_state), stop_id, g_prev_stop_id); + info->GetRemote()->NotifyThatProcessStopped (); + } + } + else + { + GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? skipping...", __FUNCTION__, (int)initialize, StateAsCString (pid_state), stop_id, g_prev_stop_id); + } + } + info->mode = eDCGSRunLoopModeInferiorExecuting; + return; + + case eStateStepping: + case eStateRunning: + info->mode = eDCGSRunLoopModeInferiorExecuting; + return; + + case eStateExited: + info->GetRemote()->HandlePacket_last_signal (NULL); + info->mode = eDCGSRunLoopModeExit; + return; + + } + + // Catch all... + info->mode = eDCGSRunLoopModeExit; +} + +bool +CommunicationHandleBroadcastEvent (Broadcaster *broadcaster, uint32_t event_mask, void *baton) +{ + HandleBroadcastEventInfo *info = (HandleBroadcastEventInfo *)baton; + Process *process = info->GetProcess(); + + if (process == NULL) + { + info->mode = eDCGSRunLoopModeExit; + return true; + } + + if (event_mask & Communication::eBroadcastBitPacketAvailable) + { + if (process->IsRunning()) + { + if (info->GetRemote()->HandleAsyncPacket() == gdb_not_connected) + info->mode = eDCGSRunLoopModeExit; + } + else + { + if (info->GetRemote()->HandleReceivedPacket() == gdb_not_connected) + info->mode = eDCGSRunLoopModeExit; + } + } + if (event_mask & Communication::eBroadcastBitReadThreadDidExit) + { + info->mode = eDCGSRunLoopModeExit; + } + if (event_mask & Communication::eBroadcastBitDisconnected) + { + info->mode = eDCGSRunLoopModeExit; + } + + return true; + +} + +bool +ProcessHandleBroadcastEvent (Broadcaster *broadcaster, uint32_t event_mask, void *baton) +{ + HandleBroadcastEventInfo *info = (HandleBroadcastEventInfo *)baton; + Process *process = info->GetProcess(); + if (process == NULL) + { + info->mode = eDCGSRunLoopModeExit; + return true; + } + + if (event_mask & Process::eBroadcastBitStateChanged) + { + // Consume all available process events with no timeout + Event event; + StateType process_state; + while ((process_state = process->GetNextEvent (&event)) != eStateInvalid) + { + if (StateIsStoppedState(process_state)) + info->GetRemote()->FlushSTDIO(); + HandleProcessStateChange (info, false); + + if (info->mode != eDCGSRunLoopModeInferiorExecuting) + break; + } + } + else + if (event_mask & (Process::eBroadcastBitSTDOUT | Process::eBroadcastBitSTDERR)) + { + info->GetRemote()->FlushSTDIO(); + } + return true; +} + +// This function handles the case where our inferior program is stopped and +// we are waiting for gdb remote protocol packets. When a packet occurs that +// makes the inferior run, we need to leave this function with a new state +// as the return code. +void +GSRunLoopInferiorExecuting (HandleBroadcastEventInfo *info) +{ + GDBServerLog::LogIf (GS_LOG_MINIMAL, "#### %s", __FUNCTION__); + + // Init our mode and set 'is_running' based on the current process state + HandleProcessStateChange (info, true); + + uint32_t desired_mask, acquired_mask; + Listener listener("GSRunLoopInferiorExecuting"); + + desired_mask = Communication::eBroadcastBitPacketAvailable | + Communication::eBroadcastBitReadThreadDidExit | + Communication::eBroadcastBitDisconnected; + + acquired_mask = listener.StartListeningForEvents (&(info->GetRemote()->GetPacketComm()), + desired_mask, + CommunicationHandleBroadcastEvent, + info); + + assert (acquired_mask == desired_mask); + desired_mask = GDBRemotePacket::eBroadcastBitPacketAvailable; + + acquired_mask = listener.StartListeningForEvents (&(info->GetRemote()->GetPacketComm()), + desired_mask, + CommunicationHandleBroadcastEvent, + info); + + assert (acquired_mask == desired_mask); + + desired_mask = Process::eBroadcastBitStateChanged | + Process::eBroadcastBitSTDOUT | + Process::eBroadcastBitSTDERR ; + acquired_mask = listener.StartListeningForEvents (info->GetProcess (), + desired_mask, + ProcessHandleBroadcastEvent, + info); + + assert (acquired_mask == desired_mask); + + Process *process = info->GetProcess(); + + while (process->IsAlive()) + { + if (!info->GetRemote()->IsConnected()) + { + info->mode = eDCGSRunLoopModeInferiorKillOrDetach; + break; + } + + // We want to make sure we consume all process state changes and have + // whomever is notifying us to wait for us to reset the event bit before + // continuing. + //ctx.Events().SetResetAckMask (GSContext::event_proc_state_changed); + uint32_t event_mask = 0; + Broadcaster *broadcaster = listener.WaitForEvent(NULL, &event_mask); + if (broadcaster) + { + listener.HandleBroadcastEvent(broadcaster, event_mask); + } + } +} + + +//---------------------------------------------------------------------- +// Convenience function to set up the remote listening port +// Returns 1 for success 0 for failure. +//---------------------------------------------------------------------- + +static bool +StartListening (HandleBroadcastEventInfo *info, int listen_port) +{ + if (!info->GetRemote()->IsConnected()) + { + Log::STDOUT ("Listening to port %i...\n", listen_port); + char connect_url[256]; + snprintf(connect_url, sizeof(connect_url), "listen://%i", listen_port); + + Communication &comm = info->remote_sp->GetPacketComm(); + comm.SetConnection (new ConnectionFileDescriptor); + + if (comm.Connect (connect_url)) + { + if (comm.StartReadThread()) + return true; + + Log::STDERR ("Failed to start the communication read thread.\n", connect_url); + comm.Disconnect(); + } + else + { + Log::STDERR ("Failed to connection to %s.\n", connect_url); + } + return false; + } + return true; +} + +//---------------------------------------------------------------------- +// ASL Logging callback that can be registered with DNBLogSetLogDCScriptInterpreter::Type +//---------------------------------------------------------------------- +//void +//ASLLogDCScriptInterpreter::Type(void *baton, uint32_t flags, const char *format, va_list args) +//{ +// if (format == NULL) +// return; +// static aslmsg g_aslmsg = NULL; +// if (g_aslmsg == NULL) +// { +// g_aslmsg = ::asl_new (ASL_TYPE_MSG); +// char asl_key_sender[PATH_MAX]; +// snprintf(asl_key_sender, sizeof(asl_key_sender), "com.apple.dc-gdbserver-%g", dc_gdbserverVersionNumber); +// ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender); +// } +// +// int asl_level; +// if (flags & DNBLOG_FLAG_FATAL) asl_level = ASL_LEVEL_CRIT; +// else if (flags & DNBLOG_FLAG_ERROR) asl_level = ASL_LEVEL_ERR; +// else if (flags & DNBLOG_FLAG_WARNING) asl_level = ASL_LEVEL_WARNING; +// else if (flags & DNBLOG_FLAG_VERBOSE) asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_INFO; +// else asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_DEBUG; +// +// ::asl_vlog (NULL, g_aslmsg, asl_level, format, args); +//} + +//---------------------------------------------------------------------- +// FILE based Logging callback that can be registered with +// DNBLogSetLogDCScriptInterpreter::Type +//---------------------------------------------------------------------- +void +FileLogDCScriptInterpreter::Type(void *baton, uint32_t flags, const char *format, va_list args) +{ + if (baton == NULL || format == NULL) + return; + + ::vfprintf ((FILE *)baton, format, args); + ::fprintf ((FILE *)baton, "\n"); +} + +//---------------------------------------------------------------------- +// option descriptors for getopt_long() +//---------------------------------------------------------------------- +static struct option g_long_options[] = +{ + { "arch", required_argument, NULL, 'c' }, + { "attach", required_argument, NULL, 'a' }, + { "debug", no_argument, NULL, 'g' }, + { "verbose", no_argument, NULL, 'v' }, + { "lockdown", no_argument, &g_lockdown_opt, 1 }, // short option "-k" + { "applist", no_argument, &g_applist_opt, 1 }, // short option "-t" + { "log-file", required_argument, NULL, 'l' }, + { "log-flags", required_argument, NULL, 'f' }, + { "launch", required_argument, NULL, 'x' }, // Valid values are "auto", "posix-spawn", "fork-exec", "springboard" (arm only) + { "waitfor", required_argument, NULL, 'w' }, // Wait for a process whose namet starts with ARG + { "waitfor-interval", required_argument, NULL, 'i' }, // Time in usecs to wait between sampling the pid list when waiting for a process by name + { "waitfor-duration", required_argument, NULL, 'd' }, // The time in seconds to wait for a process to show up by name + { NULL, 0, NULL, 0 } +}; + +extern const double dc_gdbserverVersionNumber; +int +main (int argc, char *argv[]) +{ + Initialize(); + Host::ThreadCreated ("[main]"); + + g_isatty = ::isatty (STDIN_FILENO); + +// signal (SIGINT, signal_handler); + signal (SIGPIPE, signal_handler); + + Log *log = GDBServerLog::GetLogIfAllCategoriesSet(GS_LOG_ALL); + const char *this_exe_name = argv[0]; + int i; + int attach_pid = LLDB_INVALID_PROCESS_ID; + for (i=0; i<argc; i++) + GDBServerLog::LogIf(GS_LOG_DEBUG, "argv[%i] = %s", i, argv[i]); + + FILE* log_file = NULL; + uint32_t log_flags = 0; + // Parse our options + int ch; + int long_option_index = 0; + int debug = 0; + std::string waitfor_pid_name; // Wait for a process that starts with this name + std::string attach_pid_name; + useconds_t waitfor_interval = 1000; // Time in usecs between process lists polls when waiting for a process by name, default 1 msec. + useconds_t waitfor_duration = 0; // Time in seconds to wait for a process by name, 0 means wait forever. + ArchSpec arch; + GSRunLoopMode start_mode = eDCGSRunLoopModeExit; + + while ((ch = getopt_long(argc, argv, "a:c:d:gi:vktl:f:w:x:", g_long_options, &long_option_index)) != -1) + { +// DNBLogDebug("option: ch == %c (0x%2.2x) --%s%c%s\n", +// ch, (uint8_t)ch, +// g_long_options[long_option_index].name, +// g_long_options[long_option_index].has_arg ? '=' : ' ', +// optarg ? optarg : ""); + switch (ch) + { + case 0: // Any optional that auto set themselves will return 0 + break; + + case 'c': + arch.SetArch(optarg); + if (!arch.IsValid()) + { + Log::STDERR ("error: invalid arch string '%s'\n", optarg); + exit (8); + } + break; + + case 'a': + if (optarg && optarg[0]) + { + if (isdigit(optarg[0])) + { + char *end = NULL; + attach_pid = strtoul(optarg, &end, 0); + if (end == NULL || *end != '\0') + { + Log::STDERR ("error: invalid pid option '%s'\n", optarg); + exit (4); + } + } + else + { + attach_pid_name = optarg; + } + start_mode = eDCGSRunLoopModeInferiorAttaching; + } + break; + + // --waitfor=NAME + case 'w': + if (optarg && optarg[0]) + { + waitfor_pid_name = optarg; + start_mode = eDCGSRunLoopModeInferiorAttaching; + } + break; + + // --waitfor-interval=USEC + case 'i': + if (optarg && optarg[0]) + { + char *end = NULL; + waitfor_interval = strtoul(optarg, &end, 0); + if (end == NULL || *end != '\0') + { + Log::STDERR ("error: invalid waitfor-interval option value '%s'.\n", optarg); + exit (6); + } + } + break; + + // --waitfor-duration=SEC + case 'd': + if (optarg && optarg[0]) + { + char *end = NULL; + waitfor_duration = strtoul(optarg, &end, 0); + if (end == NULL || *end != '\0') + { + Log::STDERR ("error: invalid waitfor-duration option value '%s'.\n", optarg); + exit (7); + } + } + break; + + case 'x': + if (optarg && optarg[0]) + { + if (strcasecmp(optarg, "auto") == 0) + g_launch_flavor = eLaunchFlavorDefault; + else if (strcasestr(optarg, "posix") == optarg) + g_launch_flavor = eLaunchFlavorPosixSpawn; + else if (strcasestr(optarg, "fork") == optarg) + g_launch_flavor = eLaunchFlavorForkExec; +#if defined (__arm__) + else if (strcasestr(optarg, "spring") == optarg) + g_launch_flavor = eLaunchFlavorSpringBoard; +#endif + else + { + Log::STDERR ("error: invalid TYPE for the --launch=TYPE (-x TYPE) option: '%s'\n", optarg); + Log::STDERR ("Valid values TYPE are:\n"); + Log::STDERR (" auto Auto-detect the best launch method to use.\n"); + Log::STDERR (" posix Launch the executable using posix_spawn.\n"); + Log::STDERR (" fork Launch the executable using fork and exec.\n"); +#if defined (__arm__) + Log::STDERR (" spring Launch the executable through Springboard.\n"); +#endif + exit (5); + } + } + break; + + case 'l': // Set Log File + if (optarg && optarg[0]) + { + if (strcasecmp(optarg, "stdout") == 0) + log_file = stdout; + else if (strcasecmp(optarg, "stderr") == 0) + log_file = stderr; + else + log_file = fopen(optarg, "w+"); + + if (log_file == NULL) + { + const char *errno_str = strerror(errno); + Log::STDERR ("Failed to open log file '%s' for writing: errno = %i (%s)", optarg, errno, errno_str ? errno_str : "unknown error"); + } + } + break; + + case 'f': // Log Flags + if (optarg && optarg[0]) + log_flags = strtoul(optarg, NULL, 0); + break; + + case 'g': + debug = 1; + //DNBLogSetDebug(1); + break; + + case 't': + g_applist_opt = 1; + break; + + case 'k': + g_lockdown_opt = 1; + break; + + case 'v': + //DNBLogSetVerbose(1); + break; + } + } + + // Skip any options we consumed with getopt_long + argc -= optind; + argv += optind; + + // It is ok for us to set NULL as the logfile (this will disable any logging) + +// if (log_file != NULL) +// { +// DNBLogSetLogDCScriptInterpreter::Type(FileLogDCScriptInterpreter::Type, log_file); +// // If our log file was set, yet we have no log flags, log everything! +// if (log_flags == 0) +// log_flags = LOG_ALL | LOG_DCGS_ALL; +// +// DNBLogSetLogMask (log_flags); +// } +// else +// { +// // Enable DNB logging +// DNBLogSetLogDCScriptInterpreter::Type(ASLLogDCScriptInterpreter::Type, NULL); +// DNBLogSetLogMask (log_flags); +// +// } + + // as long as we're dropping remotenub in as a replacement for gdbserver, + // explicitly note that this is not gdbserver. + + Log::STDOUT ("debugserver-%g \n", dc_gdbserverVersionNumber); + int listen_port = -1; + if (g_lockdown_opt == 0 && g_applist_opt == 0) + { + // Make sure we at least have port + if (argc < 1) + { + Log::STDERR ("Usage: %s host:port [program-name program-arg1 program-arg2 ...]\n", this_exe_name); + exit (1); + } + // accept 'localhost:' prefix on port number + + std::string host_str; + std::string port_str(argv[0]); + + // We just used the host:port arg... + argc--; + argv++; + + size_t port_idx = port_str.find(':'); + if (port_idx != std::string::npos) + { + host_str.assign(port_str, 0, port_idx); + port_str.erase(0, port_idx + 1); + } + + if (port_str.empty()) + { + Log::STDERR ("error: no port specified\nUsage: %s host:port [program-name program-arg1 program-arg2 ...]\n", this_exe_name); + exit (2); + } + else if (port_str.find_first_not_of("0123456789") != std::string::npos) + { + Log::STDERR ("error: port must be an integer: %s\nUsage: %s host:port [program-name program-arg1 program-arg2 ...]\n", port_str.c_str(), this_exe_name); + exit (3); + } + //DNBLogDebug("host_str = '%s' port_str = '%s'", host_str.c_str(), port_str.c_str()); + listen_port = atoi (port_str.c_str()); + } + + + // We must set up some communications now. + + FileSpec exe_spec; + if (argv[0]) + exe_spec.SetFile (argv[0]); + + HandleBroadcastEventInfo info; + info.target_sp = TargetList::SharedList().CreateTarget(&exe_spec, &arch); + ProcessSP process_sp (info.target_sp->CreateProcess ()); + info.remote_sp.reset (new GDBRemoteSession (process_sp)); + + info.remote_sp->SetLog (log); + StreamString sstr; + sstr.Printf("ConnectionFileDescriptor(%s)", argv[0]); + + if (info.remote_sp.get() == NULL) + { + Log::STDERR ("error: failed to create a GDBRemoteSession class\n"); + return -1; + } + + + + // If we know we're waiting to attach, we don't need any of this other info. + if (start_mode != eDCGSRunLoopModeInferiorAttaching) + { + if (argc == 0 || g_lockdown_opt) + { + if (g_lockdown_opt != 0) + { + // Work around for SIGPIPE crashes due to posix_spawn issue. We have to close + // STDOUT and STDERR, else the first time we try and do any, we get SIGPIPE and + // die as posix_spawn is doing bad things with our file descriptors at the moment. + int null = open("/dev/null", O_RDWR); + dup2(null, STDOUT_FILENO); + dup2(null, STDERR_FILENO); + } + else if (g_applist_opt != 0) + { +// // List all applications we are able to see +// std::string applist_plist; +// int err = ListApplications(applist_plist, false, false); +// if (err == 0) +// { +// fputs (applist_plist.c_str(), stdout); +// } +// else +// { +// Log::STDERR ("error: ListApplications returned error %i\n", err); +// } +// // Exit with appropriate error if we were asked to list the applications +// // with no other args were given (and we weren't trying to do this over +// // lockdown) +// return err; + return 0; + } + + //DNBLogDebug("Get args from remote protocol..."); + start_mode = eDCGSRunLoopModeGetStartModeFromRemoteProtocol; + } + else + { + start_mode = eDCGSRunLoopModeInferiorLaunching; + // Fill in the argv array in the context from the rest of our args. + // Skip the name of this executable and the port number + info.remote_sp->SetArguments (argc, argv); + } + } + + if (start_mode == eDCGSRunLoopModeExit) + return -1; + + info.mode = start_mode; + + while (info.mode != eDCGSRunLoopModeExit) + { + switch (info.mode) + { + case eDCGSRunLoopModeGetStartModeFromRemoteProtocol: + #if defined (__arm__) + if (g_lockdown_opt) + { + if (!info.remote_sp->GetCommunication()->IsConnected()) + { + if (info.remote_sp->GetCommunication()->ConnectToService () != gdb_success) + { + Log::STDERR ("Failed to get connection from a remote gdb process.\n"); + info.mode = eDCGSRunLoopModeExit; + } + else if (g_applist_opt != 0) + { + // List all applications we are able to see + std::string applist_plist; + if (ListApplications(applist_plist, false, false) == 0) + { + //DNBLogDebug("Task list: %s", applist_plist.c_str()); + + info.remote_sp->GetCommunication()->Write(applist_plist.c_str(), applist_plist.size()); + // Issue a read that will never yield any data until the other side + // closes the socket so this process doesn't just exit and cause the + // socket to close prematurely on the other end and cause data loss. + std::string buf; + info.remote_sp->GetCommunication()->Read(buf); + } + info.remote_sp->GetCommunication()->Disconnect(false); + info.mode = eDCGSRunLoopModeExit; + break; + } + else + { + // Start watching for remote packets + info.remote_sp->StartReadRemoteDataThread(); + } + } + } + else +#endif + { + if (StartListening (&info, listen_port)) + Log::STDOUT ("Got a connection, waiting for process information for launching or attaching.\n"); + else + info.mode = eDCGSRunLoopModeExit; + } + + if (info.mode != eDCGSRunLoopModeExit) + GSRunLoopGetStartModeFromRemote (&info); + break; + + case eDCGSRunLoopModeInferiorAttaching: + if (!waitfor_pid_name.empty()) + { + // Set our end wait time if we are using a waitfor-duration + // option that may have been specified + + TimeValue attach_timeout_abstime; + if (waitfor_duration != 0) + { + attach_timeout_abstime = TimeValue::Now(); + attach_timeout_abstime.OffsetWithSeconds (waitfor_duration); + } + GSLaunchFlavor launch_flavor = g_launch_flavor; + if (launch_flavor == eLaunchFlavorDefault) + { + // Our default launch method is posix spawn + launch_flavor = eLaunchFlavorPosixSpawn; + +#if defined (__arm__) + // Check if we have an app bundle, if so launch using SpringBoard. + if (waitfor_pid_name.find (".app") != std::string::npos) + { + launch_flavor = eLaunchFlavorSpringBoard; + } +#endif + } + + //ctx.SetLaunchFlavor(launch_flavor); + + + lldb::pid_t pid = info.GetProcess()->Attach (waitfor_pid_name.c_str()); + if (pid == LLDB_INVALID_PROCESS_ID) + { + info.GetRemote()->GetLaunchError() = info.GetProcess()->GetError(); + Log::STDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), info.GetRemote()->GetLaunchError().AsCString()); + info.mode = eDCGSRunLoopModeExit; + } + else + { + info.mode = eDCGSRunLoopModeInferiorExecuting; + } + } + else if (attach_pid != LLDB_INVALID_PROCESS_ID) + { + Log::STDOUT ("Attaching to process %i...\n", attach_pid); + info.mode = GSRunLoopLaunchAttaching (&info, attach_pid); + if (info.mode != eDCGSRunLoopModeInferiorExecuting) + { + const char *error_str = info.GetRemote()->GetLaunchError().AsCString(); + Log::STDERR ("error: failed to attach process %i: %s\n", attach_pid, error_str ? error_str : "unknown error."); + info.mode = eDCGSRunLoopModeExit; + } + } + else if (!attach_pid_name.empty ()) + { + lldb::pid_t pid = info.GetProcess()->Attach (waitfor_pid_name.c_str()); + if (pid == LLDB_INVALID_PROCESS_ID) + { + info.GetRemote()->GetLaunchError() = info.GetProcess()->GetError(); + Log::STDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), info.GetRemote()->GetLaunchError().AsCString()); + info.mode = eDCGSRunLoopModeExit; + } + else + { + info.mode = eDCGSRunLoopModeInferiorExecuting; + } + } + else + { + Log::STDERR ("error: asked to attach with empty name and invalid PID."); + info.mode = eDCGSRunLoopModeExit; + } + + if (info.mode != eDCGSRunLoopModeExit) + { + if (StartListening (&info, listen_port)) + Log::STDOUT ("Got a connection, waiting for debugger instructions for process %d.\n", attach_pid); + else + info.mode = eDCGSRunLoopModeExit; + } + break; + + case eDCGSRunLoopModeInferiorLaunching: + info.mode = GSRunLoopLaunchInferior (&info); + + if (info.mode == eDCGSRunLoopModeInferiorExecuting) + { + if (StartListening (&info, listen_port)) + Log::STDOUT ("Got a connection, waiting for debugger instructions for task \"%s\".\n", argv[0]); + else + info.mode = eDCGSRunLoopModeExit; + } + else + { + Log::STDERR ("error: failed to launch process %s: %s\n", argv[0], info.GetRemote()->GetLaunchError().AsCString()); + } + break; + + case eDCGSRunLoopModeInferiorExecuting: + GSRunLoopInferiorExecuting (&info); + break; + + case eDCGSRunLoopModeInferiorKillOrDetach: + { + Process *process = info.GetProcess(); + if (process && process->IsAlive()) + { + process->Kill(SIGCONT); + process->Kill(SIGKILL); + } + } + info.mode = eDCGSRunLoopModeExit; + break; + + default: + info.mode = eDCGSRunLoopModeExit; + case eDCGSRunLoopModeExit: + break; + } + } + + return 0; +} diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.cpp new file mode 100644 index 00000000000..2d4116ebe7b --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.cpp @@ -0,0 +1,80 @@ +//===-- GDBServerLog.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//---------------------------------------------------------------------- +// +// GDBServerLog.cpp +// liblldb +// +// Created by Greg Clayton on 6/19/09. +// +// +//---------------------------------------------------------------------- + +#include "GDBServerLog.h" + +using namespace lldb; + +static Log * +LogAccessor (bool get, Log *log) +{ + static Log* g_log = NULL; // Leak for now as auto_ptr was being cleaned up + // by global constructors before other threads + // were done with it. + if (get) + { +// // Debug code below for enabling logging by default +// if (g_log == NULL) +// { +// g_log = new Log("/dev/stdout", false); +// g_log->GetMask().SetAllFlagBits(GS_LOG_ALL); +// g_log->GetOptions().Set(LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_THREAD_NAME); +// } + } + else + { + if (g_log) + delete g_log; + g_log = log; + } + + return g_log; +} + +Log * +GDBServerLog::GetLogIfAllCategoriesSet (uint32_t mask) +{ + Log *log = LogAccessor (true, NULL); + if (log && mask) + { + uint32_t log_mask = log->GetMask().GetAllFlagBits(); + if ((log_mask & mask) != mask) + return NULL; + } + return log; +} + +void +GDBServerLog::SetLog (Log *log) +{ + LogAccessor (false, log); +} + + +void +GDBServerLog::LogIf (uint32_t mask, const char *format, ...) +{ + Log *log = GDBServerLog::GetLogIfAllCategoriesSet (mask); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.h b/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.h new file mode 100644 index 00000000000..3dec8088cef --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.h @@ -0,0 +1,55 @@ +//===-- GDBServerLog.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//---------------------------------------------------------------------- +// +// GDBServerLog.h +// liblldb +// +// Created by Greg Clayton on 6/19/09. +// +// +//---------------------------------------------------------------------- + +#ifndef liblldb_GDBServerLog_h_ +#define liblldb_GDBServerLog_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +#include "lldb/Core/Log.h" + +// Project includes +#define GS_LOG_VERBOSE (1u << 0) +#define GS_LOG_DEBUG (1u << 1) +#define GS_LOG_PACKETS (1u << 2) +#define GS_LOG_EVENTS (1u << 3) +#define GS_LOG_MINIMAL (1u << 4) +#define GS_LOG_ALL (UINT32_MAX) +#define GS_LOG_DEFAULT (GS_LOG_VERBOSE |\ + GS_LOG_PACKETS) + +namespace lldb { + +class GDBServerLog +{ +public: + static Log * + GetLog (uint32_t mask = 0); + + static void + SetLog (Log *log); + + static void + LogIf (uint32_t mask, const char *format, ...); +}; + +} // namespace lldb + +#endif // liblldb_GDBServerLog_h_ diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp new file mode 100644 index 00000000000..e08679c1e1c --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -0,0 +1,2272 @@ +//===-- ProcessGDBRemote.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 +#include <errno.h> +#include <mach/mach.h> +#include <mach-o/dyld.h> +#include <spawn.h> +#include <sys/fcntl.h> +#include <sys/types.h> +#include <sys/ptrace.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <unistd.h> + +// C++ Includes +#include <algorithm> +#include <map> + +// Other libraries and framework includes + +#include "lldb/Breakpoint/WatchpointLocation.h" +#include "lldb/Core/Args.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/TargetList.h" +#include "PseudoTerminal.h" + +// Project includes +#include "lldb/Host/Host.h" +#include "StringExtractorGDBRemote.h" +#include "GDBRemoteRegisterContext.h" +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" +#include "ThreadGDBRemote.h" +#include "libunwind.h" +#include "MacOSXLibunwindCallbacks.h" + +#if defined (__i386__) || defined (__x86_64__) +#define MACH_EXC_DATA0_SOFTWARE_BREAKPOINT EXC_I386_BPT +#define MACH_EXC_DATA0_TRACE EXC_I386_SGL +#elif defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) +#define MACH_EXC_DATA0_SOFTWARE_BREAKPOINT EXC_PPC_BREAKPOINT +#elif defined (__arm__) +#define MACH_EXC_DATA0_SOFTWARE_BREAKPOINT EXC_ARM_BREAKPOINT +#endif + + +#define DEBUGSERVER_BASENAME "debugserver" +using namespace lldb; +using namespace lldb_private; + +static inline uint16_t +get_random_port () +{ + return (arc4random() % (UINT16_MAX - 1000u)) + 1000u; +} + + +const char * +ProcessGDBRemote::GetPluginNameStatic() +{ + return "process.gdb-remote"; +} + +const char * +ProcessGDBRemote::GetPluginDescriptionStatic() +{ + return "GDB Remote protocol based debugging plug-in."; +} + +void +ProcessGDBRemote::Terminate() +{ + PluginManager::UnregisterPlugin (ProcessGDBRemote::CreateInstance); +} + + +Process* +ProcessGDBRemote::CreateInstance (Target &target, Listener &listener) +{ + return new ProcessGDBRemote (target, listener); +} + +bool +ProcessGDBRemote::CanDebug(Target &target) +{ + // For now we are just making sure the file exists for a given module + ModuleSP exe_module_sp(target.GetExecutableModule()); + if (exe_module_sp.get()) + return exe_module_sp->GetFileSpec().Exists(); + return false; +} + +//---------------------------------------------------------------------- +// ProcessGDBRemote constructor +//---------------------------------------------------------------------- +ProcessGDBRemote::ProcessGDBRemote(Target& target, Listener &listener) : + Process (target, listener), + m_dynamic_loader_ap (), + m_byte_order (eByteOrderHost), + m_flags (0), + m_stdio_communication ("gdb-remote.stdio"), + m_stdio_mutex (Mutex::eMutexTypeRecursive), + m_stdout_data (), + m_arch_spec (), + m_gdb_comm(), + m_debugserver_pid (LLDB_INVALID_PROCESS_ID), + m_debugserver_monitor (0), + m_register_info (), + m_curr_tid (LLDB_INVALID_THREAD_ID), + m_curr_tid_run (LLDB_INVALID_THREAD_ID), + m_async_broadcaster ("lldb.process.gdb-remote.async-broadcaster"), + m_async_thread (LLDB_INVALID_HOST_THREAD), + m_z0_supported (1), + m_continue_packet(), + m_dispatch_queue_offsets_addr (LLDB_INVALID_ADDRESS), + m_libunwind_target_type (UNW_TARGET_UNSPECIFIED), + m_libunwind_addr_space (NULL), + m_waiting_for_attach (false), + m_packet_timeout (1), + m_max_memory_size (512) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ProcessGDBRemote::~ProcessGDBRemote() +{ + // m_mach_process.UnregisterNotificationCallbacks (this); + Clear(); +} + +//---------------------------------------------------------------------- +// PluginInterface +//---------------------------------------------------------------------- +const char * +ProcessGDBRemote::GetPluginName() +{ + return "Process debugging plug-in that uses the GDB remote protocol"; +} + +const char * +ProcessGDBRemote::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessGDBRemote::GetPluginVersion() +{ + return 1; +} + +void +ProcessGDBRemote::GetPluginCommandHelp (const char *command, Stream *strm) +{ + strm->Printf("TODO: fill this in\n"); +} + +Error +ProcessGDBRemote::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in commands are currently supported."); + return error; +} + +Log * +ProcessGDBRemote::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + +void +ProcessGDBRemote::BuildDynamicRegisterInfo () +{ + char register_info_command[64]; + m_register_info.Clear(); + StringExtractorGDBRemote::Type packet_type = StringExtractorGDBRemote::eResponse; + uint32_t reg_offset = 0; + uint32_t reg_num = 0; + for (; packet_type == StringExtractorGDBRemote::eResponse; ++reg_num) + { + ::snprintf (register_info_command, sizeof(register_info_command), "qRegisterInfo%x", reg_num); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(register_info_command, response, 2, false)) + { + packet_type = response.GetType(); + if (packet_type == StringExtractorGDBRemote::eResponse) + { + std::string name; + std::string value; + ConstString reg_name; + ConstString alt_name; + ConstString set_name; + RegisterInfo reg_info = { NULL, // Name + NULL, // Alt name + 0, // byte size + reg_offset, // offset + eEncodingUint, // encoding + eFormatHex, // formate + reg_num, // native register number + { + LLDB_INVALID_REGNUM, // GCC reg num + LLDB_INVALID_REGNUM, // DWARF reg num + LLDB_INVALID_REGNUM, // generic reg num + reg_num // GDB reg num + } + }; + + while (response.GetNameColonValue(name, value)) + { + if (name.compare("name") == 0) + { + reg_name.SetCString(value.c_str()); + } + else if (name.compare("alt-name") == 0) + { + alt_name.SetCString(value.c_str()); + } + else if (name.compare("bitsize") == 0) + { + reg_info.byte_size = Args::StringToUInt32(value.c_str(), 0, 0) / CHAR_BIT; + } + else if (name.compare("offset") == 0) + { + uint32_t offset = Args::StringToUInt32(value.c_str(), UINT32_MAX, 0); + if (offset != offset) + { + reg_offset = offset; + reg_info.byte_offset = offset; + } + } + else if (name.compare("encoding") == 0) + { + if (value.compare("uint") == 0) + reg_info.encoding = eEncodingUint; + else if (value.compare("sint") == 0) + reg_info.encoding = eEncodingSint; + else if (value.compare("ieee754") == 0) + reg_info.encoding = eEncodingIEEE754; + else if (value.compare("vector") == 0) + reg_info.encoding = eEncodingVector; + } + else if (name.compare("format") == 0) + { + if (value.compare("binary") == 0) + reg_info.format = eFormatBinary; + else if (value.compare("decimal") == 0) + reg_info.format = eFormatDecimal; + else if (value.compare("hex") == 0) + reg_info.format = eFormatHex; + else if (value.compare("float") == 0) + reg_info.format = eFormatFloat; + else if (value.compare("vector-sint8") == 0) + reg_info.format = eFormatVectorOfSInt8; + else if (value.compare("vector-uint8") == 0) + reg_info.format = eFormatVectorOfUInt8; + else if (value.compare("vector-sint16") == 0) + reg_info.format = eFormatVectorOfSInt16; + else if (value.compare("vector-uint16") == 0) + reg_info.format = eFormatVectorOfUInt16; + else if (value.compare("vector-sint32") == 0) + reg_info.format = eFormatVectorOfSInt32; + else if (value.compare("vector-uint32") == 0) + reg_info.format = eFormatVectorOfUInt32; + else if (value.compare("vector-float32") == 0) + reg_info.format = eFormatVectorOfFloat32; + else if (value.compare("vector-uint128") == 0) + reg_info.format = eFormatVectorOfUInt128; + } + else if (name.compare("set") == 0) + { + set_name.SetCString(value.c_str()); + } + else if (name.compare("gcc") == 0) + { + reg_info.kinds[eRegisterKindGCC] = Args::StringToUInt32(value.c_str(), LLDB_INVALID_REGNUM, 0); + } + else if (name.compare("dwarf") == 0) + { + reg_info.kinds[eRegisterKindDWARF] = Args::StringToUInt32(value.c_str(), LLDB_INVALID_REGNUM, 0); + } + else if (name.compare("generic") == 0) + { + if (value.compare("pc") == 0) + reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; + else if (value.compare("sp") == 0) + reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; + else if (value.compare("fp") == 0) + reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; + else if (value.compare("ra") == 0) + reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; + else if (value.compare("flags") == 0) + reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; + } + } + + assert (reg_info.byte_size != 0); + reg_offset += reg_info.byte_size; + m_register_info.AddRegister(reg_info, reg_name, alt_name, set_name); + } + } + else + { + packet_type = StringExtractorGDBRemote::eError; + } + } + + if (reg_num == 0) + { + // We didn't get anything. See if we are debugging ARM and fill with + // a hard coded register set until we can get an updated debugserver + // down on the devices. + ArchSpec arm_arch ("arm"); + if (GetTarget().GetArchitecture() == arm_arch) + m_register_info.HardcodeARMRegisters(); + } + m_register_info.Finalize (); +} + +Error +ProcessGDBRemote::WillLaunch (Module* module) +{ + return WillLaunchOrAttach (); +} + +Error +ProcessGDBRemote::WillAttach (lldb::pid_t pid) +{ + return WillLaunchOrAttach (); +} + +Error +ProcessGDBRemote::WillAttach (const char *process_name, bool wait_for_launch) +{ + return WillLaunchOrAttach (); +} + +Error +ProcessGDBRemote::WillLaunchOrAttach () +{ + Error error; + // TODO: this is hardcoded for macosx right now. We need this to be more dynamic + m_dynamic_loader_ap.reset(DynamicLoader::FindPlugin(this, "dynamic-loader.macosx-dyld")); + + if (m_dynamic_loader_ap.get() == NULL) + error.SetErrorString("unable to find the dynamic loader named 'dynamic-loader.macosx-dyld'"); + m_stdio_communication.Clear (); + + return error; +} + +//---------------------------------------------------------------------- +// Process Control +//---------------------------------------------------------------------- +Error +ProcessGDBRemote::DoLaunch +( + Module* module, + char const *argv[], + char const *envp[], + const char *stdin_path, + const char *stdout_path, + const char *stderr_path +) +{ + // ::LogSetBitMask (GDBR_LOG_DEFAULT); + // ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); + // ::LogSetLogFile ("/dev/stdout"); + Error error; + + ObjectFile * object_file = module->GetObjectFile(); + if (object_file) + { + ArchSpec inferior_arch(module->GetArchitecture()); + char host_port[128]; + snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ()); + + bool start_debugserver_with_inferior_args = false; + if (start_debugserver_with_inferior_args) + { + // We want to launch debugserver with the inferior program and its + // arguments on the command line. We should only do this if we + // the GDB server we are talking to doesn't support the 'A' packet. + error = StartDebugserverProcess (host_port, + argv, + envp, + NULL, //stdin_path, + LLDB_INVALID_PROCESS_ID, + NULL, false, + inferior_arch); + if (error.Fail()) + return error; + + error = ConnectToDebugserver (host_port); + if (error.Success()) + { + SetID (m_gdb_comm.GetCurrentProcessID (m_packet_timeout)); + } + } + else + { + error = StartDebugserverProcess (host_port, + NULL, + NULL, + NULL, //stdin_path, + LLDB_INVALID_PROCESS_ID, + NULL, false, + inferior_arch); + if (error.Fail()) + return error; + + error = ConnectToDebugserver (host_port); + if (error.Success()) + { + // Send the environment and the program + arguments after we connect + if (envp) + { + const char *env_entry; + for (int i=0; (env_entry = envp[i]); ++i) + { + if (m_gdb_comm.SendEnvironmentPacket(env_entry, m_packet_timeout) != 0) + break; + } + } + + const uint32_t arg_timeout_seconds = 10; + int arg_packet_err = m_gdb_comm.SendArgumentsPacket (argv, arg_timeout_seconds); + if (arg_packet_err == 0) + { + std::string error_str; + if (m_gdb_comm.GetLaunchSuccess (m_packet_timeout, error_str)) + { + SetID (m_gdb_comm.GetCurrentProcessID (m_packet_timeout)); + } + else + { + error.SetErrorString (error_str.c_str()); + } + } + else + { + error.SetErrorStringWithFormat("'A' packet returned an error: %i.\n", arg_packet_err); + } + + SetID (m_gdb_comm.GetCurrentProcessID (m_packet_timeout)); + } + } + + if (GetID() == LLDB_INVALID_PROCESS_ID) + { + KillDebugserverProcess (); + return error; + } + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse("?", 1, response, m_packet_timeout, false)) + SetPrivateState (SetThreadStopInfo (response)); + + } + else + { + // Set our user ID to an invalid process ID. + SetID(LLDB_INVALID_PROCESS_ID); + error.SetErrorStringWithFormat("Failed to get object file from '%s' for arch %s.\n", module->GetFileSpec().GetFilename().AsCString(), module->GetArchitecture().AsCString()); + } + + // Return the process ID we have + return error; +} + + +Error +ProcessGDBRemote::ConnectToDebugserver (const char *host_port) +{ + Error error; + // Sleep and wait a bit for debugserver to start to listen... + std::auto_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor()); + if (conn_ap.get()) + { + std::string connect_url("connect://"); + connect_url.append (host_port); + const uint32_t max_retry_count = 50; + uint32_t retry_count = 0; + while (!m_gdb_comm.IsConnected()) + { + if (conn_ap->Connect(connect_url.c_str(), &error) == eConnectionStatusSuccess) + { + m_gdb_comm.SetConnection (conn_ap.release()); + break; + } + retry_count++; + + if (retry_count >= max_retry_count) + break; + + usleep (100000); + } + } + + if (!m_gdb_comm.IsConnected()) + { + if (error.Success()) + error.SetErrorString("not connected to remote gdb server"); + return error; + } + + m_gdb_comm.SetAckMode (true); + if (m_gdb_comm.StartReadThread(&error)) + { + // Send an initial ack + m_gdb_comm.SendAck('+'); + + if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) + m_debugserver_monitor = Host::StartMonitoringChildProcess (MonitorDebugserverProcess, + (void*)(intptr_t)GetID(), // Pass the inferior pid in the thread argument (which is a void *) + m_debugserver_pid, + false); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse("QStartNoAckMode", response, 1, false)) + { + if (response.IsOKPacket()) + m_gdb_comm.SetAckMode (false); + } + + BuildDynamicRegisterInfo (); + } + return error; +} + +void +ProcessGDBRemote::DidLaunchOrAttach () +{ + ProcessGDBRemoteLog::LogIf (GDBR_LOG_PROCESS, "ProcessGDBRemote::DidLaunch()"); + if (GetID() == LLDB_INVALID_PROCESS_ID) + { + m_dynamic_loader_ap.reset(); + } + else + { + m_dispatch_queue_offsets_addr = LLDB_INVALID_ADDRESS; + + Module * exe_module = GetTarget().GetExecutableModule ().get(); + assert(exe_module); + + m_arch_spec = exe_module->GetArchitecture(); + + ObjectFile *exe_objfile = exe_module->GetObjectFile(); + assert(exe_objfile); + + m_byte_order = exe_objfile->GetByteOrder(); + assert (m_byte_order != eByteOrderInvalid); + + StreamString strm; + + ArchSpec inferior_arch; + // See if the GDB server supports the qHostInfo information + const char *vendor = m_gdb_comm.GetVendorString().AsCString(); + const char *os_type = m_gdb_comm.GetOSString().AsCString(); + + if (m_arch_spec.IsValid() && m_arch_spec == ArchSpec ("arm")) + { + // For ARM we can't trust the arch of the process as it could + // have an armv6 object file, but be running on armv7 kernel. + inferior_arch = m_gdb_comm.GetHostArchitecture(); + } + + if (!inferior_arch.IsValid()) + inferior_arch = m_arch_spec; + + if (vendor == NULL) + vendor = Host::GetVendorString().AsCString("apple"); + + if (os_type == NULL) + os_type = Host::GetOSString().AsCString("darwin"); + + strm.Printf ("%s-%s-%s", inferior_arch.AsCString(), vendor, os_type); + + std::transform (strm.GetString().begin(), + strm.GetString().end(), + strm.GetString().begin(), + ::tolower); + + m_target_triple.SetCString(strm.GetString().c_str()); + } +} + +void +ProcessGDBRemote::DidLaunch () +{ + DidLaunchOrAttach (); + if (m_dynamic_loader_ap.get()) + m_dynamic_loader_ap->DidLaunch(); +} + +Error +ProcessGDBRemote::DoAttach (pid_t attach_pid) +{ + Error error; + // Clear out and clean up from any current state + Clear(); + // HACK: require arch be set correctly at the target level until we can + // figure out a good way to determine the arch of what we are attaching to + m_arch_spec = m_target.GetArchitecture(); + + //Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS); + if (attach_pid != LLDB_INVALID_PROCESS_ID) + { + SetPrivateState (eStateAttaching); + char host_port[128]; + snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ()); + error = StartDebugserverProcess (host_port, + NULL, + NULL, + NULL, + LLDB_INVALID_PROCESS_ID, + NULL, false, + m_arch_spec); + + if (error.Fail()) + { + const char *error_string = error.AsCString(); + if (error_string == NULL) + error_string = "unable to launch " DEBUGSERVER_BASENAME; + + SetExitStatus (-1, error_string); + } + else + { + error = ConnectToDebugserver (host_port); + if (error.Success()) + { + char packet[64]; + const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%x", attach_pid); + StringExtractorGDBRemote response; + StateType stop_state = m_gdb_comm.SendContinuePacketAndWaitForResponse (this, + packet, + packet_len, + response); + switch (stop_state) + { + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + SetID (attach_pid); + m_last_stop_packet = response; + m_last_stop_packet.SetFilePos (0); + SetPrivateState (stop_state); + break; + + case eStateExited: + m_last_stop_packet = response; + m_last_stop_packet.SetFilePos (0); + response.SetFilePos(1); + SetExitStatus(response.GetHexU8(), NULL); + break; + + default: + SetExitStatus(-1, "unable to attach to process"); + break; + } + + } + } + } + + lldb::pid_t pid = GetID(); + if (pid == LLDB_INVALID_PROCESS_ID) + { + KillDebugserverProcess(); + } + return error; +} + +size_t +ProcessGDBRemote::AttachInputReaderCallback +( + void *baton, + InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + if (notification == eInputReaderGotToken) + { + ProcessGDBRemote *gdb_process = (ProcessGDBRemote *)baton; + if (gdb_process->m_waiting_for_attach) + gdb_process->m_waiting_for_attach = false; + reader->SetIsDone(true); + return 1; + } + return 0; +} + +Error +ProcessGDBRemote::DoAttach (const char *process_name, bool wait_for_launch) +{ + Error error; + // Clear out and clean up from any current state + Clear(); + // HACK: require arch be set correctly at the target level until we can + // figure out a good way to determine the arch of what we are attaching to + m_arch_spec = m_target.GetArchitecture(); + + //Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS); + if (process_name && process_name[0]) + { + + SetPrivateState (eStateAttaching); + char host_port[128]; + snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ()); + error = StartDebugserverProcess (host_port, + NULL, + NULL, + NULL, + LLDB_INVALID_PROCESS_ID, + NULL, false, + m_arch_spec); + if (error.Fail()) + { + const char *error_string = error.AsCString(); + if (error_string == NULL) + error_string = "unable to launch " DEBUGSERVER_BASENAME; + + SetExitStatus (-1, error_string); + } + else + { + error = ConnectToDebugserver (host_port); + if (error.Success()) + { + StreamString packet; + + packet.PutCString("vAttach"); + if (wait_for_launch) + packet.PutCString("Wait"); + packet.PutChar(';'); + packet.PutBytesAsRawHex8(process_name, strlen(process_name), eByteOrderHost, eByteOrderHost); + StringExtractorGDBRemote response; + StateType stop_state = m_gdb_comm.SendContinuePacketAndWaitForResponse (this, + packet.GetData(), + packet.GetSize(), + response); + switch (stop_state) + { + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + SetID (m_gdb_comm.GetCurrentProcessID(m_packet_timeout)); + m_last_stop_packet = response; + m_last_stop_packet.SetFilePos (0); + SetPrivateState (stop_state); + break; + + case eStateExited: + m_last_stop_packet = response; + m_last_stop_packet.SetFilePos (0); + response.SetFilePos(1); + SetExitStatus(response.GetHexU8(), NULL); + break; + + default: + SetExitStatus(-1, "unable to attach to process"); + break; + } + } + } + } + + lldb::pid_t pid = GetID(); + if (pid == LLDB_INVALID_PROCESS_ID) + { + KillDebugserverProcess(); + } + return error; +} + +// +// if (wait_for_launch) +// { +// InputReaderSP reader_sp (new InputReader()); +// StreamString instructions; +// instructions.Printf("Hit any key to cancel waiting for '%s' to launch...", process_name); +// error = reader_sp->Initialize (AttachInputReaderCallback, // callback +// this, // baton +// eInputReaderGranularityByte, +// NULL, // End token +// false); +// +// StringExtractorGDBRemote response; +// m_waiting_for_attach = true; +// FILE *reader_out_fh = reader_sp->GetOutputFileHandle(); +// while (m_waiting_for_attach) +// { +// // Wait for one second for the stop reply packet +// if (m_gdb_comm.WaitForPacket(response, 1)) +// { +// // Got some sort of packet, see if it is the stop reply packet? +// char ch = response.GetChar(0); +// if (ch == 'T') +// { +// m_waiting_for_attach = false; +// } +// } +// else +// { +// // Put a period character every second +// fputc('.', reader_out_fh); +// } +// } +// } +// } +// return GetID(); +//} + +void +ProcessGDBRemote::DidAttach () +{ + DidLaunchOrAttach (); + if (m_dynamic_loader_ap.get()) + m_dynamic_loader_ap->DidAttach(); +} + +Error +ProcessGDBRemote::WillResume () +{ + m_continue_packet.Clear(); + // Start the continue packet we will use to run the target. Each thread + // will append what it is supposed to be doing to this packet when the + // ThreadList::WillResume() is called. If a thread it supposed + // to stay stopped, then don't append anything to this string. + m_continue_packet.Printf("vCont"); + return Error(); +} + +Error +ProcessGDBRemote::DoResume () +{ + ProcessGDBRemoteLog::LogIf (GDBR_LOG_PROCESS, "ProcessGDBRemote::Resume()"); + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (m_continue_packet.GetData(), m_continue_packet.GetSize())); + return Error(); +} + +size_t +ProcessGDBRemote::GetSoftwareBreakpointTrapOpcode (BreakpointSite* bp_site) +{ + const uint8_t *trap_opcode = NULL; + uint32_t trap_opcode_size = 0; + + static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 }; + //static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE }; + static const uint8_t g_ppc_breakpoint_opcode[] = { 0x7F, 0xC0, 0x00, 0x08 }; + static const uint8_t g_i386_breakpoint_opcode[] = { 0xCC }; + + switch (m_arch_spec.GetCPUType()) + { + case CPU_TYPE_ARM: + // TODO: fill this in for ARM. We need to dig up the symbol for + // the address in the breakpoint locaiton and figure out if it is + // an ARM or Thumb breakpoint. + trap_opcode = g_arm_breakpoint_opcode; + trap_opcode_size = sizeof(g_arm_breakpoint_opcode); + break; + + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + trap_opcode = g_ppc_breakpoint_opcode; + trap_opcode_size = sizeof(g_ppc_breakpoint_opcode); + break; + + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + trap_opcode = g_i386_breakpoint_opcode; + trap_opcode_size = sizeof(g_i386_breakpoint_opcode); + break; + + default: + assert(!"Unhandled architecture in ProcessGDBRemote::GetSoftwareBreakpointTrapOpcode()"); + return 0; + } + + if (trap_opcode && trap_opcode_size) + { + if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) + return trap_opcode_size; + } + return 0; +} + +uint32_t +ProcessGDBRemote::UpdateThreadListIfNeeded () +{ + // locker will keep a mutex locked until it goes out of scope + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD); + if (log && log->GetMask().IsSet(GDBR_LOG_VERBOSE)) + log->Printf ("ProcessGDBRemote::%s (pid = %i)", __FUNCTION__, GetID()); + + const uint32_t stop_id = GetStopID(); + if (m_thread_list.GetSize(false) == 0 || stop_id != m_thread_list.GetStopID()) + { + // Update the thread list's stop id immediately so we don't recurse into this function. + ThreadList curr_thread_list (this); + curr_thread_list.SetStopID(stop_id); + + Error err; + StringExtractorGDBRemote response; + for (m_gdb_comm.SendPacketAndWaitForResponse("qfThreadInfo", response, 1, false); + response.IsNormalPacket(); + m_gdb_comm.SendPacketAndWaitForResponse("qsThreadInfo", response, 1, false)) + { + char ch = response.GetChar(); + if (ch == 'l') + break; + if (ch == 'm') + { + do + { + tid_t tid = response.GetHexMaxU32(false, LLDB_INVALID_THREAD_ID); + + if (tid != LLDB_INVALID_THREAD_ID) + { + ThreadSP thread_sp (GetThreadList().FindThreadByID (tid, false)); + if (thread_sp) + thread_sp->GetRegisterContext()->Invalidate(); + else + thread_sp.reset (new ThreadGDBRemote (*this, tid)); + curr_thread_list.AddThread(thread_sp); + } + + ch = response.GetChar(); + } while (ch == ','); + } + } + + m_thread_list = curr_thread_list; + + SetThreadStopInfo (m_last_stop_packet); + } + return GetThreadList().GetSize(false); +} + + +StateType +ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) +{ + const char stop_type = stop_packet.GetChar(); + switch (stop_type) + { + case 'T': + case 'S': + { + // Stop with signal and thread info + const uint8_t signo = stop_packet.GetHexU8(); + std::string name; + std::string value; + std::string thread_name; + uint32_t exc_type = 0; + std::vector<uint64_t> exc_data; + uint32_t tid = LLDB_INVALID_THREAD_ID; + addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; + uint32_t exc_data_count = 0; + while (stop_packet.GetNameColonValue(name, value)) + { + if (name.compare("metype") == 0) + { + // exception type in big endian hex + exc_type = Args::StringToUInt32 (value.c_str(), 0, 16); + } + else if (name.compare("mecount") == 0) + { + // exception count in big endian hex + exc_data_count = Args::StringToUInt32 (value.c_str(), 0, 16); + } + else if (name.compare("medata") == 0) + { + // exception data in big endian hex + exc_data.push_back(Args::StringToUInt64 (value.c_str(), 0, 16)); + } + else if (name.compare("thread") == 0) + { + // thread in big endian hex + tid = Args::StringToUInt32 (value.c_str(), 0, 16); + } + else if (name.compare("name") == 0) + { + thread_name.swap (value); + } + else if (name.compare("dispatchqaddr") == 0) + { + thread_dispatch_qaddr = Args::StringToUInt64 (value.c_str(), 0, 16); + } + } + ThreadSP thread_sp (m_thread_list.FindThreadByID(tid, false)); + + if (thread_sp) + { + ThreadGDBRemote *gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get()); + + gdb_thread->SetThreadDispatchQAddr (thread_dispatch_qaddr); + gdb_thread->SetName (thread_name.empty() ? thread_name.c_str() : NULL); + Thread::StopInfo& stop_info = gdb_thread->GetStopInfoRef(); + gdb_thread->SetStopInfoStopID (GetStopID()); + if (exc_type != 0) + { + if (exc_type == EXC_SOFTWARE && exc_data.size() == 2 && exc_data[0] == EXC_SOFT_SIGNAL) + { + stop_info.SetStopReasonWithSignal(exc_data[1]); + } +#if defined (MACH_EXC_DATA0_SOFTWARE_BREAKPOINT) + else if (exc_type == EXC_BREAKPOINT && exc_data[0] == MACH_EXC_DATA0_SOFTWARE_BREAKPOINT) + { + addr_t pc = gdb_thread->GetRegisterContext()->GetPC(); + user_id_t break_id = GetBreakpointSiteList().FindIDByAddress(pc); + if (break_id == LLDB_INVALID_BREAK_ID) + { + //log->Printf("got EXC_BREAKPOINT at 0x%llx but didn't find a breakpoint site.\n", pc); + stop_info.SetStopReasonWithException(exc_type, exc_data.size()); + for (uint32_t i=0; i<exc_data.size(); ++i) + stop_info.SetExceptionDataAtIndex(i, exc_data[i]); + } + else + { + stop_info.Clear (); + stop_info.SetStopReasonWithBreakpointSiteID (break_id); + } + } +#endif +#if defined (MACH_EXC_DATA0_TRACE) + else if (exc_type == EXC_BREAKPOINT && exc_data[0] == MACH_EXC_DATA0_TRACE) + { + stop_info.SetStopReasonToTrace (); + } +#endif + else + { + stop_info.SetStopReasonWithException(exc_type, exc_data.size()); + for (uint32_t i=0; i<exc_data.size(); ++i) + stop_info.SetExceptionDataAtIndex(i, exc_data[i]); + } + } + else if (signo) + { + stop_info.SetStopReasonWithSignal(signo); + } + else + { + stop_info.SetStopReasonToNone(); + } + } + return eStateStopped; + } + break; + + case 'W': + // process exited + return eStateExited; + + default: + break; + } + return eStateInvalid; +} + +void +ProcessGDBRemote::RefreshStateAfterStop () +{ + // We must be attaching if we don't already have a valid architecture + if (!m_arch_spec.IsValid()) + { + Module *exe_module = GetTarget().GetExecutableModule().get(); + if (exe_module) + m_arch_spec = exe_module->GetArchitecture(); + } + // Let all threads recover from stopping and do any clean up based + // on the previous thread state (if any). + m_thread_list.RefreshStateAfterStop(); + + // Discover new threads: + UpdateThreadListIfNeeded (); +} + +Error +ProcessGDBRemote::DoHalt () +{ + Error error; + if (m_gdb_comm.IsRunning()) + { + bool timed_out = false; + if (!m_gdb_comm.SendInterrupt (2, &timed_out)) + { + if (timed_out) + error.SetErrorString("timed out sending interrupt packet"); + else + error.SetErrorString("unknown error sending interrupt packet"); + } + } + return error; +} + +Error +ProcessGDBRemote::WillDetach () +{ + Error error; + const StateType state = m_private_state.GetValue(); + + if (IsRunning(state)) + error.SetErrorString("Process must be stopped in order to detach."); + + return error; +} + + +Error +ProcessGDBRemote::DoDestroy () +{ + Error error; + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS); + if (log) + log->Printf ("ProcessGDBRemote::DoDestroy()"); + + // Interrupt if our inferior is running... + m_gdb_comm.SendInterrupt (1); + DisableAllBreakpointSites (); + SetExitStatus(-1, "process killed"); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse("k", response, 2, false)) + { + if (log) + { + if (response.IsOKPacket()) + log->Printf ("ProcessGDBRemote::DoDestroy() kill was successful"); + else + log->Printf ("ProcessGDBRemote::DoDestroy() kill failed: %s", response.GetStringRef().c_str()); + } + } + + StopAsyncThread (); + m_gdb_comm.StopReadThread(); + KillDebugserverProcess (); + return error; +} + +ByteOrder +ProcessGDBRemote::GetByteOrder () const +{ + return m_byte_order; +} + +//------------------------------------------------------------------ +// Process Queries +//------------------------------------------------------------------ + +bool +ProcessGDBRemote::IsAlive () +{ + return m_gdb_comm.IsConnected(); +} + +addr_t +ProcessGDBRemote::GetImageInfoAddress() +{ + if (!m_gdb_comm.IsRunning()) + { + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse("qShlibInfoAddr", ::strlen ("qShlibInfoAddr"), response, 2, false)) + { + if (response.IsNormalPacket()) + return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); + } + } + return LLDB_INVALID_ADDRESS; +} + +DynamicLoader * +ProcessGDBRemote::GetDynamicLoader() +{ + return m_dynamic_loader_ap.get(); +} + +//------------------------------------------------------------------ +// Process Memory +//------------------------------------------------------------------ +size_t +ProcessGDBRemote::DoReadMemory (addr_t addr, void *buf, size_t size, Error &error) +{ + if (size > m_max_memory_size) + { + // Keep memory read sizes down to a sane limit. This function will be + // called multiple times in order to complete the task by + // lldb_private::Process so it is ok to do this. + size = m_max_memory_size; + } + + char packet[64]; + const int packet_len = ::snprintf (packet, sizeof(packet), "m%llx,%zx", (uint64_t)addr, size); + assert (packet_len + 1 < sizeof(packet)); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, 2, true)) + { + if (response.IsNormalPacket()) + { + error.Clear(); + return response.GetHexBytes(buf, size, '\xdd'); + } + else if (response.IsErrorPacket()) + error.SetErrorStringWithFormat("gdb remote returned an error: %s", response.GetStringRef().c_str()); + else if (response.IsUnsupportedPacket()) + error.SetErrorStringWithFormat("'%s' packet unsupported", packet); + else + error.SetErrorStringWithFormat("unexpected response to '%s': '%s'", packet, response.GetStringRef().c_str()); + } + else + { + error.SetErrorStringWithFormat("failed to sent packet: '%s'", packet); + } + return 0; +} + +size_t +ProcessGDBRemote::DoWriteMemory (addr_t addr, const void *buf, size_t size, Error &error) +{ + StreamString packet; + packet.Printf("M%llx,%zx:", addr, size); + packet.PutBytesAsRawHex8(buf, size, eByteOrderHost, eByteOrderHost); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, 2, true)) + { + if (response.IsOKPacket()) + { + error.Clear(); + return size; + } + else if (response.IsErrorPacket()) + error.SetErrorStringWithFormat("gdb remote returned an error: %s", response.GetStringRef().c_str()); + else if (response.IsUnsupportedPacket()) + error.SetErrorStringWithFormat("'%s' packet unsupported", packet.GetString().c_str()); + else + error.SetErrorStringWithFormat("unexpected response to '%s': '%s'", packet.GetString().c_str(), response.GetStringRef().c_str()); + } + else + { + error.SetErrorStringWithFormat("failed to sent packet: '%s'", packet.GetString().c_str()); + } + return 0; +} + +lldb::addr_t +ProcessGDBRemote::DoAllocateMemory (size_t size, uint32_t permissions, Error &error) +{ + addr_t allocated_addr = m_gdb_comm.AllocateMemory (size, permissions, m_packet_timeout); + if (allocated_addr == LLDB_INVALID_ADDRESS) + error.SetErrorStringWithFormat("unable to allocate %zu bytes of memory with permissions %u", size, permissions); + else + error.Clear(); + return allocated_addr; +} + +Error +ProcessGDBRemote::DoDeallocateMemory (lldb::addr_t addr) +{ + Error error; + if (!m_gdb_comm.DeallocateMemory (addr, m_packet_timeout)) + error.SetErrorStringWithFormat("unable to deallocate memory at 0x%llx", addr); + return error; +} + + +//------------------------------------------------------------------ +// Process STDIO +//------------------------------------------------------------------ + +size_t +ProcessGDBRemote::GetSTDOUT (char *buf, size_t buf_size, Error &error) +{ + Mutex::Locker locker(m_stdio_mutex); + size_t bytes_available = m_stdout_data.size(); + if (bytes_available > 0) + { + ProcessGDBRemoteLog::LogIf (GDBR_LOG_PROCESS, "ProcessGDBRemote::%s (&%p[%u]) ...", __FUNCTION__, buf, buf_size); + if (bytes_available > buf_size) + { + memcpy(buf, m_stdout_data.data(), buf_size); + m_stdout_data.erase(0, buf_size); + bytes_available = buf_size; + } + else + { + memcpy(buf, m_stdout_data.data(), bytes_available); + m_stdout_data.clear(); + + //ResetEventBits(eBroadcastBitSTDOUT); + } + } + return bytes_available; +} + +size_t +ProcessGDBRemote::GetSTDERR (char *buf, size_t buf_size, Error &error) +{ + // Can we get STDERR through the remote protocol? + return 0; +} + +size_t +ProcessGDBRemote::PutSTDIN (const char *src, size_t src_len, Error &error) +{ + if (m_stdio_communication.IsConnected()) + { + ConnectionStatus status; + m_stdio_communication.Write(src, src_len, status, NULL); + } + return 0; +} + +Error +ProcessGDBRemote::EnableBreakpoint (BreakpointSite *bp_site) +{ + Error error; + assert (bp_site != NULL); + + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS); + user_id_t site_id = bp_site->GetID(); + const addr_t addr = bp_site->GetLoadAddress(); + if (log) + log->Printf ("ProcessGDBRemote::EnableBreakpoint (size_id = %d) address = 0x%llx", site_id, (uint64_t)addr); + + if (bp_site->IsEnabled()) + { + if (log) + log->Printf ("ProcessGDBRemote::EnableBreakpoint (size_id = %d) address = 0x%llx -- SUCCESS (already enabled)", site_id, (uint64_t)addr); + return error; + } + else + { + const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode (bp_site); + + if (bp_site->HardwarePreferred()) + { + // Try and set hardware breakpoint, and if that fails, fall through + // and set a software breakpoint? + } + + if (m_z0_supported) + { + char packet[64]; + const int packet_len = ::snprintf (packet, sizeof(packet), "Z0,%llx,%zx", addr, bp_op_size); + assert (packet_len + 1 < sizeof(packet)); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, 2, true)) + { + if (response.IsUnsupportedPacket()) + { + // Disable z packet support and try again + m_z0_supported = 0; + return EnableBreakpoint (bp_site); + } + else if (response.IsOKPacket()) + { + bp_site->SetEnabled(true); + bp_site->SetType (BreakpointSite::eExternal); + return error; + } + else + { + uint8_t error_byte = response.GetError(); + if (error_byte) + error.SetErrorStringWithFormat("%x packet failed with error: %i (0x%2.2x).\n", packet, error_byte, error_byte); + } + } + } + else + { + return EnableSoftwareBreakpoint (bp_site); + } + } + + if (log) + { + const char *err_string = error.AsCString(); + log->Printf ("ProcessGDBRemote::EnableBreakpoint() error for breakpoint at 0x%8.8llx: %s", + bp_site->GetLoadAddress(), + err_string ? err_string : "NULL"); + } + // We shouldn't reach here on a successful breakpoint enable... + if (error.Success()) + error.SetErrorToGenericError(); + return error; +} + +Error +ProcessGDBRemote::DisableBreakpoint (BreakpointSite *bp_site) +{ + Error error; + assert (bp_site != NULL); + addr_t addr = bp_site->GetLoadAddress(); + user_id_t site_id = bp_site->GetID(); + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS); + if (log) + log->Printf ("ProcessGDBRemote::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx", site_id, (uint64_t)addr); + + if (bp_site->IsEnabled()) + { + const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode (bp_site); + + if (bp_site->IsHardware()) + { + // TODO: disable hardware breakpoint... + } + else + { + if (m_z0_supported) + { + char packet[64]; + const int packet_len = ::snprintf (packet, sizeof(packet), "z0,%llx,%zx", addr, bp_op_size); + assert (packet_len + 1 < sizeof(packet)); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, 2, true)) + { + if (response.IsUnsupportedPacket()) + { + error.SetErrorString("Breakpoint site was set with Z packet, yet remote debugserver states z packets are not supported."); + } + else if (response.IsOKPacket()) + { + if (log) + log->Printf ("ProcessGDBRemote::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx -- SUCCESS", site_id, (uint64_t)addr); + bp_site->SetEnabled(false); + return error; + } + else + { + uint8_t error_byte = response.GetError(); + if (error_byte) + error.SetErrorStringWithFormat("%x packet failed with error: %i (0x%2.2x).\n", packet, error_byte, error_byte); + } + } + } + else + { + return DisableSoftwareBreakpoint (bp_site); + } + } + } + else + { + if (log) + log->Printf ("ProcessGDBRemote::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx -- SUCCESS (already disabled)", site_id, (uint64_t)addr); + return error; + } + + if (error.Success()) + error.SetErrorToGenericError(); + return error; +} + +Error +ProcessGDBRemote::EnableWatchpoint (WatchpointLocation *wp) +{ + Error error; + if (wp) + { + user_id_t watchID = wp->GetID(); + addr_t addr = wp->GetLoadAddress(); + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS); + if (log) + log->Printf ("ProcessGDBRemote::EnableWatchpoint(watchID = %d)", watchID); + if (wp->IsEnabled()) + { + if (log) + log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %d) addr = 0x%8.8llx: watchpoint already enabled.", watchID, (uint64_t)addr); + return error; + } + else + { + // Pass down an appropriate z/Z packet... + error.SetErrorString("watchpoints not supported"); + } + } + else + { + error.SetErrorString("Watchpoint location argument was NULL."); + } + if (error.Success()) + error.SetErrorToGenericError(); + return error; +} + +Error +ProcessGDBRemote::DisableWatchpoint (WatchpointLocation *wp) +{ + Error error; + if (wp) + { + user_id_t watchID = wp->GetID(); + + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS); + + addr_t addr = wp->GetLoadAddress(); + if (log) + log->Printf ("ProcessGDBRemote::DisableWatchpoint (watchID = %d) addr = 0x%8.8llx", watchID, (uint64_t)addr); + + if (wp->IsHardware()) + { + // Pass down an appropriate z/Z packet... + error.SetErrorString("watchpoints not supported"); + } + // TODO: clear software watchpoints if we implement them + } + else + { + error.SetErrorString("Watchpoint location argument was NULL."); + } + if (error.Success()) + error.SetErrorToGenericError(); + return error; +} + +void +ProcessGDBRemote::Clear() +{ + m_flags = 0; + m_thread_list.Clear(); + { + Mutex::Locker locker(m_stdio_mutex); + m_stdout_data.clear(); + } + DestoryLibUnwindAddressSpace(); +} + +Error +ProcessGDBRemote::DoSignal (int signo) +{ + Error error; + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS); + if (log) + log->Printf ("ProcessGDBRemote::DoSignal (signal = %d)", signo); + + if (!m_gdb_comm.SendAsyncSignal (signo)) + error.SetErrorStringWithFormat("failed to send signal %i", signo); + return error; +} + + +Error +ProcessGDBRemote::DoDetach() +{ + Error error; + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS); + if (log) + log->Printf ("ProcessGDBRemote::DoDetach()"); + + // if (DoSIGSTOP (true)) + // { + // CloseChildFileDescriptors (); + // + // // Scope for "locker" so we can reply to all of our exceptions (the SIGSTOP + // // exception). + // { + // Mutex::Locker locker(m_exception_messages_mutex); + // ReplyToAllExceptions(); + // } + // + // // Shut down the exception thread and cleanup our exception remappings + // Task().ShutDownExceptionThread(); + // + // pid_t pid = GetID(); + // + // // Detach from our process while we are stopped. + // errno = 0; + // + // // Detach from our process + // ::ptrace (PT_DETACH, pid, (caddr_t)1, 0); + // + // error.SetErrorToErrno(); + // + // if (log || error.Fail()) + // error.PutToLog(log, "::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid); + // + // // Resume our task + // Task().Resume(); + // + // // NULL our task out as we have already retored all exception ports + // Task().Clear(); + // + // // Clear out any notion of the process we once were + // Clear(); + // + // SetPrivateState (eStateDetached); + // return true; + // } + return error; +} + +void +ProcessGDBRemote::STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len) +{ + ProcessGDBRemote *process = (ProcessGDBRemote *)baton; + process->AppendSTDOUT(static_cast<const char *>(src), src_len); +} + +void +ProcessGDBRemote::AppendSTDOUT (const char* s, size_t len) +{ + ProcessGDBRemoteLog::LogIf (GDBR_LOG_PROCESS, "ProcessGDBRemote::%s (<%d> %s) ...", __FUNCTION__, len, s); + Mutex::Locker locker(m_stdio_mutex); + m_stdout_data.append(s, len); + + // FIXME: Make a real data object for this and put it out. + BroadcastEventIfUnique (eBroadcastBitSTDOUT); +} + + +Error +ProcessGDBRemote::StartDebugserverProcess +( + const char *debugserver_url, // The connection string to use in the spawned debugserver ("localhost:1234" or "/dev/tty...") + char const *inferior_argv[], // Arguments for the inferior program including the path to the inferior itself as the first argument + char const *inferior_envp[], // Environment to pass along to the inferior program + char const *stdio_path, + lldb::pid_t attach_pid, // If inferior inferior_argv == NULL, and attach_pid != LLDB_INVALID_PROCESS_ID then attach to this attach_pid + const char *attach_name, // Wait for the next process to launch whose basename matches "attach_name" + bool wait_for_launch, // Wait for the process named "attach_name" to launch + ArchSpec& inferior_arch // The arch of the inferior that we will launch +) +{ + Error error; + if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID) + { + // If we locate debugserver, keep that located version around + static FileSpec g_debugserver_file_spec; + + FileSpec debugserver_file_spec; + char debugserver_path[PATH_MAX]; + + // Always check to see if we have an environment override for the path + // to the debugserver to use and use it if we do. + const char *env_debugserver_path = getenv("LLDB_DEBUGSERVER_PATH"); + if (env_debugserver_path) + debugserver_file_spec.SetFile (env_debugserver_path); + else + debugserver_file_spec = g_debugserver_file_spec; + bool debugserver_exists = debugserver_file_spec.Exists(); + if (!debugserver_exists) + { + // The debugserver binary is in the LLDB.framework/Resources + // directory. + FileSpec framework_file_spec (Host::GetModuleFileSpecForHostAddress ((void *)lldb_private::Initialize)); + const char *framework_dir = framework_file_spec.GetDirectory().AsCString(); + const char *lldb_framework = ::strstr (framework_dir, "/LLDB.framework"); + + if (lldb_framework) + { + int len = lldb_framework - framework_dir + strlen ("/LLDB.framework"); + ::snprintf (debugserver_path, + sizeof(debugserver_path), + "%.*s/Resources/%s", + len, + framework_dir, + DEBUGSERVER_BASENAME); + debugserver_file_spec.SetFile (debugserver_path); + debugserver_exists = debugserver_file_spec.Exists(); + } + + if (debugserver_exists) + { + g_debugserver_file_spec = debugserver_file_spec; + } + else + { + g_debugserver_file_spec.Clear(); + debugserver_file_spec.Clear(); + } + } + + if (debugserver_exists) + { + debugserver_file_spec.GetPath (debugserver_path, sizeof(debugserver_path)); + + m_stdio_communication.Clear(); + posix_spawnattr_t attr; + + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS); + + Error local_err; // Errors that don't affect the spawning. + if (log) + log->Printf ("%s ( path='%s', argv=%p, envp=%p, arch=%s )", __FUNCTION__, debugserver_path, inferior_argv, inferior_envp, inferior_arch.AsCString()); + error.SetError( ::posix_spawnattr_init (&attr), eErrorTypePOSIX); + if (error.Fail() || log) + error.PutToLog(log, "::posix_spawnattr_init ( &attr )"); + if (error.Fail()) + return error;; + +#if !defined (__arm__) + + // We don't need to do this for ARM, and we really shouldn't now that we + // have multiple CPU subtypes and no posix_spawnattr call that allows us + // to set which CPU subtype to launch... + cpu_type_t cpu = inferior_arch.GetCPUType(); + if (cpu != 0 && cpu != CPU_TYPE_ANY && cpu != LLDB_INVALID_CPUTYPE) + { + size_t ocount = 0; + error.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &ocount), eErrorTypePOSIX); + if (error.Fail() || log) + error.PutToLog(log, "::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %zu )", cpu, ocount); + + if (error.Fail() != 0 || ocount != 1) + return error; + } + +#endif + + Args debugserver_args; + char arg_cstr[PATH_MAX]; + bool launch_process = true; + + if (inferior_argv == NULL && attach_pid != LLDB_INVALID_PROCESS_ID) + launch_process = false; + else if (attach_name) + launch_process = false; // Wait for a process whose basename matches that in inferior_argv[0] + + bool pass_stdio_path_to_debugserver = true; + lldb_utility::PseudoTerminal pty; + if (stdio_path == NULL) + { + pass_stdio_path_to_debugserver = false; + if (pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, NULL, 0)) + { + struct termios stdin_termios; + if (::tcgetattr (pty.GetMasterFileDescriptor(), &stdin_termios) == 0) + { + stdin_termios.c_lflag &= ~ECHO; // Turn off echoing + stdin_termios.c_lflag &= ~ICANON; // Get one char at a time + ::tcsetattr (pty.GetMasterFileDescriptor(), TCSANOW, &stdin_termios); + } + stdio_path = pty.GetSlaveName (NULL, 0); + } + } + + // Start args with "debugserver /file/path -r --" + debugserver_args.AppendArgument(debugserver_path); + debugserver_args.AppendArgument(debugserver_url); + debugserver_args.AppendArgument("--native-regs"); // use native registers, not the GDB registers + debugserver_args.AppendArgument("--setsid"); // make debugserver run in its own session so + // signals generated by special terminal key + // sequences (^C) don't affect debugserver + + // Only set the inferior + if (launch_process) + { + if (stdio_path && pass_stdio_path_to_debugserver) + { + debugserver_args.AppendArgument("-s"); // short for --stdio-path + StreamString strm; + strm.Printf("'%s'", stdio_path); + debugserver_args.AppendArgument(strm.GetData()); // path to file to have inferior open as it's STDIO + } + } + + const char *env_debugserver_log_file = getenv("LLDB_DEBUGSERVER_LOG_FILE"); + if (env_debugserver_log_file) + { + ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-file=%s", env_debugserver_log_file); + debugserver_args.AppendArgument(arg_cstr); + } + + const char *env_debugserver_log_flags = getenv("LLDB_DEBUGSERVER_LOG_FLAGS"); + if (env_debugserver_log_flags) + { + ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-flags=%s", env_debugserver_log_flags); + debugserver_args.AppendArgument(arg_cstr); + } +// debugserver_args.AppendArgument("--log-file=/tmp/debugserver.txt"); +// debugserver_args.AppendArgument("--log-flags=0x800e0e"); + + // Now append the program arguments + if (launch_process) + { + if (inferior_argv) + { + // Terminate the debugserver args so we can now append the inferior args + debugserver_args.AppendArgument("--"); + + for (int i = 0; inferior_argv[i] != NULL; ++i) + debugserver_args.AppendArgument (inferior_argv[i]); + } + else + { + // Will send environment entries with the 'QEnvironment:' packet + // Will send arguments with the 'A' packet + } + } + else if (attach_pid != LLDB_INVALID_PROCESS_ID) + { + ::snprintf (arg_cstr, sizeof(arg_cstr), "--attach=%u", attach_pid); + debugserver_args.AppendArgument (arg_cstr); + } + else if (attach_name && attach_name[0]) + { + if (wait_for_launch) + debugserver_args.AppendArgument ("--waitfor"); + else + debugserver_args.AppendArgument ("--attach"); + debugserver_args.AppendArgument (attach_name); + } + + Error file_actions_err; + posix_spawn_file_actions_t file_actions; +#if DONT_CLOSE_DEBUGSERVER_STDIO + file_actions_err.SetErrorString ("Remove this after uncommenting the code block below."); +#else + file_actions_err.SetError( ::posix_spawn_file_actions_init (&file_actions), eErrorTypePOSIX); + if (file_actions_err.Success()) + { + ::posix_spawn_file_actions_addclose (&file_actions, STDIN_FILENO); + ::posix_spawn_file_actions_addclose (&file_actions, STDOUT_FILENO); + ::posix_spawn_file_actions_addclose (&file_actions, STDERR_FILENO); + } +#endif + + if (log) + { + StreamString strm; + debugserver_args.Dump (&strm); + log->Printf("%s arguments:\n%s", debugserver_args.GetArgumentAtIndex(0), strm.GetData()); + } + + error.SetError(::posix_spawnp (&m_debugserver_pid, + debugserver_path, + file_actions_err.Success() ? &file_actions : NULL, + &attr, + debugserver_args.GetArgumentVector(), + (char * const*)inferior_envp), + eErrorTypePOSIX); + + if (file_actions_err.Success()) + ::posix_spawn_file_actions_destroy (&file_actions); + + // We have seen some cases where posix_spawnp was returning a valid + // looking pid even when an error was returned, so clear it out + if (error.Fail()) + m_debugserver_pid = LLDB_INVALID_PROCESS_ID; + + if (error.Fail() || log) + error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", m_debugserver_pid, debugserver_path, NULL, &attr, inferior_argv, inferior_envp); + +// if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) +// { +// std::auto_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor (pty.ReleaseMasterFileDescriptor(), true)); +// if (conn_ap.get()) +// { +// m_stdio_communication.SetConnection(conn_ap.release()); +// if (m_stdio_communication.IsConnected()) +// { +// m_stdio_communication.SetReadThreadBytesReceivedCallback (STDIOReadThreadBytesReceived, this); +// m_stdio_communication.StartReadThread(); +// } +// } +// } + } + else + { + error.SetErrorStringWithFormat ("Unable to locate " DEBUGSERVER_BASENAME ".\n"); + } + + if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) + StartAsyncThread (); + } + return error; +} + +bool +ProcessGDBRemote::MonitorDebugserverProcess +( + void *callback_baton, + lldb::pid_t debugserver_pid, + int signo, // Zero for no signal + int exit_status // Exit value of process if signal is zero +) +{ + // We pass in the ProcessGDBRemote inferior process it and name it + // "gdb_remote_pid". The process ID is passed in the "callback_baton" + // pointer value itself, thus we need the double cast... + + // "debugserver_pid" argument passed in is the process ID for + // debugserver that we are tracking... + + lldb::pid_t gdb_remote_pid = (lldb::pid_t)(intptr_t)callback_baton; + TargetSP target_sp(Debugger::GetSharedInstance().GetTargetList().FindTargetWithProcessID (gdb_remote_pid)); + if (target_sp) + { + ProcessSP process_sp (target_sp->GetProcessSP()); + if (process_sp) + { + // Sleep for a half a second to make sure our inferior process has + // time to set its exit status before we set it incorrectly when + // both the debugserver and the inferior process shut down. + usleep (500000); + // If our process hasn't yet exited, debugserver might have died. + // If the process did exit, the we are reaping it. + if (process_sp->GetState() != eStateExited) + { + char error_str[1024]; + if (signo) + { + const char *signal_cstr = process_sp->GetUnixSignals().GetSignalAsCString (signo); + if (signal_cstr) + ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %s", signal_cstr); + else + ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %i", signo); + } + else + { + ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x", exit_status); + } + + process_sp->SetExitStatus (-1, error_str); + } + else + { + ProcessGDBRemote *gdb_process = (ProcessGDBRemote *)process_sp.get(); + // Debugserver has exited we need to let our ProcessGDBRemote + // know that it no longer has a debugserver instance + gdb_process->m_debugserver_pid = LLDB_INVALID_PROCESS_ID; + // We are returning true to this function below, so we can + // forget about the monitor handle. + gdb_process->m_debugserver_monitor = 0; + } + } + } + return true; +} + +void +ProcessGDBRemote::KillDebugserverProcess () +{ + if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) + { + ::kill (m_debugserver_pid, SIGINT); + m_debugserver_pid = LLDB_INVALID_PROCESS_ID; + } +} + +void +ProcessGDBRemote::Initialize() +{ + static bool g_initialized = false; + + if (g_initialized == false) + { + g_initialized = true; + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + + Log::Callbacks log_callbacks = { + ProcessGDBRemoteLog::DisableLog, + ProcessGDBRemoteLog::EnableLog, + ProcessGDBRemoteLog::ListLogCategories + }; + + Log::RegisterLogChannel (ProcessGDBRemote::GetPluginNameStatic(), log_callbacks); + } +} + +bool +ProcessGDBRemote::SetCurrentGDBRemoteThread (int tid) +{ + if (m_curr_tid == tid) + return true; + + char packet[32]; + const int packet_len = ::snprintf (packet, sizeof(packet), "Hg%x", tid); + assert (packet_len + 1 < sizeof(packet)); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, 2, false)) + { + if (response.IsOKPacket()) + { + m_curr_tid = tid; + return true; + } + } + return false; +} + +bool +ProcessGDBRemote::SetCurrentGDBRemoteThreadForRun (int tid) +{ + if (m_curr_tid_run == tid) + return true; + + char packet[32]; + const int packet_len = ::snprintf (packet, sizeof(packet), "Hg%x", tid); + assert (packet_len + 1 < sizeof(packet)); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, 2, false)) + { + if (response.IsOKPacket()) + { + m_curr_tid_run = tid; + return true; + } + } + return false; +} + +void +ProcessGDBRemote::ResetGDBRemoteState () +{ + // Reset and GDB remote state + m_curr_tid = LLDB_INVALID_THREAD_ID; + m_curr_tid_run = LLDB_INVALID_THREAD_ID; + m_z0_supported = 1; +} + + +bool +ProcessGDBRemote::StartAsyncThread () +{ + ResetGDBRemoteState (); + + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS); + + if (log) + log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); + + // Create a thread that watches our internal state and controls which + // events make it to clients (into the DCProcess event queue). + m_async_thread = Host::ThreadCreate ("<lldb.process.gdb-remote.async>", ProcessGDBRemote::AsyncThread, this, NULL); + return m_async_thread != LLDB_INVALID_HOST_THREAD; +} + +void +ProcessGDBRemote::StopAsyncThread () +{ + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS); + + if (log) + log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); + + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit); + + // Stop the stdio thread + if (m_async_thread != LLDB_INVALID_HOST_THREAD) + { + Host::ThreadJoin (m_async_thread, NULL, NULL); + } +} + + +void * +ProcessGDBRemote::AsyncThread (void *arg) +{ + ProcessGDBRemote *process = (ProcessGDBRemote*) arg; + + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS); + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) thread starting...", __FUNCTION__, arg, process->GetID()); + + Listener listener ("ProcessGDBRemote::AsyncThread"); + EventSP event_sp; + const uint32_t desired_event_mask = eBroadcastBitAsyncContinue | + eBroadcastBitAsyncThreadShouldExit; + + if (listener.StartListeningForEvents (&process->m_async_broadcaster, desired_event_mask) == desired_event_mask) + { + bool done = false; + while (!done) + { + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) listener.WaitForEvent (NULL, event_sp)...", __FUNCTION__, arg, process->GetID()); + if (listener.WaitForEvent (NULL, event_sp)) + { + const uint32_t event_type = event_sp->GetType(); + switch (event_type) + { + case eBroadcastBitAsyncContinue: + { + const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event_sp.get()); + + if (continue_packet) + { + const char *continue_cstr = (const char *)continue_packet->GetBytes (); + const size_t continue_cstr_len = continue_packet->GetByteSize (); + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) got eBroadcastBitAsyncContinue: %s", __FUNCTION__, arg, process->GetID(), continue_cstr); + + process->SetPrivateState(eStateRunning); + StringExtractorGDBRemote response; + StateType stop_state = process->GetGDBRemote().SendContinuePacketAndWaitForResponse (process, continue_cstr, continue_cstr_len, response); + + switch (stop_state) + { + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + process->m_last_stop_packet = response; + process->m_last_stop_packet.SetFilePos (0); + process->SetPrivateState (stop_state); + break; + + case eStateExited: + process->m_last_stop_packet = response; + process->m_last_stop_packet.SetFilePos (0); + response.SetFilePos(1); + process->SetExitStatus(response.GetHexU8(), NULL); + done = true; + break; + + case eStateInvalid: + break; + + default: + process->SetPrivateState (stop_state); + break; + } + } + } + break; + + case eBroadcastBitAsyncThreadShouldExit: + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) got eBroadcastBitAsyncThreadShouldExit...", __FUNCTION__, arg, process->GetID()); + done = true; + break; + + default: + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type); + done = true; + break; + } + } + else + { + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) listener.WaitForEvent (NULL, event_sp) => false", __FUNCTION__, arg, process->GetID()); + done = true; + } + } + } + + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) thread exiting...", __FUNCTION__, arg, process->GetID()); + + process->m_async_thread = LLDB_INVALID_HOST_THREAD; + return NULL; +} + +lldb_private::unw_addr_space_t +ProcessGDBRemote::GetLibUnwindAddressSpace () +{ + unw_targettype_t target_type = UNW_TARGET_UNSPECIFIED; + if (m_target.GetArchitecture().GetCPUType() == CPU_TYPE_I386) + target_type = UNW_TARGET_I386; + if (m_target.GetArchitecture().GetCPUType() == CPU_TYPE_X86_64) + target_type = UNW_TARGET_X86_64; + + if (m_libunwind_addr_space) + { + if (m_libunwind_target_type != target_type) + DestoryLibUnwindAddressSpace(); + else + return m_libunwind_addr_space; + } + unw_accessors_t callbacks = get_macosx_libunwind_callbacks (); + m_libunwind_addr_space = unw_create_addr_space (&callbacks, target_type); + if (m_libunwind_addr_space) + m_libunwind_target_type = target_type; + else + m_libunwind_target_type = UNW_TARGET_UNSPECIFIED; + return m_libunwind_addr_space; +} + +void +ProcessGDBRemote::DestoryLibUnwindAddressSpace () +{ + if (m_libunwind_addr_space) + { + unw_destroy_addr_space (m_libunwind_addr_space); + m_libunwind_addr_space = NULL; + } + m_libunwind_target_type = UNW_TARGET_UNSPECIFIED; +} + + +const char * +ProcessGDBRemote::GetDispatchQueueNameForThread +( + addr_t thread_dispatch_qaddr, + std::string &dispatch_queue_name +) +{ + dispatch_queue_name.clear(); + if (thread_dispatch_qaddr != 0 && thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) + { + // Cache the dispatch_queue_offsets_addr value so we don't always have + // to look it up + if (m_dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS) + { + ModuleSP module_sp(GetTarget().GetImages().FindFirstModuleForFileSpec (FileSpec("libSystem.B.dylib"))); + if (module_sp.get() == NULL) + return NULL; + + const Symbol *dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (ConstString("dispatch_queue_offsets"), eSymbolTypeData); + if (dispatch_queue_offsets_symbol) + m_dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol->GetValue().GetLoadAddress(this); + + if (m_dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS) + return NULL; + } + + uint8_t memory_buffer[8]; + DataExtractor data(memory_buffer, sizeof(memory_buffer), GetByteOrder(), GetAddressByteSize()); + + // Excerpt from src/queue_private.h + struct dispatch_queue_offsets_s + { + uint16_t dqo_version; + uint16_t dqo_label; + uint16_t dqo_label_size; + } dispatch_queue_offsets; + + + Error error; + if (ReadMemory (m_dispatch_queue_offsets_addr, memory_buffer, sizeof(dispatch_queue_offsets), error) == sizeof(dispatch_queue_offsets)) + { + uint32_t data_offset = 0; + if (data.GetU16(&data_offset, &dispatch_queue_offsets.dqo_version, sizeof(dispatch_queue_offsets)/sizeof(uint16_t))) + { + if (ReadMemory (thread_dispatch_qaddr, &memory_buffer, data.GetAddressByteSize(), error) == data.GetAddressByteSize()) + { + data_offset = 0; + lldb::addr_t queue_addr = data.GetAddress(&data_offset); + lldb::addr_t label_addr = queue_addr + dispatch_queue_offsets.dqo_label; + dispatch_queue_name.resize(dispatch_queue_offsets.dqo_label_size, '\0'); + size_t bytes_read = ReadMemory (label_addr, &dispatch_queue_name[0], dispatch_queue_offsets.dqo_label_size, error); + if (bytes_read < dispatch_queue_offsets.dqo_label_size) + dispatch_queue_name.erase (bytes_read); + } + } + } + } + if (dispatch_queue_name.empty()) + return NULL; + return dispatch_queue_name.c_str(); +} + diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h new file mode 100644 index 00000000000..cd5bab0194f --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -0,0 +1,404 @@ +//===-- ProcessGDBRemote.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_ProcessGDBRemote_h_ +#define liblldb_ProcessGDBRemote_h_ + +// C Includes + +// C++ Includes +#include <list> + +// Other libraries and framework includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ThreadSafeValue.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +#include "GDBRemoteCommunication.h" +#include "StringExtractor.h" +#include "GDBRemoteRegisterContext.h" +#include "libunwind.h" + +class ThreadGDBRemote; + +class ProcessGDBRemote : public lldb_private::Process +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + static Process* + CreateInstance (lldb_private::Target& target, lldb_private::Listener &listener); + + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ProcessGDBRemote(lldb_private::Target& target, lldb_private::Listener &listener); + + virtual + ~ProcessGDBRemote(); + + //------------------------------------------------------------------ + // Check if a given Process + //------------------------------------------------------------------ + virtual bool + CanDebug (lldb_private::Target &target); + + //------------------------------------------------------------------ + // Creating a new process, or attaching to an existing one + //------------------------------------------------------------------ + virtual lldb_private::Error + WillLaunch (lldb_private::Module* module); + + virtual lldb_private::Error + DoLaunch (lldb_private::Module* module, + char const *argv[], // Can be NULL + char const *envp[], // Can be NULL + const char *stdin_path, // Can be NULL + const char *stdout_path, // Can be NULL + const char *stderr_path); // Can be NULL + + virtual void + DidLaunch (); + + virtual lldb_private::Error + WillAttach (lldb::pid_t pid); + + virtual lldb_private::Error + WillAttach (const char *process_name, bool wait_for_launch); + + lldb_private::Error + WillLaunchOrAttach (); + + virtual lldb_private::Error + DoAttach (lldb::pid_t pid); + + virtual lldb_private::Error + DoAttach (const char *process_name, bool wait_for_launch); + + virtual void + DidAttach (); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + + //------------------------------------------------------------------ + // Process Control + //------------------------------------------------------------------ + virtual lldb_private::Error + WillResume (); + + virtual lldb_private::Error + DoResume (); + + virtual lldb_private::Error + DoHalt (); + + virtual lldb_private::Error + WillDetach (); + + virtual lldb_private::Error + DoDetach (); + + virtual lldb_private::Error + DoSignal (int signal); + + virtual lldb_private::Error + DoDestroy (); + + virtual void + RefreshStateAfterStop(); + + //------------------------------------------------------------------ + // Process Queries + //------------------------------------------------------------------ + virtual bool + IsAlive (); + + virtual lldb::addr_t + GetImageInfoAddress(); + + //------------------------------------------------------------------ + // Process Memory + //------------------------------------------------------------------ + virtual size_t + DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error); + + virtual size_t + DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, lldb_private::Error &error); + + virtual lldb::addr_t + DoAllocateMemory (size_t size, uint32_t permissions, lldb_private::Error &error); + + virtual lldb_private::Error + DoDeallocateMemory (lldb::addr_t ptr); + + //------------------------------------------------------------------ + // Process STDIO + //------------------------------------------------------------------ + virtual size_t + GetSTDOUT (char *buf, size_t buf_size, lldb_private::Error &error); + + virtual size_t + GetSTDERR (char *buf, size_t buf_size, lldb_private::Error &error); + + virtual size_t + PutSTDIN (const char *buf, size_t buf_size, lldb_private::Error &error); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + virtual size_t + GetSoftwareBreakpointTrapOpcode (lldb_private::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableBreakpoint (lldb_private::BreakpointSite *bp_site); + + virtual lldb_private::Error + DisableBreakpoint (lldb_private::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Watchpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableWatchpoint (lldb_private::WatchpointLocation *wp_loc); + + virtual lldb_private::Error + DisableWatchpoint (lldb_private::WatchpointLocation *wp_loc); + + virtual lldb::ByteOrder + GetByteOrder () const; + + virtual lldb_private::DynamicLoader * + GetDynamicLoader (); + +protected: + friend class ThreadGDBRemote; + friend class GDBRemoteCommunication; + friend class GDBRemoteRegisterContext; + + bool + SetCurrentGDBRemoteThread (int tid); + + bool + SetCurrentGDBRemoteThreadForRun (int tid); + + //---------------------------------------------------------------------- + // Accessors + //---------------------------------------------------------------------- + bool + IsRunning ( lldb::StateType state ) + { + return state == lldb::eStateRunning || IsStepping(state); + } + + bool + IsStepping ( lldb::StateType state) + { + return state == lldb::eStateStepping; + } + bool + CanResume ( lldb::StateType state) + { + return state == lldb::eStateStopped; + } + + bool + HasExited (lldb::StateType state) + { + return state == lldb::eStateExited; + } + + bool + ProcessIDIsValid ( ) const; + + static void + STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len); + + void + AppendSTDOUT (const char* s, size_t len); + + lldb_private::ArchSpec& + GetArchSpec() + { + return m_arch_spec; + } + const lldb_private::ArchSpec& + GetArchSpec() const + { + return m_arch_spec; + } + + void + Clear ( ); + + lldb_private::Flags & + GetFlags () + { + return m_flags; + } + + const lldb_private::Flags & + GetFlags () const + { + return m_flags; + } + + uint32_t + UpdateThreadListIfNeeded (); + + lldb_private::Error + StartDebugserverProcess (const char *debugserver_url, // The connection string to use in the spawned debugserver ("localhost:1234" or "/dev/tty...") + char const *inferior_argv[], + char const *inferior_envp[], + const char *stdin_path, + lldb::pid_t attach_pid, // If inferior inferior_argv == NULL, then attach to this pid + const char *attach_pid_name, // Wait for the next process to launch whose basename matches "attach_wait_name" + bool wait_for_launch, // Wait for the process named "attach_wait_name" to launch + lldb_private::ArchSpec& arch_spec); + + void + KillDebugserverProcess (); + + void + BuildDynamicRegisterInfo (); + + GDBRemoteCommunication & + GetGDBRemote() + { + return m_gdb_comm; + } + + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitAsyncContinue = (1 << 0), + eBroadcastBitAsyncThreadShouldExit = (1 << 1) + }; + + + std::auto_ptr<lldb_private::DynamicLoader> m_dynamic_loader_ap; + lldb_private::Flags m_flags; // Process specific flags (see eFlags enums) + lldb_private::Communication m_stdio_communication; + lldb_private::Mutex m_stdio_mutex; // Multithreaded protection for stdio + std::string m_stdout_data; + lldb_private::ArchSpec m_arch_spec; + lldb::ByteOrder m_byte_order; + GDBRemoteCommunication m_gdb_comm; + lldb::pid_t m_debugserver_pid; + uint32_t m_debugserver_monitor; + StringExtractor m_last_stop_packet; + GDBRemoteDynamicRegisterInfo m_register_info; + lldb_private::Broadcaster m_async_broadcaster; + lldb::thread_t m_async_thread; + // Current GDB remote state. Any members added here need to be reset to + // proper default values in ResetGDBRemoteState (). + lldb::tid_t m_curr_tid; // Current gdb remote protocol thread index for all other operations + lldb::tid_t m_curr_tid_run; // Current gdb remote protocol thread index for continue, step, etc + uint32_t m_z0_supported:1; // Set to non-zero if Z0 and z0 packets are supported + lldb_private::StreamString m_continue_packet; + lldb::addr_t m_dispatch_queue_offsets_addr; + uint32_t m_packet_timeout; + size_t m_max_memory_size; // The maximum number of bytes to read/write when reading and writing memory + lldb_private::unw_targettype_t m_libunwind_target_type; + lldb_private::unw_addr_space_t m_libunwind_addr_space; // libunwind address space object for this process. + bool m_waiting_for_attach; + + void + ResetGDBRemoteState (); + + bool + StartAsyncThread (); + + void + StopAsyncThread (); + + static void * + AsyncThread (void *arg); + + static bool + MonitorDebugserverProcess (void *callback_baton, + lldb::pid_t pid, + int signo, // Zero for no signal + int exit_status); // Exit value of process if signal is zero + + lldb::StateType + SetThreadStopInfo (StringExtractor& stop_packet); + + void + DidLaunchOrAttach (); + + lldb_private::Error + ConnectToDebugserver (const char *host_port); + + const char * + GetDispatchQueueNameForThread (lldb::addr_t thread_dispatch_qaddr, + std::string &dispatch_queue_name); + + static size_t + AttachInputReaderCallback (void *baton, + lldb_private::InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + +private: + //------------------------------------------------------------------ + // For ProcessGDBRemote only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ProcessGDBRemote); + + lldb_private::unw_addr_space_t + GetLibUnwindAddressSpace (); + + void + DestoryLibUnwindAddressSpace (); +}; + +#endif // liblldb_ProcessGDBRemote_h_ diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp new file mode 100644 index 00000000000..051ce8f20f4 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp @@ -0,0 +1,121 @@ +//===-- ProcessGDBRemoteLog.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessGDBRemoteLog.h" + +#include "lldb/Core/Args.h" +#include "lldb/Core/StreamFile.h" + +#include "ProcessGDBRemote.h" + +using namespace lldb; +using namespace lldb_private; + + +static Log* g_log = NULL; // Leak for now as auto_ptr was being cleaned up + // by global constructors before other threads + // were done with it. +Log * +ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (uint32_t mask) +{ + Log *log = g_log; + if (log && mask) + { + uint32_t log_mask = log->GetMask().GetAllFlagBits(); + if ((log_mask & mask) != mask) + return NULL; + } + return log; +} + +void +ProcessGDBRemoteLog::DisableLog () +{ + if (g_log) + { + delete g_log; + g_log = NULL; + } +} + +Log * +ProcessGDBRemoteLog::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, Args &args, Stream *feedback_strm) +{ + DisableLog (); + g_log = new Log (log_stream_sp); + if (g_log) + { + uint32_t flag_bits = 0; + bool got_unknown_category = false; + const size_t argc = args.GetArgumentCount(); + for (size_t i=0; i<argc; ++i) + { + const char *arg = args.GetArgumentAtIndex(i); + + if (::strcasecmp (arg, "all") == 0 ) flag_bits |= GDBR_LOG_ALL; + else if (::strcasestr (arg, "break") == arg ) flag_bits |= GDBR_LOG_BREAKPOINTS; + else if (::strcasecmp (arg, "default") == 0 ) flag_bits |= GDBR_LOG_DEFAULT; + else if (::strcasecmp (arg, "packets") == 0 ) flag_bits |= GDBR_LOG_PACKETS; + else if (::strcasecmp (arg, "memory") == 0 ) flag_bits |= GDBR_LOG_MEMORY; + else if (::strcasecmp (arg, "data-short") == 0 ) flag_bits |= GDBR_LOG_MEMORY_DATA_SHORT; + else if (::strcasecmp (arg, "data-long") == 0 ) flag_bits |= GDBR_LOG_MEMORY_DATA_LONG; + else if (::strcasecmp (arg, "process") == 0 ) flag_bits |= GDBR_LOG_PROCESS; + else if (::strcasecmp (arg, "step") == 0 ) flag_bits |= GDBR_LOG_STEP; + else if (::strcasecmp (arg, "thread") == 0 ) flag_bits |= GDBR_LOG_THREAD; + else if (::strcasecmp (arg, "verbose") == 0 ) flag_bits |= GDBR_LOG_VERBOSE; + else if (::strcasestr (arg, "watch") == arg ) flag_bits |= GDBR_LOG_WATCHPOINTS; + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + if (got_unknown_category == false) + { + got_unknown_category = true; + ListLogCategories (feedback_strm); + } + } + } + if (flag_bits == 0) + flag_bits = GDBR_LOG_DEFAULT; + g_log->GetMask().SetAllFlagBits(flag_bits); + g_log->GetOptions().SetAllFlagBits(log_options); + } + return g_log; +} + +void +ProcessGDBRemoteLog::ListLogCategories (Stream *strm) +{ + strm->Printf("Logging categories for '%s':\n" + "\tall - turn on all available logging categories\n" + "\tbreak - log breakpoints\n" + "\tdefault - enable the default set of logging categories for liblldb\n" + "\tpackets - log gdb remote packets\n" + "\tmemory - log memory reads and writes\n" + "\tdata-short - log memory bytes for memory reads and writes for short transactions only\n" + "\tdata-long - log memory bytes for memory reads and writes for all transactions\n" + "\tprocess - log process events and activities\n" + "\tthread - log thread events and activities\n" + "\tstep - log step related activities\n" + "\tverbose - enable verbose loggging\n" + "\twatch - log watchpoint related activities\n", ProcessGDBRemote::GetPluginNameStatic()); +} + + +void +ProcessGDBRemoteLog::LogIf (uint32_t mask, const char *format, ...) +{ + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (mask); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h new file mode 100644 index 00000000000..97580d38674 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h @@ -0,0 +1,53 @@ +//===-- ProcessGDBRemoteLog.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_ProcessGDBRemoteLog_h_ +#define liblldb_ProcessGDBRemoteLog_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +// Project includes +#include "lldb/Core/Log.h" + +#define GDBR_LOG_VERBOSE (1u << 0) +#define GDBR_LOG_PROCESS (1u << 1) +#define GDBR_LOG_THREAD (1u << 2) +#define GDBR_LOG_PACKETS (1u << 3) +#define GDBR_LOG_MEMORY (1u << 4) // Log memory reads/writes calls +#define GDBR_LOG_MEMORY_DATA_SHORT (1u << 5) // Log short memory reads/writes bytes +#define GDBR_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes +#define GDBR_LOG_BREAKPOINTS (1u << 7) +#define GDBR_LOG_WATCHPOINTS (1u << 8) +#define GDBR_LOG_STEP (1u << 9) +#define GDBR_LOG_COMM (1u << 10) +#define GDBR_LOG_ALL (UINT32_MAX) +#define GDBR_LOG_DEFAULT GDBR_LOG_PACKETS + +class ProcessGDBRemoteLog +{ +public: + static lldb_private::Log * + GetLogIfAllCategoriesSet(uint32_t mask = 0); + + static void + DisableLog (); + + static lldb_private::Log * + EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, lldb_private::Args &args, lldb_private::Stream *feedback_strm); + + static void + ListLogCategories (lldb_private::Stream *strm); + + static void + LogIf (uint32_t mask, const char *format, ...); +}; + +#endif // liblldb_ProcessGDBRemoteLog_h_ diff --git a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp new file mode 100644 index 00000000000..37485edf4e5 --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp @@ -0,0 +1,296 @@ +//===-- ThreadGDBRemote.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "ThreadGDBRemote.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Breakpoint/WatchpointLocation.h" + +#include "LibUnwindRegisterContext.h" +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" +#include "StringExtractorGDBRemote.h" +#include "UnwindLibUnwind.h" +#include "UnwindMacOSXFrameBackchain.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Thread Registers +//---------------------------------------------------------------------- + +ThreadGDBRemote::ThreadGDBRemote (ProcessGDBRemote &process, lldb::tid_t tid) : + Thread(process, tid), + m_stop_info_stop_id (0), + m_stop_info (this), + m_thread_name (), + m_dispatch_queue_name (), + m_thread_dispatch_qaddr (LLDB_INVALID_ADDRESS), + m_unwinder_ap () +{ +// ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD | GDBR_LOG_VERBOSE, "ThreadGDBRemote::ThreadGDBRemote ( pid = %i, tid = 0x%4.4x, )", m_process.GetID(), GetID()); + ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::ThreadGDBRemote (pid = %i, tid = 0x%4.4x)", this, m_process.GetID(), GetID()); +} + +ThreadGDBRemote::~ThreadGDBRemote () +{ + ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::~ThreadGDBRemote (pid = %i, tid = 0x%4.4x)", this, m_process.GetID(), GetID()); +} + + +const char * +ThreadGDBRemote::GetInfo () +{ + return NULL; +} + + +const char * +ThreadGDBRemote::GetName () +{ + if (m_thread_name.empty()) + return NULL; + return m_thread_name.c_str(); +} + + +const char * +ThreadGDBRemote::GetQueueName () +{ + // Always re-fetch the dispatch queue name since it can change + if (m_thread_dispatch_qaddr != 0 || m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) + return GetGDBProcess().GetDispatchQueueNameForThread (m_thread_dispatch_qaddr, m_dispatch_queue_name); + return NULL; +} + +bool +ThreadGDBRemote::WillResume (StateType resume_state) +{ + // TODO: cache for next time in case we can match things up?? + ClearStackFrames(); + int signo = GetResumeSignal(); + m_stop_info.Clear(); + switch (resume_state) + { + case eStateSuspended: + case eStateStopped: + // Don't append anything for threads that should stay stopped. + break; + + case eStateRunning: + if (m_process.GetUnixSignals().SignalIsValid (signo)) + GetGDBProcess().m_continue_packet.Printf(";C%2.2x:%4.4x", signo, GetID()); + else + GetGDBProcess().m_continue_packet.Printf(";c:%4.4x", GetID()); + break; + + case eStateStepping: + if (m_process.GetUnixSignals().SignalIsValid (signo)) + GetGDBProcess().m_continue_packet.Printf(";S%2.2x:%4.4x", signo, GetID()); + else + GetGDBProcess().m_continue_packet.Printf(";s:%4.4x", GetID()); + break; + } + Thread::WillResume(resume_state); + return true; +} + +void +ThreadGDBRemote::RefreshStateAfterStop() +{ + // Invalidate all registers in our register context + GetRegisterContext()->Invalidate(); +} + +Unwind * +ThreadGDBRemote::GetUnwinder () +{ + if (m_unwinder_ap.get() == NULL) + { + const ArchSpec target_arch (GetProcess().GetTarget().GetArchitecture ()); + if (target_arch == ArchSpec("x86_64") || target_arch == ArchSpec("i386")) + { + m_unwinder_ap.reset (new UnwindLibUnwind (*this, GetGDBProcess().GetLibUnwindAddressSpace())); + } + else + { + m_unwinder_ap.reset (new UnwindMacOSXFrameBackchain (*this)); + } + } + return m_unwinder_ap.get(); +} + +uint32_t +ThreadGDBRemote::GetStackFrameCount() +{ + Unwind *unwinder = GetUnwinder (); + if (unwinder) + return unwinder->GetFrameCount(); + return 0; +} + +// Make sure that GetStackFrameAtIndex() does NOT call GetStackFrameCount() when +// getting the stack frame at index zero! This way GetStackFrameCount() (via +// GetStackFRameData()) can call this function to get the first frame in order +// to provide the first frame to a lower call for efficiency sake (avoid +// redundant lookups in the frame symbol context). +lldb::StackFrameSP +ThreadGDBRemote::GetStackFrameAtIndex (uint32_t idx) +{ + + StackFrameSP frame_sp (m_frames.GetFrameAtIndex(idx)); + + if (frame_sp.get()) + return frame_sp; + + // Don't try and fetch a frame while process is running +// FIXME: This check isn't right because IsRunning checks the Public state, but this +// is work you need to do - for instance in ShouldStop & friends - before the public +// state has been changed. +// if (m_process.IsRunning()) +// return frame_sp; + + // Special case the first frame (idx == 0) so that we don't need to + // know how many stack frames there are to get it. If we need any other + // frames, then we do need to know if "idx" is a valid index. + if (idx == 0) + { + // If this is the first frame, we want to share the thread register + // context with the stack frame at index zero. + GetRegisterContext(); + assert (m_reg_context_sp.get()); + frame_sp.reset (new StackFrame (idx, *this, m_reg_context_sp, m_reg_context_sp->GetSP(), m_reg_context_sp->GetPC())); + } + else if (idx < GetStackFrameCount()) + { + Unwind *unwinder = GetUnwinder (); + if (unwinder) + { + addr_t pc, cfa; + if (unwinder->GetFrameInfoAtIndex(idx, cfa, pc)) + frame_sp.reset (new StackFrame (idx, *this, cfa, pc)); + } + } + m_frames.SetFrameAtIndex(idx, frame_sp); + return frame_sp; +} + +void +ThreadGDBRemote::ClearStackFrames () +{ + Unwind *unwinder = GetUnwinder (); + if (unwinder) + unwinder->Clear(); + Thread::ClearStackFrames(); +} + + +bool +ThreadGDBRemote::ThreadIDIsValid (lldb::tid_t thread) +{ + return thread != 0; +} + +void +ThreadGDBRemote::Dump(Log *log, uint32_t index) +{ +} + + +bool +ThreadGDBRemote::ShouldStop (bool &step_more) +{ + return true; +} +RegisterContext * +ThreadGDBRemote::GetRegisterContext () +{ + if (m_reg_context_sp.get() == NULL) + m_reg_context_sp.reset (CreateRegisterContextForFrame (NULL)); + return m_reg_context_sp.get(); +} + +RegisterContext * +ThreadGDBRemote::CreateRegisterContextForFrame (StackFrame *frame) +{ + const bool read_all_registers_at_once = false; + uint32_t frame_idx = 0; + + if (frame) + frame_idx = frame->GetID(); + + if (frame_idx == 0) + return new GDBRemoteRegisterContext (*this, frame, GetGDBProcess().m_register_info, read_all_registers_at_once); + else if (m_unwinder_ap.get() && frame_idx < m_unwinder_ap->GetFrameCount()) + return m_unwinder_ap->CreateRegisterContextForFrame (frame); + return NULL; +} + +bool +ThreadGDBRemote::SaveFrameZeroState (RegisterCheckpoint &checkpoint) +{ + lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0)); + if (frame_sp) + { + checkpoint.SetStackID(frame_sp->GetStackID()); + return frame_sp->GetRegisterContext()->ReadAllRegisterValues (checkpoint.GetData()); + } + return false; +} + +bool +ThreadGDBRemote::RestoreSaveFrameZero (const RegisterCheckpoint &checkpoint) +{ + lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0)); + if (frame_sp) + { + bool ret = frame_sp->GetRegisterContext()->WriteAllRegisterValues (checkpoint.GetData()); + frame_sp->GetRegisterContext()->Invalidate(); + ClearStackFrames(); + return ret; + } + return false; +} + +bool +ThreadGDBRemote::GetRawStopReason (StopInfo *stop_info) +{ + if (m_stop_info_stop_id != m_process.GetStopID()) + { + char packet[256]; + const int packet_len = snprintf(packet, sizeof(packet), "qThreadStopInfo%x", GetID()); + assert (packet_len < (sizeof(packet) - 1)); + StringExtractorGDBRemote stop_packet; + if (GetGDBProcess().GetGDBRemote().SendPacketAndWaitForResponse(packet, stop_packet, 1, false)) + { + std::string copy(stop_packet.GetStringRef()); + GetGDBProcess().SetThreadStopInfo (stop_packet); + // The process should have set the stop info stop ID and also + // filled this thread in with valid stop info + if (m_stop_info_stop_id != m_process.GetStopID()) + { + //ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "warning: qThreadStopInfo problem: '%s' => '%s'", packet, stop_packet.GetStringRef().c_str()); + printf("warning: qThreadStopInfo problem: '%s' => '%s'\n\torig '%s'\n", packet, stop_packet.GetStringRef().c_str(), copy.c_str()); /// REMOVE THIS + return false; + } + } + } + *stop_info = m_stop_info; + return true; +} + + diff --git a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h new file mode 100644 index 00000000000..3fa4ae09a2a --- /dev/null +++ b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h @@ -0,0 +1,156 @@ +//===-- ThreadGDBRemote.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_ThreadGDBRemote_h_ +#define liblldb_ThreadGDBRemote_h_ + +#include <string> + +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "MachException.h" +#include "libunwind.h" + +class StringExtractor; +class ProcessGDBRemote; + +class ThreadGDBRemote : public lldb_private::Thread +{ +public: + ThreadGDBRemote (ProcessGDBRemote &process, lldb::tid_t tid); + + virtual + ~ThreadGDBRemote (); + + virtual bool + WillResume (lldb::StateType resume_state); + + virtual void + RefreshStateAfterStop(); + + virtual const char * + GetInfo (); + + virtual const char * + GetName (); + + virtual const char * + GetQueueName (); + + virtual lldb_private::RegisterContext * + GetRegisterContext (); + + virtual lldb_private::RegisterContext * + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + virtual bool + SaveFrameZeroState (RegisterCheckpoint &checkpoint); + + virtual bool + RestoreSaveFrameZero (const RegisterCheckpoint &checkpoint); + + virtual uint32_t + GetStackFrameCount(); + + virtual lldb::StackFrameSP + GetStackFrameAtIndex (uint32_t idx); + + virtual void + ClearStackFrames (); + + ProcessGDBRemote & + GetGDBProcess () + { + return (ProcessGDBRemote &)m_process; + } + + const ProcessGDBRemote & + GetGDBProcess () const + { + return (ProcessGDBRemote &)m_process; + } + + void + Dump (lldb_private::Log *log, uint32_t index); + + static bool + ThreadIDIsValid (lldb::tid_t thread); + + bool + ShouldStop (bool &step_more); + + const char * + GetBasicInfoAsString (); + + lldb_private::Thread::StopInfo & + GetStopInfoRef () + { + return m_stop_info; + } + + uint32_t + GetStopInfoStopID() + { + return m_stop_info_stop_id; + } + + void + SetStopInfoStopID (uint32_t stop_id) + { + m_stop_info_stop_id = stop_id; + } + + void + SetName (const char *name) + { + if (name && name[0]) + m_thread_name.assign (name); + else + m_thread_name.clear(); + } + + lldb::addr_t + GetThreadDispatchQAddr () + { + return m_thread_dispatch_qaddr; + } + + void + SetThreadDispatchQAddr (lldb::addr_t thread_dispatch_qaddr) + { + m_thread_dispatch_qaddr = thread_dispatch_qaddr; + } + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + uint32_t m_stop_info_stop_id; + lldb_private::Thread::StopInfo m_stop_info; + std::string m_thread_name; + std::string m_dispatch_queue_name; + lldb::addr_t m_thread_dispatch_qaddr; + std::auto_ptr<lldb_private::Unwind> m_unwinder_ap; + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + + lldb_private::Unwind * + GetUnwinder (); + + void + SetStopInfoFromPacket (StringExtractor &stop_packet, uint32_t stop_id); + + virtual bool + GetRawStopReason (StopInfo *stop_info); + + +}; + +#endif // liblldb_ThreadGDBRemote_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp new file mode 100644 index 00000000000..989e49452fb --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp @@ -0,0 +1,211 @@ +//===-- DWARFAbbreviationDeclaration.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFAbbreviationDeclaration.h" + +#include "lldb/Core/dwarf.h" + +#include "DWARFFormValue.h" + +using namespace lldb_private; + +DWARFAbbreviationDeclaration::DWARFAbbreviationDeclaration() : + m_code (InvalidCode), + m_tag (0), + m_has_children (0), + m_attributes() +{ +} + +DWARFAbbreviationDeclaration::DWARFAbbreviationDeclaration(dw_tag_t tag, uint8_t has_children) : + m_code (InvalidCode), + m_tag (tag), + m_has_children (has_children), + m_attributes() +{ +} + +bool +DWARFAbbreviationDeclaration::Extract(const DataExtractor& data, uint32_t* offset_ptr) +{ + return Extract(data, offset_ptr, data.GetULEB128(offset_ptr)); +} + +bool +DWARFAbbreviationDeclaration::Extract(const DataExtractor& data, uint32_t* offset_ptr, dw_uleb128_t code) +{ + m_code = code; + m_attributes.clear(); + if (m_code) + { + m_tag = data.GetULEB128(offset_ptr); + m_has_children = data.GetU8(offset_ptr); + + while (data.ValidOffset(*offset_ptr)) + { + dw_attr_t attr = data.GetULEB128(offset_ptr); + dw_form_t form = data.GetULEB128(offset_ptr); + + if (attr && form) + m_attributes.push_back(DWARFAttribute(attr, form)); + else + break; + } + + return m_tag != 0; + } + else + { + m_tag = 0; + m_has_children = 0; + } + + return false; +} + + +void +DWARFAbbreviationDeclaration::Dump(Stream *s) const +{ +// *ostrm_ptr << std::setfill(' ') << std::dec << '[' << std::setw(3) << std::right << m_code << ']' << ' ' << std::setw(30) << std::left << DW_TAG_value_to_name(m_tag) << DW_CHILDREN_value_to_name(m_has_children) << std::endl; +// +// DWARFAttribute::const_iterator pos; +// +// for (pos = m_attributes.begin(); pos != m_attributes.end(); ++pos) +// *ostrm_ptr << " " << std::setw(29) << std::left << DW_AT_value_to_name(pos->attr()) << ' ' << DW_FORM_value_to_name(pos->form()) << std::endl; +// +// *ostrm_ptr << std::endl; +} + + + +bool +DWARFAbbreviationDeclaration::IsValid() +{ + return m_code != 0 && m_tag != 0; +} + + +void +DWARFAbbreviationDeclaration::CopyExcludingAddressAttributes(const DWARFAbbreviationDeclaration& abbr_decl, const uint32_t idx) +{ + m_code = abbr_decl.Code(); // Invalidate the code since that can't be copied safely. + m_tag = abbr_decl.Tag(); + m_has_children = abbr_decl.HasChildren(); + + const DWARFAttribute::collection& attributes = abbr_decl.Attributes(); + const uint32_t num_abbr_decl_attributes = attributes.size(); + + dw_attr_t attr; + dw_form_t form; + uint32_t i; + + for (i = 0; i < num_abbr_decl_attributes; ++i) + { + attributes[i].get(attr, form); + switch (attr) + { + case DW_AT_location: + case DW_AT_frame_base: + // Only add these if they are location expressions (have a single + // value) and not location lists (have a lists of location + // expressions which are only valid over specific address ranges) + if (DWARFFormValue::IsBlockForm(form)) + m_attributes.push_back(DWARFAttribute(attr, form)); + break; + + case DW_AT_low_pc: + case DW_AT_high_pc: + case DW_AT_ranges: + case DW_AT_entry_pc: + // Don't add these attributes + if (i >= idx) + break; + // Fall through and add attribute + default: + // Add anything that isn't address related + m_attributes.push_back(DWARFAttribute(attr, form)); + break; + } + } +} + +void +DWARFAbbreviationDeclaration::CopyChangingStringToStrp( + const DWARFAbbreviationDeclaration& abbr_decl, + const DataExtractor& debug_info_data, + dw_offset_t debug_info_offset, + const DWARFCompileUnit* cu, + const uint32_t strp_min_len +) +{ + m_code = InvalidCode; + m_tag = abbr_decl.Tag(); + m_has_children = abbr_decl.HasChildren(); + + const DWARFAttribute::collection& attributes = abbr_decl.Attributes(); + const uint32_t num_abbr_decl_attributes = attributes.size(); + + dw_attr_t attr; + dw_form_t form; + uint32_t i; + dw_offset_t offset = debug_info_offset; + + for (i = 0; i < num_abbr_decl_attributes; ++i) + { + attributes[i].get(attr, form); + dw_offset_t attr_offset = offset; + DWARFFormValue::SkipValue(form, debug_info_data, &offset, cu); + + if (form == DW_FORM_string && ((offset - attr_offset) >= strp_min_len)) + m_attributes.push_back(DWARFAttribute(attr, DW_FORM_strp)); + else + m_attributes.push_back(DWARFAttribute(attr, form)); + } +} + + +uint32_t +DWARFAbbreviationDeclaration::FindAttributeIndex(dw_attr_t attr) const +{ + uint32_t i; + const uint32_t kNumAttributes = m_attributes.size(); + for (i = 0; i < kNumAttributes; ++i) + { + if (m_attributes[i].get_attr() == attr) + return i; + } + return DW_INVALID_INDEX; +} + + +bool +DWARFAbbreviationDeclaration::operator == (const DWARFAbbreviationDeclaration& rhs) const +{ + return Tag() == rhs.Tag() + && HasChildren() == rhs.HasChildren() + && Attributes() == rhs.Attributes(); +} + +#if 0 +DWARFAbbreviationDeclaration::Append(BinaryStreamBuf& out_buff) const +{ + out_buff.Append32_as_ULEB128(Code()); + out_buff.Append32_as_ULEB128(Tag()); + out_buff.Append8(HasChildren()); + const uint32_t kNumAttributes = m_attributes.size(); + for (uint32_t i = 0; i < kNumAttributes; ++i) + { + out_buff.Append32_as_ULEB128(m_attributes[i].attr()); + out_buff.Append32_as_ULEB128(m_attributes[i].form()); + } + out_buff.Append8(0); // Output a zero for attr (faster than using Append32_as_ULEB128) + out_buff.Append8(0); // Output a zero for attr (faster than using Append32_as_ULEB128) +} +#endif // 0 diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h new file mode 100644 index 00000000000..7959f264dfe --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h @@ -0,0 +1,77 @@ +//===-- DWARFAbbreviationDeclaration.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_DWARFAbbreviationDeclaration_h_ +#define liblldb_DWARFAbbreviationDeclaration_h_ + +#include "SymbolFileDWARF.h" +#include "DWARFAttribute.h" + +class DWARFCompileUnit; + +class DWARFAbbreviationDeclaration +{ +public: + enum { InvalidCode = 0 }; + DWARFAbbreviationDeclaration(); + + // For hand crafting an abbreviation declaration + DWARFAbbreviationDeclaration(dw_tag_t tag, uint8_t has_children); + void AddAttribute(const DWARFAttribute& attr) + { + m_attributes.push_back(attr); + } + + dw_uleb128_t Code() const { return m_code; } + void SetCode(dw_uleb128_t code) { m_code = code; } + dw_tag_t Tag() const { return m_tag; } + bool HasChildren() const { return m_has_children; } + uint32_t NumAttributes() const { return m_attributes.size(); } + dw_attr_t GetAttrByIndex(uint32_t idx) const { return m_attributes.size() > idx ? m_attributes[idx].get_attr() : 0; } + dw_form_t GetFormByIndex(uint32_t idx) const { return m_attributes.size() > idx ? m_attributes[idx].get_form() : 0; } + bool GetAttrAndFormByIndex(uint32_t idx, dw_attr_t& attr, dw_form_t& form) const + { + if (m_attributes.size() > idx) + { + m_attributes[idx].get(attr, form); + return true; + } + attr = form = 0; + return false; + } + + // idx is assumed to be valid when calling GetAttrAndFormByIndexUnchecked() + void GetAttrAndFormByIndexUnchecked(uint32_t idx, dw_attr_t& attr, dw_form_t& form) const + { + m_attributes[idx].get(attr, form); + } + void CopyExcludingAddressAttributes(const DWARFAbbreviationDeclaration& abbr_decl, const uint32_t idx); + void CopyChangingStringToStrp( + const DWARFAbbreviationDeclaration& abbr_decl, + const lldb_private::DataExtractor& debug_info_data, + dw_offset_t debug_info_offset, + const DWARFCompileUnit* cu, + const uint32_t strp_min_len); + uint32_t FindAttributeIndex(dw_attr_t attr) const; + bool Extract(const lldb_private::DataExtractor& data, uint32_t* offset_ptr); + bool Extract(const lldb_private::DataExtractor& data, uint32_t* offset_ptr, dw_uleb128_t code); +// void Append(BinaryStreamBuf& out_buff) const; + bool IsValid(); + void Dump(lldb_private::Stream *s) const; + bool operator == (const DWARFAbbreviationDeclaration& rhs) const; +// DWARFAttribute::collection& Attributes() { return m_attributes; } + const DWARFAttribute::collection& Attributes() const { return m_attributes; } +protected: + dw_uleb128_t m_code; + dw_tag_t m_tag; + uint8_t m_has_children; + DWARFAttribute::collection m_attributes; +}; + +#endif // liblldb_DWARFAbbreviationDeclaration_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h new file mode 100644 index 00000000000..0b44926cba0 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h @@ -0,0 +1,45 @@ +//===-- DWARFAttribute.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_DWARFAttribute_h_ +#define liblldb_DWARFAttribute_h_ + +#include "DWARFDefines.h" +#include <vector> + +class DWARFAttribute +{ +public: + DWARFAttribute(dw_attr_t attr, dw_form_t form) : + m_attr_form ( attr << 16 | form ) + { + } + + void set(dw_attr_t attr, dw_form_t form) { m_attr_form = (attr << 16) | form; } + void set_attr(dw_attr_t attr) { m_attr_form = (m_attr_form & 0x0000ffffu) | (attr << 16); } + void set_form(dw_form_t form) { m_attr_form = (m_attr_form & 0xffff0000u) | form; } + dw_attr_t get_attr() const { return m_attr_form >> 16; } + dw_form_t get_form() const { return m_attr_form; } + void get(dw_attr_t& attr, dw_form_t& form) const + { + register uint32_t attr_form = m_attr_form; + attr = attr_form >> 16; + form = attr_form; + } + bool operator == (const DWARFAttribute& rhs) const { return m_attr_form == rhs.m_attr_form; } + typedef std::vector<DWARFAttribute> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + +protected: + uint32_t m_attr_form; // Upper 16 bits is attribute, lower 16 bits is form +}; + + +#endif // liblldb_DWARFAttribute_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp new file mode 100644 index 00000000000..e7f92c6eff2 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp @@ -0,0 +1,770 @@ +//===-- DWARFCompileUnit.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFCompileUnit.h" + +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" + +#include "DWARFDebugAbbrev.h" +#include "DWARFDebugAranges.h" +#include "DWARFDIECollection.h" +#include "DWARFFormValue.h" +#include "LogChannelDWARF.h" +#include "SymbolFileDWARF.h" + +using namespace lldb_private; +using namespace std; + +extern int g_verbose; + +DWARFCompileUnit::DWARFCompileUnit(SymbolFileDWARF* m_dwarf2Data) : + m_dwarf2Data ( m_dwarf2Data ), + m_offset ( DW_INVALID_OFFSET ), + m_length ( 0 ), + m_version ( 0 ), + m_abbrevs ( NULL ), + m_addr_size ( DWARFCompileUnit::GetDefaultAddressSize() ), + m_base_addr ( 0 ), + m_die_array (), + m_aranges_ap (), + m_user_data ( NULL ) +{ +} + +void +DWARFCompileUnit::Clear() +{ + m_offset = DW_INVALID_OFFSET; + m_length = 0; + m_version = 0; + m_abbrevs = NULL; + m_addr_size = DWARFCompileUnit::GetDefaultAddressSize(); + m_base_addr = 0; + m_die_array.clear(); + m_aranges_ap.reset(); + m_user_data = NULL; +} + +bool +DWARFCompileUnit::Extract(const DataExtractor &debug_info, uint32_t* offset_ptr) +{ + Clear(); + + m_offset = *offset_ptr; + + if (debug_info.ValidOffset(*offset_ptr)) + { + dw_offset_t abbr_offset; + const DWARFDebugAbbrev *abbr = m_dwarf2Data->DebugAbbrev(); + m_length = debug_info.GetU32(offset_ptr); + m_version = debug_info.GetU16(offset_ptr); + abbr_offset = debug_info.GetU32(offset_ptr); + m_addr_size = debug_info.GetU8 (offset_ptr); + + bool length_OK = debug_info.ValidOffset(GetNextCompileUnitOffset()-1); + bool version_OK = SymbolFileDWARF::SupportedVersion(m_version); + bool abbr_offset_OK = m_dwarf2Data->get_debug_abbrev_data().ValidOffset(abbr_offset); + bool addr_size_OK = ((m_addr_size == 4) || (m_addr_size == 8)); + + if (length_OK && version_OK && addr_size_OK && abbr_offset_OK && abbr != NULL) + { + m_abbrevs = abbr->GetAbbreviationDeclarationSet(abbr_offset); + return true; + } + + // reset the offset to where we tried to parse from if anything went wrong + *offset_ptr = m_offset; + } + + return false; +} + + +dw_offset_t +DWARFCompileUnit::Extract(dw_offset_t offset, const DataExtractor& debug_info_data, const DWARFAbbreviationDeclarationSet* abbrevs) +{ + Clear(); + + m_offset = offset; + + if (debug_info_data.ValidOffset(offset)) + { + m_length = debug_info_data.GetU32(&offset); + m_version = debug_info_data.GetU16(&offset); + bool abbrevs_OK = debug_info_data.GetU32(&offset) == abbrevs->GetOffset(); + m_abbrevs = abbrevs; + m_addr_size = debug_info_data.GetU8 (&offset); + + bool version_OK = SymbolFileDWARF::SupportedVersion(m_version); + bool addr_size_OK = ((m_addr_size == 4) || (m_addr_size == 8)); + + if (version_OK && addr_size_OK && abbrevs_OK && debug_info_data.ValidOffset(offset)) + return offset; + } + return DW_INVALID_OFFSET; +} + +void +DWARFCompileUnit::ClearDIEs(bool keep_compile_unit_die) +{ + if (m_die_array.size() > 1) + { + // std::vectors never get any smaller when resized to a smaller size, + // or when clear() or erase() are called, the size will report that it + // is smaller, but the memory allocated remains intact (call capacity() + // to see this). So we need to create a temporary vector and swap the + // contents which will cause just the internal pointers to be swapped + // so that when "tmp_array" goes out of scope, it will destroy the + // contents. + + // Save at least the compile unit DIE + DWARFDebugInfoEntry::collection tmp_array; + m_die_array.swap(tmp_array); + if (keep_compile_unit_die) + m_die_array.push_back(tmp_array.front()); + } +} + +//---------------------------------------------------------------------- +// ParseCompileUnitDIEsIfNeeded +// +// Parses a compile unit and indexes its DIEs if it already hasn't been +// done. +//---------------------------------------------------------------------- +size_t +DWARFCompileUnit::ExtractDIEsIfNeeded (bool cu_die_only) +{ + const size_t initial_die_array_size = m_die_array.size(); + if ((cu_die_only && initial_die_array_size > 0) || initial_die_array_size > 1) + return 0; // Already parsed + + Timer scoped_timer (__PRETTY_FUNCTION__, + "%8.8x: DWARFCompileUnit::ExtractDIEsIfNeeded( cu_die_only = %i )", + m_offset, + cu_die_only); + + // Set the offset to that of the first DIE + uint32_t offset = GetFirstDIEOffset(); + const dw_offset_t next_cu_offset = GetNextCompileUnitOffset(); + DWARFDebugInfoEntry die; + // Keep a flat array of the DIE for binary lookup by DIE offset + Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO); +// if (log) +// log->Printf("0x%8.8x: Compile Unit: length = 0x%8.8x, version = 0x%4.4x, abbr_offset = 0x%8.8x, addr_size = 0x%2.2x", +// cu->GetOffset(), +// cu->GetLength(), +// cu->GetVersion(), +// cu->GetAbbrevOffset(), +// cu->GetAddressByteSize()); + + uint32_t depth = 0; + // We are in our compile unit, parse starting at the offset + // we were told to parse + while (die.Extract(m_dwarf2Data, this, &offset)) + { + if (log) + log->Printf("0x%8.8x: %*.*s%s%s", + die.GetOffset(), + depth * 2, depth * 2, "", + DW_TAG_value_to_name (die.Tag()), + die.HasChildren() ? " *" : ""); + if (cu_die_only) + { + AddDIE(die); + return 1; + } + else if (depth == 0 && initial_die_array_size == 1) + { + // Don't append the CU die as we already did that + } + else + { + AddDIE(die); + } + + const DWARFAbbreviationDeclaration* abbrDecl = die.GetAbbreviationDeclarationPtr(); + if (abbrDecl) + { + // Normal DIE + if (abbrDecl->HasChildren()) + ++depth; + } + else + { + // NULL DIE. + if (depth > 0) + --depth; + else + break; // We are done with this compile unit! + } + + assert(offset <= next_cu_offset); + } + SetDIERelations(); + return m_die_array.size(); +} + + +dw_offset_t +DWARFCompileUnit::GetAbbrevOffset() const +{ + return m_abbrevs ? m_abbrevs->GetOffset() : DW_INVALID_OFFSET; +} + + + +bool +DWARFCompileUnit::Verify(Stream *s) const +{ + const DataExtractor& debug_info = m_dwarf2Data->get_debug_info_data(); + bool valid_offset = debug_info.ValidOffset(m_offset); + bool length_OK = debug_info.ValidOffset(GetNextCompileUnitOffset()-1); + bool version_OK = SymbolFileDWARF::SupportedVersion(m_version); + bool abbr_offset_OK = m_dwarf2Data->get_debug_abbrev_data().ValidOffset(GetAbbrevOffset()); + bool addr_size_OK = ((m_addr_size == 4) || (m_addr_size == 8)); + bool verbose = s->GetVerbose(); + if (valid_offset && length_OK && version_OK && addr_size_OK && abbr_offset_OK) + { + if (verbose) + s->Printf(" 0x%8.8x: OK\n", m_offset); + return true; + } + else + { + s->Printf(" 0x%8.8x: ", m_offset); + + m_dwarf2Data->get_debug_info_data().Dump (s, m_offset, lldb::eFormatHex, 1, Size(), 32, LLDB_INVALID_ADDRESS, 0, 0); + s->EOL(); + if (valid_offset) + { + if (!length_OK) + s->Printf(" The length (0x%8.8x) for this compile unit is too large for the .debug_info provided.\n", m_length); + if (!version_OK) + s->Printf(" The 16 bit compile unit header version is not supported.\n"); + if (!abbr_offset_OK) + s->Printf(" The offset into the .debug_abbrev section (0x%8.8x) is not valid.\n", GetAbbrevOffset()); + if (!addr_size_OK) + s->Printf(" The address size is unsupported: 0x%2.2x\n", m_addr_size); + } + else + s->Printf(" The start offset of the compile unit header in the .debug_info is invalid.\n"); + } + return false; +} + + +void +DWARFCompileUnit::Dump(Stream *s) const +{ + s->Printf("0x%8.8x: Compile Unit: length = 0x%8.8x, version = 0x%4.4x, abbr_offset = 0x%8.8x, addr_size = 0x%2.2x (next CU at {0x%8.8x})\n", + m_offset, m_length, m_version, GetAbbrevOffset(), m_addr_size, GetNextCompileUnitOffset()); +} + + +static uint8_t g_default_addr_size = 4; + +uint8_t +DWARFCompileUnit::GetAddressByteSize(const DWARFCompileUnit* cu) +{ + if (cu) + return cu->GetAddressByteSize(); + return DWARFCompileUnit::GetDefaultAddressSize(); +} + +uint8_t +DWARFCompileUnit::GetDefaultAddressSize() +{ + return g_default_addr_size; +} + +void +DWARFCompileUnit::SetDefaultAddressSize(uint8_t addr_size) +{ + g_default_addr_size = addr_size; +} + +bool +DWARFCompileUnit::LookupAddress +( + const dw_addr_t address, + DWARFDebugInfoEntry** function_die_handle, + DWARFDebugInfoEntry** block_die_handle +) +{ + bool success = false; + + if (function_die_handle != NULL && DIE()) + { + if (m_aranges_ap.get() == NULL) + { + m_aranges_ap.reset(new DWARFDebugAranges()); + m_die_array.front().BuildFunctionAddressRangeTable(m_dwarf2Data, this, m_aranges_ap.get()); + } + + // Re-check the aranges auto pointer contents in case it was created above + if (m_aranges_ap.get() != NULL) + { + *function_die_handle = GetDIEPtr(m_aranges_ap->FindAddress(address)); + if (*function_die_handle != NULL) + { + success = true; + if (block_die_handle != NULL) + { + DWARFDebugInfoEntry* child = (*function_die_handle)->GetFirstChild(); + while (child) + { + if (child->LookupAddress(address, m_dwarf2Data, this, NULL, block_die_handle)) + break; + child = child->GetSibling(); + } + } + } + } + } + return success; +} + +//---------------------------------------------------------------------- +// SetDIERelations() +// +// We read in all of the DIE entries into our flat list of DIE entries +// and now we need to go back through all of them and set the parent, +// sibling and child pointers for quick DIE navigation. +//---------------------------------------------------------------------- +void +DWARFCompileUnit::SetDIERelations() +{ +#if 0 + // Compute average bytes per DIE + // + // We can figure out what the average number of bytes per DIE is + // to help us pre-allocate the correct number of m_die_array + // entries so we don't end up doing a lot of memory copies as we + // are creating our DIE array when parsing + // + // Enable this code by changing "#if 0" above to "#if 1" and running + // the dsymutil or dwarfdump with a bunch of dwarf files and see what + // the running average ends up being in the stdout log. + static size_t g_total_cu_debug_info_size = 0; + static size_t g_total_num_dies = 0; + static size_t g_min_bytes_per_die = UINT_MAX; + static size_t g_max_bytes_per_die = 0; + const size_t num_dies = m_die_array.size(); + const size_t cu_debug_info_size = GetDebugInfoSize(); + const size_t bytes_per_die = cu_debug_info_size / num_dies; + if (g_min_bytes_per_die > bytes_per_die) + g_min_bytes_per_die = bytes_per_die; + if (g_max_bytes_per_die < bytes_per_die) + g_max_bytes_per_die = bytes_per_die; + if (g_total_cu_debug_info_size == 0) + { + cout << " min max avg" << endl + << "n dies cu size bpd bpd bpd bpd" << endl + << "------ -------- --- === === ===" << endl; + } + g_total_cu_debug_info_size += cu_debug_info_size; + g_total_num_dies += num_dies; + const size_t avg_bytes_per_die = g_total_cu_debug_info_size / g_total_num_dies; + cout + << DECIMAL_WIDTH(6) << num_dies << ' ' + << DECIMAL_WIDTH(8) << cu_debug_info_size << ' ' + << DECIMAL_WIDTH(3) << bytes_per_die << ' ' + << DECIMAL_WIDTH(3) << g_min_bytes_per_die << ' ' + << DECIMAL_WIDTH(3) << g_max_bytes_per_die << ' ' + << DECIMAL_WIDTH(3) << avg_bytes_per_die + << endl; +#endif + if (m_die_array.empty()) + return; + DWARFDebugInfoEntry* die_array_begin = &m_die_array.front(); + DWARFDebugInfoEntry* die_array_end = &m_die_array.back(); + DWARFDebugInfoEntry* curr_die; + // We purposely are skipping the last element in the array in the loop below + // so that we can always have a valid next item + for (curr_die = die_array_begin; curr_die < die_array_end; ++curr_die) + { + // Since our loop doesn't include the last element, we can always + // safely access the next die in the array. + DWARFDebugInfoEntry* next_die = curr_die + 1; + + const DWARFAbbreviationDeclaration* curr_die_abbrev = curr_die->GetAbbreviationDeclarationPtr(); + + if (curr_die_abbrev) + { + // Normal DIE + if (curr_die_abbrev->HasChildren()) + next_die->SetParent(curr_die); + else + curr_die->SetSibling(next_die); + } + else + { + // NULL DIE that terminates a sibling chain + DWARFDebugInfoEntry* parent = curr_die->GetParent(); + if (parent) + parent->SetSibling(next_die); + } + } + + // Since we skipped the last element, we need to fix it up! + if (die_array_begin < die_array_end) + curr_die->SetParent(die_array_begin); + +#if 0 + // The code below will dump the DIE relations in case any modification + // is done to the above code. This dump can be used in a diff to make + // sure that no functionality is lost. + { + DWARFDebugInfoEntry::const_iterator pos; + DWARFDebugInfoEntry::const_iterator end = m_die_array.end(); + puts("offset parent sibling child"); + puts("-------- -------- -------- --------"); + for (pos = m_die_array.begin(); pos != end; ++pos) + { + const DWARFDebugInfoEntry& die_ref = *pos; + const DWARFDebugInfoEntry* p = die_ref.GetParent(); + const DWARFDebugInfoEntry* s = die_ref.GetSibling(); + const DWARFDebugInfoEntry* c = die_ref.GetFirstChild(); + printf("%.8x: %.8x %.8x %.8x\n", die_ref.GetOffset(), + p ? p->GetOffset() : 0, + s ? s->GetOffset() : 0, + c ? c->GetOffset() : 0); + } + } +#endif + +} +//---------------------------------------------------------------------- +// Compare function DWARFDebugAranges::Range structures +//---------------------------------------------------------------------- +static bool CompareDIEOffset (const DWARFDebugInfoEntry& die1, const DWARFDebugInfoEntry& die2) +{ + return die1.GetOffset() < die2.GetOffset(); +} + +//---------------------------------------------------------------------- +// GetDIEPtr() +// +// Get the DIE (Debug Information Entry) with the specified offset. +//---------------------------------------------------------------------- +DWARFDebugInfoEntry* +DWARFCompileUnit::GetDIEPtr(dw_offset_t die_offset) +{ + if (die_offset != DW_INVALID_OFFSET) + { + ExtractDIEsIfNeeded (false); + DWARFDebugInfoEntry compare_die; + compare_die.SetOffset(die_offset); + DWARFDebugInfoEntry::iterator end = m_die_array.end(); + DWARFDebugInfoEntry::iterator pos = lower_bound(m_die_array.begin(), end, compare_die, CompareDIEOffset); + if (pos != end) + { + if (die_offset == (*pos).GetOffset()) + return &(*pos); + } + } + return NULL; // Not found in any compile units +} + +//---------------------------------------------------------------------- +// GetDIEPtrContainingOffset() +// +// Get the DIE (Debug Information Entry) that contains the specified +// .debug_info offset. +//---------------------------------------------------------------------- +const DWARFDebugInfoEntry* +DWARFCompileUnit::GetDIEPtrContainingOffset(dw_offset_t die_offset) +{ + if (die_offset != DW_INVALID_OFFSET) + { + ExtractDIEsIfNeeded (false); + DWARFDebugInfoEntry compare_die; + compare_die.SetOffset(die_offset); + DWARFDebugInfoEntry::iterator end = m_die_array.end(); + DWARFDebugInfoEntry::iterator pos = lower_bound(m_die_array.begin(), end, compare_die, CompareDIEOffset); + if (pos != end) + { + if (die_offset >= (*pos).GetOffset()) + { + DWARFDebugInfoEntry::iterator next = pos + 1; + if (next != end) + { + if (die_offset < (*next).GetOffset()) + return &(*pos); + } + } + } + } + return NULL; // Not found in any compile units +} + + + +size_t +DWARFCompileUnit::AppendDIEsWithTag (const dw_tag_t tag, DWARFDIECollection& dies, uint32_t depth) const +{ + size_t old_size = dies.Size(); + DWARFDebugInfoEntry::const_iterator pos; + DWARFDebugInfoEntry::const_iterator end = m_die_array.end(); + for (pos = m_die_array.begin(); pos != end; ++pos) + { + if (pos->Tag() == tag) + dies.Insert(&(*pos)); + } + + // Return the number of DIEs added to the collection + return dies.Size() - old_size; +} + +void +DWARFCompileUnit::AddGlobalDIEByIndex (uint32_t die_idx) +{ + m_global_die_indexes.push_back (die_idx); +} + + +void +DWARFCompileUnit::AddGlobal (const DWARFDebugInfoEntry* die) +{ + // Indexes to all file level global and static variables + m_global_die_indexes; + + if (m_die_array.empty()) + return; + + const DWARFDebugInfoEntry* first_die = &m_die_array[0]; + const DWARFDebugInfoEntry* end = first_die + m_die_array.size(); + if (first_die <= die && die < end) + m_global_die_indexes.push_back (die - first_die); +} + + +void +DWARFCompileUnit::Index +( + lldb_private::UniqueCStringMap<dw_offset_t>& name_to_function_die, + lldb_private::UniqueCStringMap<dw_offset_t>& name_to_inlined_die, + lldb_private::UniqueCStringMap<dw_offset_t>& name_to_global_die, + lldb_private::UniqueCStringMap<dw_offset_t>& name_to_type_die +) +{ + + const DataExtractor* debug_str = &m_dwarf2Data->get_debug_str_data(); + + DWARFDebugInfoEntry::const_iterator pos; + DWARFDebugInfoEntry::const_iterator begin = m_die_array.begin(); + DWARFDebugInfoEntry::const_iterator end = m_die_array.end(); + for (pos = begin; pos != end; ++pos) + { + const DWARFDebugInfoEntry &die = *pos; + + const dw_tag_t tag = die.Tag(); + + switch (tag) + { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_base_type: + case DW_TAG_class_type: + case DW_TAG_constant: + case DW_TAG_enumeration_type: + case DW_TAG_string_type: + case DW_TAG_subroutine_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_typedef: + case DW_TAG_namespace: + case DW_TAG_variable: + break; + + default: + continue; + } + + DWARFDebugInfoEntry::Attributes attributes; + const char *name = NULL; + const char *mangled = NULL; + bool is_variable = false; + bool is_declaration = false; + bool is_artificial = false; + bool has_address = false; + bool has_location = false; + bool is_global_or_static_variable = false; + const size_t num_attributes = die.GetAttributes(m_dwarf2Data, this, attributes); + if (num_attributes > 0) + { + uint32_t i; + + dw_tag_t tag = die.Tag(); + + is_variable = tag == DW_TAG_variable; + + for (i=0; i<num_attributes; ++i) + { + dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + switch (attr) + { + case DW_AT_name: + if (attributes.ExtractFormValueAtIndex(m_dwarf2Data, i, form_value)) + name = form_value.AsCString(debug_str); + break; + + case DW_AT_declaration: + if (attributes.ExtractFormValueAtIndex(m_dwarf2Data, i, form_value)) + is_declaration = form_value.Unsigned() != 0; + break; + + case DW_AT_artificial: + if (attributes.ExtractFormValueAtIndex(m_dwarf2Data, i, form_value)) + is_artificial = form_value.Unsigned() != 0; + break; + + case DW_AT_MIPS_linkage_name: + if (attributes.ExtractFormValueAtIndex(m_dwarf2Data, i, form_value)) + mangled = form_value.AsCString(debug_str); + break; + + case DW_AT_low_pc: + case DW_AT_ranges: + case DW_AT_entry_pc: + has_address = true; + break; + + case DW_AT_location: + has_location = true; + if (tag == DW_TAG_variable) + { + const DWARFDebugInfoEntry* parent_die = die.GetParent(); + while ( parent_die != NULL ) + { + switch (parent_die->Tag()) + { + case DW_TAG_subprogram: + case DW_TAG_lexical_block: + case DW_TAG_inlined_subroutine: + // Even if this is a function level static, we don't add it. We could theoretically + // add these if we wanted to by introspecting into the DW_AT_location and seeing + // if the location describes a hard coded address, but we dont want the performance + // penalty of that right now. + is_global_or_static_variable = false; +// if (attributes.ExtractFormValueAtIndex(dwarf2Data, i, form_value)) +// { +// // If we have valid block data, then we have location expression bytes +// // that are fixed (not a location list). +// const uint8_t *block_data = form_value.BlockData(); +// if (block_data) +// { +// uint32_t block_length = form_value.Unsigned(); +// if (block_length == 1 + attributes.CompileUnitAtIndex(i)->GetAddressByteSize()) +// { +// if (block_data[0] == DW_OP_addr) +// add_die = true; +// } +// } +// } + parent_die = NULL; // Terminate the while loop. + break; + + case DW_TAG_compile_unit: + is_global_or_static_variable = true; + parent_die = NULL; // Terminate the while loop. + break; + + default: + parent_die = parent_die->GetParent(); // Keep going in the while loop. + break; + } + } + } + break; + } + } + } + + switch (tag) + { + case DW_TAG_subprogram: + if (has_address) + { + if (name && name[0]) + { + if ((name[0] == '-' || name[0] == '+') && name[1] == '[') + { + int name_len = strlen (name); + // Objective C methods must have at least: + // "-[" or "+[" prefix + // One character for a class name + // One character for the space between the class name + // One character for the method name + // "]" suffix + if (name_len >= 6 && name[name_len - 1] == ']') + { + const char *method_name = strchr (name, ' '); + if (method_name) + { + // Skip the space + ++method_name; + // Extract the objective C basename and add it to the + // accelerator tables + size_t method_name_len = name_len - (method_name - name) - 1; + ConstString method_const_str (method_name, method_name_len); + name_to_function_die.Append(method_const_str.AsCString(), die.GetOffset()); + } + } + } + name_to_function_die.Append(ConstString(name).AsCString(), die.GetOffset()); + } + if (mangled && mangled[0]) + name_to_function_die.Append(ConstString(mangled).AsCString(), die.GetOffset()); + } + break; + + case DW_TAG_inlined_subroutine: + if (has_address) + { + if (name && name[0]) + name_to_inlined_die.Append(ConstString(name).AsCString(), die.GetOffset()); + if (mangled && mangled[0]) + name_to_inlined_die.Append(ConstString(mangled).AsCString(), die.GetOffset()); + } + break; + + case DW_TAG_base_type: + case DW_TAG_class_type: + case DW_TAG_constant: + case DW_TAG_enumeration_type: + case DW_TAG_string_type: + case DW_TAG_subroutine_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_typedef: + case DW_TAG_namespace: + if (name && is_declaration == false) + { + name_to_type_die.Append(ConstString(name).AsCString(), die.GetOffset()); + } + break; + + case DW_TAG_variable: + if (name && has_location && is_global_or_static_variable) + { + AddGlobalDIEByIndex (std::distance (begin, pos)); + name_to_global_die.Append(ConstString(name).AsCString(), die.GetOffset()); + } + break; + + default: + continue; + } + } +} + + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h new file mode 100644 index 00000000000..44bbbfe5ba4 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h @@ -0,0 +1,168 @@ +//===-- DWARFCompileUnit.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_DWARFCompileUnit_h_ +#define SymbolFileDWARF_DWARFCompileUnit_h_ + +#include "SymbolFileDWARF.h" +#include "DWARFDebugInfoEntry.h" + +class DWARFCompileUnit +{ +public: + DWARFCompileUnit(SymbolFileDWARF* dwarf2Data); + + bool Extract(const lldb_private::DataExtractor &debug_info, uint32_t* offset_ptr); + dw_offset_t Extract(dw_offset_t offset, const lldb_private::DataExtractor& debug_info_data, const DWARFAbbreviationDeclarationSet* abbrevs); + size_t ExtractDIEsIfNeeded (bool cu_die_only); + bool LookupAddress( + const dw_addr_t address, + DWARFDebugInfoEntry** function_die, + DWARFDebugInfoEntry** block_die); + + size_t AppendDIEsWithTag (const dw_tag_t tag, DWARFDIECollection& matching_dies, uint32_t depth = UINT_MAX) const; + void Clear(); + bool Verify(lldb_private::Stream *s) const; + void Dump(lldb_private::Stream *s) const; + dw_offset_t GetOffset() const { return m_offset; } + uint32_t Size() const { return 11; /* Size in bytes of the compile unit header */ } + bool ContainsDIEOffset(dw_offset_t die_offset) const { return die_offset >= GetFirstDIEOffset() && die_offset < GetNextCompileUnitOffset(); } + dw_offset_t GetFirstDIEOffset() const { return m_offset + Size(); } + dw_offset_t GetNextCompileUnitOffset() const { return m_offset + m_length + 4; } + size_t GetDebugInfoSize() const { return m_length + 4 - Size(); /* Size in bytes of the .debug_info data associated with this compile unit. */ } + uint32_t GetLength() const { return m_length; } + uint16_t GetVersion() const { return m_version; } + const DWARFAbbreviationDeclarationSet* GetAbbreviations() const { return m_abbrevs; } + dw_offset_t GetAbbrevOffset() const; + uint8_t GetAddressByteSize() const { return m_addr_size; } + dw_addr_t GetBaseAddress() const { return m_base_addr; } + void ClearDIEs(bool keep_compile_unit_die); + + void + SetBaseAddress(dw_addr_t base_addr) + { + m_base_addr = base_addr; + } + + void + SetDIERelations(); + + const DWARFDebugInfoEntry* + GetCompileUnitDIEOnly() + { + ExtractDIEsIfNeeded (true); + if (m_die_array.empty()) + return NULL; + return &m_die_array[0]; + } + + const DWARFDebugInfoEntry* + DIE() + { + ExtractDIEsIfNeeded (false); + if (m_die_array.empty()) + return NULL; + return &m_die_array[0]; + } + + void + AddDIE(DWARFDebugInfoEntry& die) + { + // The average bytes per DIE entry has been seen to be + // around 14-20 so lets pre-reserve the needed memory for + // our DIE entries accordingly. Search forward for "Compute + // average bytes per DIE" to see #if'ed out code that does + // that determination. + + // Only reserve the memory if we are adding children of + // the main compile unit DIE. The compile unit DIE is always + // the first entry, so if our size is 1, then we are adding + // the first compile unit child DIE and should reserve + // the memory. + if (m_die_array.empty()) + m_die_array.reserve(GetDebugInfoSize() / 14); + m_die_array.push_back(die); + } + + DWARFDebugInfoEntry* + GetDIEPtr (dw_offset_t die_offset); + + const DWARFDebugInfoEntry* + GetDIEPtrContainingOffset (dw_offset_t die_offset); + + static uint8_t + GetAddressByteSize(const DWARFCompileUnit* cu); + + static uint8_t + GetDefaultAddressSize(); + static void + SetDefaultAddressSize(uint8_t addr_size); + + void * + GetUserData() const + { + return m_user_data; + } + + void + SetUserData(void *d) + { + m_user_data = d; + } + + + void + AddGlobalDIEByIndex (uint32_t die_idx); + + void + AddGlobal (const DWARFDebugInfoEntry* die); + + size_t + GetNumGlobals () const + { + return m_global_die_indexes.size(); + } + + const DWARFDebugInfoEntry * + GetGlobalDIEAtIndex (uint32_t idx) + { + if (idx < m_global_die_indexes.size()) + { + uint32_t die_idx = m_global_die_indexes[idx]; + if (die_idx < m_die_array.size()) + return &m_die_array[die_idx]; + } + return NULL; + } + + void + Index (lldb_private::UniqueCStringMap<dw_offset_t>& name_to_function_die, + lldb_private::UniqueCStringMap<dw_offset_t>& name_to_inlined_die, + lldb_private::UniqueCStringMap<dw_offset_t>& name_to_global_die, + lldb_private::UniqueCStringMap<dw_offset_t>& name_to_type_die); + +protected: + SymbolFileDWARF* m_dwarf2Data; + dw_offset_t m_offset; + uint32_t m_length; + uint16_t m_version; + const DWARFAbbreviationDeclarationSet* + m_abbrevs; + uint8_t m_addr_size; + dw_addr_t m_base_addr; + DWARFDebugInfoEntry::collection + m_die_array; // The compile unit debug information entry item + std::auto_ptr<DWARFDebugAranges> m_aranges_ap; // A table similar to the .debug_aranges table, but this one points to the exact DW_TAG_subprogram DIEs + std::vector<uint32_t> m_global_die_indexes; // Indexes to all file level global and static variables + void * m_user_data; +private: + DISALLOW_COPY_AND_ASSIGN (DWARFCompileUnit); +}; + +#endif // SymbolFileDWARF_DWARFCompileUnit_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.cpp new file mode 100644 index 00000000000..60aac74ad86 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.cpp @@ -0,0 +1,56 @@ +//===-- DWARFDIECollection.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDIECollection.h" + +#include <algorithm> + +#include "lldb/Core/Stream.h" + +#include "DWARFDebugInfoEntry.h" + +using namespace lldb_private; +using namespace std; + +bool +DWARFDIECollection::Insert(const DWARFDebugInfoEntry *die) +{ + iterator end_pos = m_dies.end(); + iterator insert_pos = upper_bound(m_dies.begin(), end_pos, die); + if (insert_pos != end_pos && (*insert_pos == die)) + return false; + m_dies.insert(insert_pos, die); + return true; +} + +const DWARFDebugInfoEntry * +DWARFDIECollection::GetDIEPtrAtIndex(uint32_t idx) const +{ + if (idx < m_dies.size()) + return m_dies[idx]; + return NULL; +} + + +size_t +DWARFDIECollection::Size() const +{ + return m_dies.size(); +} + +void +DWARFDIECollection::Dump(Stream *s, const char* title) const +{ + if (title && title[0] != '\0') + s->Printf( "%s\n", title); + const_iterator end_pos = m_dies.end(); + const_iterator pos; + for (pos = m_dies.begin(); pos != end_pos; ++pos) + s->Printf( "0x%8.8x\n", (*pos)->GetOffset()); +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.h new file mode 100644 index 00000000000..c374a148c6c --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.h @@ -0,0 +1,48 @@ +//===-- DWARFDIECollection.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_DWARFDIECollection_h_ +#define SymbolFileDWARF_DWARFDIECollection_h_ + +#include "SymbolFileDWARF.h" +#include <vector> + +class DWARFDIECollection +{ +public: + DWARFDIECollection() : + m_dies() + { + } + ~DWARFDIECollection() + { + } + + void + Dump(lldb_private::Stream *s, const char* title) const; + + const DWARFDebugInfoEntry* + GetDIEPtrAtIndex(uint32_t idx) const; + + bool + Insert(const DWARFDebugInfoEntry *die); + + size_t + Size() const; + +protected: + typedef std::vector<const DWARFDebugInfoEntry *> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + collection m_dies; // Ordered list of die offsets +}; + + +#endif // SymbolFileDWARF_DWARFDIECollection_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp new file mode 100644 index 00000000000..af6fc8e93e7 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp @@ -0,0 +1,202 @@ +//===-- DWARFDebugAbbrev.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugAbbrev.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; +using namespace std; + +//---------------------------------------------------------------------- +// DWARFAbbreviationDeclarationSet::Clear() +//---------------------------------------------------------------------- +void +DWARFAbbreviationDeclarationSet::Clear() +{ + m_idx_offset = 0; + m_decls.clear(); +} + + +//---------------------------------------------------------------------- +// DWARFAbbreviationDeclarationSet::Extract() +//---------------------------------------------------------------------- +bool +DWARFAbbreviationDeclarationSet::Extract(const DataExtractor& data, uint32_t* offset_ptr) +{ + const uint32_t begin_offset = *offset_ptr; + m_offset = begin_offset; + Clear(); + DWARFAbbreviationDeclaration abbrevDeclaration; + dw_uleb128_t prev_abbr_code = 0; + while (abbrevDeclaration.Extract(data, offset_ptr)) + { + m_decls.push_back(abbrevDeclaration); + if (m_idx_offset == 0) + m_idx_offset = abbrevDeclaration.Code(); + else + { + if (prev_abbr_code + 1 != abbrevDeclaration.Code()) + m_idx_offset = UINT_MAX; // Out of order indexes, we can't do O(1) lookups... + } + prev_abbr_code = abbrevDeclaration.Code(); + } + return begin_offset != *offset_ptr; +} + + +//---------------------------------------------------------------------- +// DWARFAbbreviationDeclarationSet::Dump() +//---------------------------------------------------------------------- +void +DWARFAbbreviationDeclarationSet::Dump(Stream *s) const +{ + std::for_each (m_decls.begin(), m_decls.end(), bind2nd(std::mem_fun_ref(&DWARFAbbreviationDeclaration::Dump),s)); +} + + +//---------------------------------------------------------------------- +// DWARFAbbreviationDeclarationSet::GetAbbreviationDeclaration() +//---------------------------------------------------------------------- +const DWARFAbbreviationDeclaration* +DWARFAbbreviationDeclarationSet::GetAbbreviationDeclaration(dw_uleb128_t abbrCode) const +{ + if (m_idx_offset == UINT_MAX) + { + DWARFAbbreviationDeclarationCollConstIter pos; + DWARFAbbreviationDeclarationCollConstIter end = m_decls.end(); + for (pos = m_decls.begin(); pos != end; ++pos) + { + if (pos->Code() == abbrCode) + return &(*pos); + } + } + else + { + uint32_t idx = abbrCode - m_idx_offset; + if (idx < m_decls.size()) + return &m_decls[idx]; + } + return NULL; +} + +//---------------------------------------------------------------------- +// DWARFAbbreviationDeclarationSet::AppendAbbrevDeclSequential() +// +// Append an abbreviation declaration with a sequential code for O(n) +// lookups. Handy when creating an DWARFAbbreviationDeclarationSet. +//---------------------------------------------------------------------- +dw_uleb128_t +DWARFAbbreviationDeclarationSet::AppendAbbrevDeclSequential(const DWARFAbbreviationDeclaration& abbrevDecl) +{ + // Get the next abbreviation code based on our current array size + dw_uleb128_t code = m_decls.size()+1; + + // Push the new declaration on the back + m_decls.push_back(abbrevDecl); + + // Update the code for this new declaration + m_decls.back().SetCode(code); + + return code; // return the new abbreviation code! +} + + +//---------------------------------------------------------------------- +// Encode +// +// Encode the abbreviation table onto the end of the buffer provided +// into a byte represenation as would be found in a ".debug_abbrev" +// debug information section. +//---------------------------------------------------------------------- +//void +//DWARFAbbreviationDeclarationSet::Encode(BinaryStreamBuf& debug_abbrev_buf) const +//{ +// DWARFAbbreviationDeclarationCollConstIter pos; +// DWARFAbbreviationDeclarationCollConstIter end = m_decls.end(); +// for (pos = m_decls.begin(); pos != end; ++pos) +// pos->Append(debug_abbrev_buf); +// debug_abbrev_buf.Append8(0); +//} + + +//---------------------------------------------------------------------- +// DWARFDebugAbbrev constructor +//---------------------------------------------------------------------- +DWARFDebugAbbrev::DWARFDebugAbbrev() : + m_abbrevCollMap(), + m_prev_abbr_offset_pos(m_abbrevCollMap.end()) +{ +} + + +//---------------------------------------------------------------------- +// DWARFDebugAbbrev::Parse() +//---------------------------------------------------------------------- +void +DWARFDebugAbbrev::Parse(const DataExtractor& data) +{ + uint32_t offset = 0; + + while (data.ValidOffset(offset)) + { + uint32_t initial_cu_offset = offset; + DWARFAbbreviationDeclarationSet abbrevDeclSet; + + if (abbrevDeclSet.Extract(data, &offset)) + m_abbrevCollMap[initial_cu_offset] = abbrevDeclSet; + else + break; + } + m_prev_abbr_offset_pos = m_abbrevCollMap.end(); +} + +//---------------------------------------------------------------------- +// DWARFDebugAbbrev::Dump() +//---------------------------------------------------------------------- +void +DWARFDebugAbbrev::Dump(Stream *s) const +{ + if (m_abbrevCollMap.empty()) + { + s->PutCString("< EMPTY >\n"); + return; + } + + DWARFAbbreviationDeclarationCollMapConstIter pos; + for (pos = m_abbrevCollMap.begin(); pos != m_abbrevCollMap.end(); ++pos) + { + s->Printf("Abbrev table for offset: 0x%8.8x\n", pos->first); + pos->second.Dump(s); + } +} + + +//---------------------------------------------------------------------- +// DWARFDebugAbbrev::GetAbbreviationDeclarationSet() +//---------------------------------------------------------------------- +const DWARFAbbreviationDeclarationSet* +DWARFDebugAbbrev::GetAbbreviationDeclarationSet(dw_offset_t cu_abbr_offset) const +{ + DWARFAbbreviationDeclarationCollMapConstIter end = m_abbrevCollMap.end(); + DWARFAbbreviationDeclarationCollMapConstIter pos; + if (m_prev_abbr_offset_pos != end && m_prev_abbr_offset_pos->first == cu_abbr_offset) + return &(m_prev_abbr_offset_pos->second); + else + { + pos = m_abbrevCollMap.find(cu_abbr_offset); + m_prev_abbr_offset_pos = pos; + } + + if (pos != m_abbrevCollMap.end()); + return &(pos->second); + return NULL; +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h new file mode 100644 index 00000000000..3185d9895a3 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h @@ -0,0 +1,74 @@ +//===-- DWARFDebugAbbrev.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_DWARFDebugAbbrev_h_ +#define SymbolFileDWARF_DWARFDebugAbbrev_h_ + +#include <list> +#include <map> + +#include "lldb/lldb-private.h" + +#include "DWARFDefines.h" +#include "DWARFAbbreviationDeclaration.h" + +typedef std::vector<DWARFAbbreviationDeclaration> DWARFAbbreviationDeclarationColl; +typedef DWARFAbbreviationDeclarationColl::iterator DWARFAbbreviationDeclarationCollIter; +typedef DWARFAbbreviationDeclarationColl::const_iterator DWARFAbbreviationDeclarationCollConstIter; + + +class DWARFAbbreviationDeclarationSet +{ +public: + DWARFAbbreviationDeclarationSet() : + m_offset(DW_INVALID_OFFSET), + m_idx_offset(0), + m_decls() + { + } + + DWARFAbbreviationDeclarationSet(dw_offset_t offset, uint32_t idx_offset) : + m_offset(offset), + m_idx_offset(idx_offset), + m_decls() + { + } + + void Clear(); + dw_offset_t GetOffset() const { return m_offset; } + void Dump(lldb_private::Stream *s) const; + bool Extract(const lldb_private::DataExtractor& data, uint32_t* offset_ptr); + //void Encode(BinaryStreamBuf& debug_abbrev_buf) const; + dw_uleb128_t AppendAbbrevDeclSequential(const DWARFAbbreviationDeclaration& abbrevDecl); + + const DWARFAbbreviationDeclaration* GetAbbreviationDeclaration(dw_uleb128_t abbrCode) const; +private: + dw_offset_t m_offset; + uint32_t m_idx_offset; + std::vector<DWARFAbbreviationDeclaration> m_decls; +}; + +typedef std::map<dw_offset_t, DWARFAbbreviationDeclarationSet> DWARFAbbreviationDeclarationCollMap; +typedef DWARFAbbreviationDeclarationCollMap::iterator DWARFAbbreviationDeclarationCollMapIter; +typedef DWARFAbbreviationDeclarationCollMap::const_iterator DWARFAbbreviationDeclarationCollMapConstIter; + + +class DWARFDebugAbbrev +{ +public: + DWARFDebugAbbrev(); + const DWARFAbbreviationDeclarationSet* GetAbbreviationDeclarationSet(dw_offset_t cu_abbr_offset) const; + void Dump(lldb_private::Stream *s) const; + void Parse(const lldb_private::DataExtractor& data); +protected: + DWARFAbbreviationDeclarationCollMap m_abbrevCollMap; + mutable DWARFAbbreviationDeclarationCollMapConstIter m_prev_abbr_offset_pos; +}; + +#endif // SymbolFileDWARF_DWARFDebugAbbrev_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp new file mode 100644 index 00000000000..57ef6ba4eb4 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp @@ -0,0 +1,274 @@ +//===-- DWARFDebugArangeSet.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugArangeSet.h" + +#include <assert.h> +#include "lldb/Core/Stream.h" +#include "SymbolFileDWARF.h" + +using namespace lldb_private; + +DWARFDebugArangeSet::DWARFDebugArangeSet() : + m_offset(DW_INVALID_OFFSET), + m_header(), + m_arange_descriptors() +{ + m_header.length = 0; + m_header.version = 0; + m_header.cu_offset = 0; + m_header.addr_size = 0; + m_header.seg_size = 0; +} + +void +DWARFDebugArangeSet::Clear() +{ + m_offset = DW_INVALID_OFFSET; + m_header.length = 0; + m_header.version = 0; + m_header.cu_offset = 0; + m_header.addr_size = 0; + m_header.seg_size = 0; + m_arange_descriptors.clear(); +} + +void +DWARFDebugArangeSet::SetHeader +( + uint16_t version, + uint32_t cu_offset, + uint8_t addr_size, + uint8_t seg_size +) +{ + m_header.version = version; + m_header.cu_offset = cu_offset; + m_header.addr_size = addr_size; + m_header.seg_size = seg_size; +} + +void +DWARFDebugArangeSet::Compact() +{ + if (m_arange_descriptors.empty()) + return; + + // Iterate through all arange descriptors and combine any ranges that + // overlap or have matching boundaries. The m_arange_descriptors are assumed + // to be in ascending order after being built by adding descriptors + // using the AddDescriptor method. + uint32_t i = 0; + while (i + 1 < m_arange_descriptors.size()) + { + if (m_arange_descriptors[i].end_address() >= m_arange_descriptors[i+1].address) + { + // The current range ends at or exceeds the start of the next address range. + // Compute the max end address between the two and use that to make the new + // length. + const dw_addr_t max_end_addr = std::max(m_arange_descriptors[i].end_address(), m_arange_descriptors[i+1].end_address()); + m_arange_descriptors[i].length = max_end_addr - m_arange_descriptors[i].address; + // Now remove the next entry as it was just combined with the previous one. + m_arange_descriptors.erase(m_arange_descriptors.begin()+i+1); + } + else + { + // Discontiguous address range, just proceed to the next one. + ++i; + } + } +} +//---------------------------------------------------------------------- +// Compare function DWARFDebugArangeSet::Descriptor structures +//---------------------------------------------------------------------- +static bool DescriptorLessThan (const DWARFDebugArangeSet::Descriptor& range1, const DWARFDebugArangeSet::Descriptor& range2) +{ + return range1.address < range2.address; +} + +//---------------------------------------------------------------------- +// Add a range descriptor and keep things sorted so we can easily +// compact the ranges before being saved or used. +//---------------------------------------------------------------------- +void +DWARFDebugArangeSet::AddDescriptor(const DWARFDebugArangeSet::Descriptor& range) +{ + if (m_arange_descriptors.empty()) + { + m_arange_descriptors.push_back(range); + return; + } + + DescriptorIter end = m_arange_descriptors.end(); + DescriptorIter pos = lower_bound(m_arange_descriptors.begin(), end, range, DescriptorLessThan); + const dw_addr_t range_end_addr = range.end_address(); + if (pos != end) + { + const dw_addr_t found_end_addr = pos->end_address(); + if (range.address < pos->address) + { + if (range_end_addr < pos->address) + { + // Non-contiguous entries, add this one before the found entry + m_arange_descriptors.insert(pos, range); + } + else if (range_end_addr == pos->address) + { + // The top end of 'range' is the lower end of the entry + // pointed to by 'pos'. We can combine range with the + // entry we found by setting the starting address and + // increasing the length since they don't overlap. + pos->address = range.address; + pos->length += range.length; + } + else + { + // We can combine these two and make sure the largest end + // address is used to make end address. + pos->address = range.address; + pos->length = std::max(found_end_addr, range_end_addr) - pos->address; + } + } + else if (range.address == pos->address) + { + pos->length = std::max(pos->length, range.length); + } + } + else + { + // NOTE: 'pos' points to entry past the end which is ok for insert, + // don't use otherwise!!! + const dw_addr_t max_addr = m_arange_descriptors.back().end_address(); + if (max_addr < range.address) + { + // Non-contiguous entries, add this one before the found entry + m_arange_descriptors.insert(pos, range); + } + else if (max_addr == range.address) + { + m_arange_descriptors.back().length += range.length; + } + else + { + m_arange_descriptors.back().length = std::max(max_addr, range_end_addr) - m_arange_descriptors.back().address; + } + } +} + +bool +DWARFDebugArangeSet::Extract(const DataExtractor &data, uint32_t* offset_ptr) +{ + if (data.ValidOffset(*offset_ptr)) + { + m_arange_descriptors.clear(); + m_offset = *offset_ptr; + + // 7.20 Address Range Table + // + // Each set of entries in the table of address ranges contained in + // the .debug_aranges section begins with a header consisting of: a + // 4-byte length containing the length of the set of entries for this + // compilation unit, not including the length field itself; a 2-byte + // version identifier containing the value 2 for DWARF Version 2; a + // 4-byte offset into the.debug_infosection; a 1-byte unsigned integer + // containing the size in bytes of an address (or the offset portion of + // an address for segmented addressing) on the target system; and a + // 1-byte unsigned integer containing the size in bytes of a segment + // descriptor on the target system. This header is followed by a series + // of tuples. Each tuple consists of an address and a length, each in + // the size appropriate for an address on the target architecture. + m_header.length = data.GetU32(offset_ptr); + m_header.version = data.GetU16(offset_ptr); + m_header.cu_offset = data.GetU32(offset_ptr); + m_header.addr_size = data.GetU8(offset_ptr); + m_header.seg_size = data.GetU8(offset_ptr); + + + // The first tuple following the header in each set begins at an offset + // that is a multiple of the size of a single tuple (that is, twice the + // size of an address). The header is padded, if necessary, to the + // appropriate boundary. + const uint32_t header_size = *offset_ptr - m_offset; + const uint32_t tuple_size = m_header.addr_size << 1; + uint32_t first_tuple_offset = 0; + while (first_tuple_offset < header_size) + first_tuple_offset += tuple_size; + + *offset_ptr = m_offset + first_tuple_offset; + + Descriptor arangeDescriptor; + + assert(sizeof(arangeDescriptor.address) == sizeof(arangeDescriptor.length)); + assert(sizeof(arangeDescriptor.address) >= m_header.addr_size); + + while (data.ValidOffset(*offset_ptr)) + { + arangeDescriptor.address = data.GetMaxU64(offset_ptr, m_header.addr_size); + arangeDescriptor.length = data.GetMaxU64(offset_ptr, m_header.addr_size); + + // Each set of tuples is terminated by a 0 for the address and 0 + // for the length. + if (arangeDescriptor.address || arangeDescriptor.length) + m_arange_descriptors.push_back(arangeDescriptor); + else + break; // We are done if we get a zero address and length + } + + return !m_arange_descriptors.empty(); + } + return false; +} + + +dw_offset_t +DWARFDebugArangeSet::GetOffsetOfNextEntry() const +{ + return m_offset + m_header.length + 4; +} + + +void +DWARFDebugArangeSet::Dump(Stream *s) const +{ + s->Printf("Address Range Header: length = 0x%8.8x, version = 0x%4.4x, cu_offset = 0x%8.8x, addr_size = 0x%2.2x, seg_size = 0x%2.2x\n", + m_header.length ,m_header.version, m_header.cu_offset, m_header.addr_size, m_header.seg_size); + + const uint32_t hex_width = m_header.addr_size * 2; + DescriptorConstIter pos; + DescriptorConstIter end = m_arange_descriptors.end(); + for (pos = m_arange_descriptors.begin(); pos != end; ++pos) + s->Printf("[0x%*.*llx - 0x%*.*llx)\n", + hex_width, hex_width, pos->address, + hex_width, hex_width, pos->end_address()); +} + + +class DescriptorContainsAddress +{ +public: + DescriptorContainsAddress (dw_addr_t address) : m_address(address) {} + bool operator() (const DWARFDebugArangeSet::Descriptor& desc) const + { + return (m_address >= desc.address) && (m_address < (desc.address + desc.length)); + } + private: + const dw_addr_t m_address; +}; + +dw_offset_t +DWARFDebugArangeSet::FindAddress(dw_addr_t address) const +{ + DescriptorConstIter end = m_arange_descriptors.end(); + DescriptorConstIter pos = std::find_if( m_arange_descriptors.begin(), end, // Range + DescriptorContainsAddress(address));// Predicate + if (pos != end) + return m_header.cu_offset; + + return DW_INVALID_OFFSET; +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h new file mode 100644 index 00000000000..fc1e391aeb4 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h @@ -0,0 +1,70 @@ +//===-- DWARFDebugArangeSet.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_DWARFDebugArangeSet_h_ +#define SymbolFileDWARF_DWARFDebugArangeSet_h_ + +#include "SymbolFileDWARF.h" +#include <vector> + +class SymbolFileDWARF; + +class DWARFDebugArangeSet +{ +public: + typedef struct HeaderTag + { + uint32_t length; // The total length of the entries for that set, not including the length field itself. + uint16_t version; // The DWARF version number + uint32_t cu_offset; // The offset from the beginning of the .debug_info section of the compilation unit entry referenced by the table. + uint8_t addr_size; // The size in bytes of an address on the target architecture. For segmented addressing, this is the size of the offset portion of the address + uint8_t seg_size; // The size in bytes of a segment descriptor on the target architecture. If the target system uses a flat address space, this value is 0. + } Header; + + typedef struct DescriptorTag + { + dw_addr_t address; + dw_addr_t length; + dw_addr_t end_address() const { return address + length; } + } Descriptor; + + + DWARFDebugArangeSet(); + void Clear(); + void SetOffset(uint32_t offset) { m_offset = offset; } + void SetHeader(uint16_t version, uint32_t cu_offset, uint8_t addr_size, uint8_t seg_size); + void AddDescriptor(const DWARFDebugArangeSet::Descriptor& range); + void Compact(); + bool Extract(const lldb_private::DataExtractor &data, uint32_t* offset_ptr); + void Dump(lldb_private::Stream *s) const; + dw_offset_t GetCompileUnitDIEOffset() const { return m_header.cu_offset; } + dw_offset_t GetOffsetOfNextEntry() const; + dw_offset_t FindAddress(dw_addr_t address) const; + uint32_t NumDescriptors() const { return m_arange_descriptors.size(); } + const Header& GetHeader() const { return m_header; } + const Descriptor* GetDescriptor(uint32_t i) const + { + if (i < m_arange_descriptors.size()) + return &m_arange_descriptors[i]; + return NULL; + } + + +protected: + typedef std::vector<Descriptor> DescriptorColl; + typedef DescriptorColl::iterator DescriptorIter; + typedef DescriptorColl::const_iterator DescriptorConstIter; + + + uint32_t m_offset; + Header m_header; + DescriptorColl m_arange_descriptors; +}; + +#endif // SymbolFileDWARF_DWARFDebugArangeSet_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp new file mode 100644 index 00000000000..a3213e080c3 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp @@ -0,0 +1,343 @@ +//===-- DWARFDebugAranges.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugAranges.h" + +#include <assert.h> + +#include <algorithm> + +#include "lldb/Core/Stream.h" + +#include "SymbolFileDWARF.h" +#include "DWARFDebugInfo.h" +#include "DWARFCompileUnit.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +DWARFDebugAranges::DWARFDebugAranges() : + m_aranges() +{ +} + + +//---------------------------------------------------------------------- +// Compare function DWARFDebugAranges::Range structures +//---------------------------------------------------------------------- +static bool RangeLessThan (const DWARFDebugAranges::Range& range1, const DWARFDebugAranges::Range& range2) +{ +// printf("RangeLessThan -- 0x%8.8x < 0x%8.8x ? %d\n", range1.lo_pc, range1.lo_pc, range1.lo_pc < range2.lo_pc); + return range1.lo_pc < range2.lo_pc; +} + +//---------------------------------------------------------------------- +// CountArangeDescriptors +//---------------------------------------------------------------------- +class CountArangeDescriptors +{ +public: + CountArangeDescriptors (uint32_t& count_ref) : count(count_ref) + { +// printf("constructor CountArangeDescriptors()\n"); + } + void operator() (const DWARFDebugArangeSet& set) + { + count += set.NumDescriptors(); + } + uint32_t& count; +}; + +//---------------------------------------------------------------------- +// AddArangeDescriptors +//---------------------------------------------------------------------- +class AddArangeDescriptors +{ +public: + AddArangeDescriptors (DWARFDebugAranges::RangeColl& ranges) : range_collection(ranges) {} + void operator() (const DWARFDebugArangeSet& set) + { + const DWARFDebugArangeSet::Descriptor* arange_desc_ptr; + DWARFDebugAranges::Range range; + range.offset = set.GetCompileUnitDIEOffset(); + + for (uint32_t i=0; arange_desc_ptr = set.GetDescriptor(i); ++i) + { + range.lo_pc = arange_desc_ptr->address; + range.hi_pc = arange_desc_ptr->address + arange_desc_ptr->length; + + // Insert each item in increasing address order so binary searching + // can later be done! + DWARFDebugAranges::RangeColl::iterator insert_pos = lower_bound(range_collection.begin(), range_collection.end(), range, RangeLessThan); + range_collection.insert(insert_pos, range); + } + } + DWARFDebugAranges::RangeColl& range_collection; +}; + +//---------------------------------------------------------------------- +// PrintRange +//---------------------------------------------------------------------- +static void PrintRange(const DWARFDebugAranges::Range& range) +{ + // Cast the address values in case the address type is compiled as 32 bit + printf("0x%8.8x: [0x%8.8llx - 0x%8.8llx)\n", range.offset, (uint64_t)range.lo_pc, (uint64_t)range.hi_pc); +} + +//---------------------------------------------------------------------- +// Extract +//---------------------------------------------------------------------- +bool +DWARFDebugAranges::Extract(const DataExtractor &debug_aranges_data) +{ + if (debug_aranges_data.ValidOffset(0)) + { + uint32_t offset = 0; + + typedef std::vector<DWARFDebugArangeSet> SetCollection; + typedef SetCollection::const_iterator SetCollectionIter; + SetCollection sets; + + DWARFDebugArangeSet set; + Range range; + while (set.Extract(debug_aranges_data, &offset)) + sets.push_back(set); + + uint32_t count = 0; + + for_each(sets.begin(), sets.end(), CountArangeDescriptors(count)); + + if (count > 0) + { + m_aranges.reserve(count); + AddArangeDescriptors range_adder(m_aranges); + for_each(sets.begin(), sets.end(), range_adder); + } + + // puts("\n\nDWARFDebugAranges list is:\n"); + // for_each(m_aranges.begin(), m_aranges.end(), PrintRange); + } + return false; +} + +//---------------------------------------------------------------------- +// Generate +//---------------------------------------------------------------------- +bool +DWARFDebugAranges::Generate(SymbolFileDWARF* dwarf2Data) +{ + Clear(); + DWARFDebugInfo* debug_info = dwarf2Data->DebugInfo(); + if (debug_info) + { + uint32_t cu_idx = 0; + const uint32_t num_compile_units = dwarf2Data->GetNumCompileUnits(); + for (cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) + { + DWARFCompileUnit* cu = debug_info->GetCompileUnitAtIndex(cu_idx); + if (cu) + cu->DIE()->BuildAddressRangeTable(dwarf2Data, cu, this); + } + } + return !IsEmpty(); +} + + +void +DWARFDebugAranges::Print() const +{ + puts("\n\nDWARFDebugAranges address range list is:\n"); + for_each(m_aranges.begin(), m_aranges.end(), PrintRange); +} + + +void +DWARFDebugAranges::Range::Dump(Stream *s) const +{ + s->Printf("{0x%8.8x}: [0x%8.8llx - 0x%8.8llx)\n", offset, lo_pc, hi_pc); +} + +//---------------------------------------------------------------------- +// Dump +//---------------------------------------------------------------------- +void +DWARFDebugAranges::Dump(SymbolFileDWARF* dwarf2Data, Stream *s) +{ + const DataExtractor &debug_aranges_data = dwarf2Data->get_debug_aranges_data(); + if (debug_aranges_data.ValidOffset(0)) + { + uint32_t offset = 0; + + DWARFDebugArangeSet set; + while (set.Extract(debug_aranges_data, &offset)) + set.Dump(s); + } + else + s->PutCString("< EMPTY >\n"); +} + + +//---------------------------------------------------------------------- +// AppendDebugRanges +//---------------------------------------------------------------------- +//void +//DWARFDebugAranges::AppendDebugRanges(BinaryStreamBuf& debug_ranges, dw_addr_t cu_base_addr, uint32_t addr_size) const +//{ +// if (!m_aranges.empty()) +// { +// RangeCollIterator end = m_aranges.end(); +// RangeCollIterator pos; +// RangeCollIterator lo_pos = end; +// for (pos = m_aranges.begin(); pos != end; ++pos) +// { +// if (lo_pos == end) +// lo_pos = pos; +// +// RangeCollIterator next = pos + 1; +// if (next != end) +// { +// // Check to see if we can combine two consecutive ranges? +// if (pos->hi_pc == next->lo_pc) +// continue; // We can combine them! +// } +// +// if (cu_base_addr == 0 || cu_base_addr == DW_INVALID_ADDRESS) +// { +// debug_ranges.AppendMax64(lo_pos->lo_pc, addr_size); +// debug_ranges.AppendMax64(pos->hi_pc, addr_size); +// } +// else +// { +// assert(lo_pos->lo_pc >= cu_base_addr); +// assert(pos->hi_pc >= cu_base_addr); +// debug_ranges.AppendMax64(lo_pos->lo_pc - cu_base_addr, addr_size); +// debug_ranges.AppendMax64(pos->hi_pc - cu_base_addr, addr_size); +// } +// +// // Reset the low part of the next address range +// lo_pos = end; +// } +// } +// // Terminate the .debug_ranges with two zero addresses +// debug_ranges.AppendMax64(0, addr_size); +// debug_ranges.AppendMax64(0, addr_size); +// +//} +// +//---------------------------------------------------------------------- +// ArangeSetContainsAddress +//---------------------------------------------------------------------- +class ArangeSetContainsAddress +{ +public: + ArangeSetContainsAddress (dw_addr_t the_address) : address(the_address), offset(DW_INVALID_OFFSET) {} + bool operator() (const DWARFDebugArangeSet& set) + { + offset = set.FindAddress(address); + return (offset != DW_INVALID_OFFSET); + } + const dw_addr_t address; + dw_offset_t offset; +}; + + +//---------------------------------------------------------------------- +// InsertRange +//---------------------------------------------------------------------- +void +DWARFDebugAranges::InsertRange(dw_offset_t offset, dw_addr_t low_pc, dw_addr_t high_pc) +{ + // Insert each item in increasing address order so binary searching + // can later be done! + DWARFDebugAranges::Range range(low_pc, high_pc, offset); + InsertRange(range); +} + +//---------------------------------------------------------------------- +// InsertRange +//---------------------------------------------------------------------- +void +DWARFDebugAranges::InsertRange(const DWARFDebugAranges::Range& range) +{ + // Insert each item in increasing address order so binary searching + // can later be done! + RangeColl::iterator insert_pos = lower_bound(m_aranges.begin(), m_aranges.end(), range, RangeLessThan); + m_aranges.insert(insert_pos, range); +} + + +//---------------------------------------------------------------------- +// FindAddress +//---------------------------------------------------------------------- +dw_offset_t +DWARFDebugAranges::FindAddress(dw_addr_t address) const +{ + if ( !m_aranges.empty() ) + { + DWARFDebugAranges::Range range(address); + DWARFDebugAranges::RangeCollIterator begin = m_aranges.begin(); + DWARFDebugAranges::RangeCollIterator end = m_aranges.end(); + DWARFDebugAranges::RangeCollIterator pos = lower_bound(begin, end, range, RangeLessThan); + + if ((pos != end) && (pos->lo_pc <= address && address < pos->hi_pc)) + { + // printf("FindAddress(1) found 0x%8.8x in compile unit: 0x%8.8x\n", address, pos->offset); + return pos->offset; + } + else if (pos != begin) + { + --pos; + if ((pos->lo_pc <= address) && (address < pos->hi_pc)) + { + // printf("FindAddress(2) found 0x%8.8x in compile unit: 0x%8.8x\n", address, pos->offset); + return (*pos).offset; + } + } + } + return DW_INVALID_OFFSET; +} + +//---------------------------------------------------------------------- +// AllRangesAreContiguous +//---------------------------------------------------------------------- +bool +DWARFDebugAranges::AllRangesAreContiguous(dw_addr_t& lo_pc, dw_addr_t& hi_pc) const +{ + if (m_aranges.empty()) + return false; + + DWARFDebugAranges::RangeCollIterator begin = m_aranges.begin(); + DWARFDebugAranges::RangeCollIterator end = m_aranges.end(); + DWARFDebugAranges::RangeCollIterator pos; + dw_addr_t next_addr = 0; + + for (pos = begin; pos != end; ++pos) + { + if ((pos != begin) && (pos->lo_pc != next_addr)) + return false; + next_addr = pos->hi_pc; + } + lo_pc = m_aranges.front().lo_pc; // We checked for empty at the start of function so front() will be valid + hi_pc = m_aranges.back().hi_pc; // We checked for empty at the start of function so back() will be valid + return true; +} + +bool +DWARFDebugAranges::GetMaxRange(dw_addr_t& lo_pc, dw_addr_t& hi_pc) const +{ + if (m_aranges.empty()) + return false; + + lo_pc = m_aranges.front().lo_pc; // We checked for empty at the start of function so front() will be valid + hi_pc = m_aranges.back().hi_pc; // We checked for empty at the start of function so back() will be valid + return true; +} + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h new file mode 100644 index 00000000000..f3db949bf26 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h @@ -0,0 +1,98 @@ +//===-- DWARFDebugAranges.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_DWARFDebugAranges_h_ +#define SymbolFileDWARF_DWARFDebugAranges_h_ + +#include "DWARFDebugArangeSet.h" +#include <list> + +class SymbolFileDWARF; + +class DWARFDebugAranges +{ +public: + struct Range + { + Range( + dw_addr_t _lo_pc = DW_INVALID_ADDRESS, + dw_addr_t _hi_pc = DW_INVALID_ADDRESS, + dw_offset_t _offset = DW_INVALID_OFFSET) : + lo_pc(_lo_pc), + hi_pc(_hi_pc), + offset(_offset) + { + } + + void Clear() + { + lo_pc = hi_pc = DW_INVALID_ADDRESS; + offset = DW_INVALID_OFFSET; + } + + bool ValidRange() const + { + return hi_pc > lo_pc; + } + + bool Contains(const Range& range) const + { + return lo_pc <= range.lo_pc && range.hi_pc <= hi_pc; + } + + void Dump(lldb_private::Stream *s) const; + dw_addr_t lo_pc; // Start of address range + dw_addr_t hi_pc; // End of address range (not including this address) + dw_offset_t offset; // Offset of the compile unit or die + }; + + DWARFDebugAranges(); + + void Clear() { m_aranges.clear(); } + bool AllRangesAreContiguous(dw_addr_t& lo_pc, dw_addr_t& hi_pc) const; + bool GetMaxRange(dw_addr_t& lo_pc, dw_addr_t& hi_pc) const; + bool Extract(const lldb_private::DataExtractor &debug_aranges_data); + bool Generate(SymbolFileDWARF* dwarf2Data); + void InsertRange(dw_offset_t cu_offset, dw_addr_t low_pc, dw_addr_t high_pc); + void InsertRange(const DWARFDebugAranges::Range& range); + const Range* RangeAtIndex(uint32_t idx) const + { + if (idx < m_aranges.size()) + return &m_aranges[idx]; + return NULL; + } + void Print() const; + dw_offset_t FindAddress(dw_addr_t address) const; + bool IsEmpty() const { return m_aranges.empty(); } + void Dump(lldb_private::Stream *s); + uint32_t NumRanges() const + { + return m_aranges.size(); + } + + dw_offset_t OffsetAtIndex(uint32_t idx) const + { + if (idx < m_aranges.size()) + return m_aranges[idx].offset; + return DW_INVALID_OFFSET; + } +// void AppendDebugRanges(BinaryStreamBuf& debug_ranges, dw_addr_t cu_base_addr, uint32_t addr_size) const; + + static void Dump(SymbolFileDWARF* dwarf2Data, lldb_private::Stream *s); + + typedef std::vector<Range> RangeColl; + typedef RangeColl::const_iterator RangeCollIterator; + +protected: + + RangeColl m_aranges; +}; + + +#endif // SymbolFileDWARF_DWARFDebugAranges_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp new file mode 100644 index 00000000000..fcb0ccf1189 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp @@ -0,0 +1,1206 @@ +//===-- DWARFDebugInfo.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolFileDWARF.h" + +#include <algorithm> +#include <set> + +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Stream.h" + +#include "DWARFDebugInfo.h" +#include "DWARFCompileUnit.h" +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfoEntry.h" +#include "DWARFFormValue.h" + +using namespace lldb_private; +using namespace std; + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +DWARFDebugInfo::DWARFDebugInfo() : + m_dwarf2Data(NULL), + m_compile_units() +{ +} + +//---------------------------------------------------------------------- +// SetDwarfData +//---------------------------------------------------------------------- +void +DWARFDebugInfo::SetDwarfData(SymbolFileDWARF* dwarf2Data) +{ + m_dwarf2Data = dwarf2Data; + m_compile_units.clear(); +} + +//---------------------------------------------------------------------- +// BuildDIEAddressRangeTable +//---------------------------------------------------------------------- +bool +DWARFDebugInfo::BuildFunctionAddressRangeTable(DWARFDebugAranges* debug_aranges) +{ + const uint32_t num_compile_units = GetNumCompileUnits(); + uint32_t idx; + for (idx = 0; idx < num_compile_units; ++idx) + { + DWARFCompileUnit* cu = GetCompileUnitAtIndex (idx); + if (cu) + { + cu->DIE()->BuildFunctionAddressRangeTable(m_dwarf2Data, cu, debug_aranges); + } + } + return !debug_aranges->IsEmpty(); +} + +//---------------------------------------------------------------------- +// LookupAddress +//---------------------------------------------------------------------- +bool +DWARFDebugInfo::LookupAddress +( + const dw_addr_t address, + const dw_offset_t hint_die_offset, + DWARFCompileUnitSP& cu_sp, + DWARFDebugInfoEntry** function_die, + DWARFDebugInfoEntry** block_die +) +{ + + if (hint_die_offset != DW_INVALID_OFFSET) + cu_sp = GetCompileUnit(hint_die_offset); + else + { + // Get a non const version of the address ranges + DWARFDebugAranges* debug_aranges = ((SymbolFileDWARF*)m_dwarf2Data)->DebugAranges(); + + if (debug_aranges != NULL) + { + // If we have an empty address ranges section, lets build a sorted + // table ourselves by going through all of the debug information so we + // can do quick subsequent searches. + + if (debug_aranges->IsEmpty()) + { + const uint32_t num_compile_units = GetNumCompileUnits(); + uint32_t idx; + for (idx = 0; idx < num_compile_units; ++idx) + { + DWARFCompileUnit* cu = GetCompileUnitAtIndex(idx); + if (cu) + cu->DIE()->BuildAddressRangeTable(m_dwarf2Data, cu, debug_aranges); + } + } + cu_sp = GetCompileUnit(debug_aranges->FindAddress(address)); + } + } + + if (cu_sp.get()) + { + if (cu_sp->LookupAddress(address, function_die, block_die)) + return true; + cu_sp.reset(); + } + else + { + // The hint_die_offset may have been a pointer to the actual item that + // we are looking for + DWARFDebugInfoEntry* die_ptr = GetDIEPtr(hint_die_offset, &cu_sp); + if (die_ptr) + { + if (cu_sp.get()) + { + if (function_die || block_die) + return die_ptr->LookupAddress(address, m_dwarf2Data, cu_sp.get(), function_die, block_die); + + // We only wanted the compile unit that contained this address + return true; + } + } + } + return false; +} + + +void +DWARFDebugInfo::ParseCompileUnitHeadersIfNeeded() +{ + if (m_compile_units.empty()) + { + if (m_dwarf2Data != NULL) + { + uint32_t offset = 0; + const DataExtractor &debug_info_data = m_dwarf2Data->get_debug_info_data(); + while (debug_info_data.ValidOffset(offset)) + { + DWARFCompileUnitSP cu_sp(new DWARFCompileUnit(m_dwarf2Data)); + // Out of memory? + if (cu_sp.get() == NULL) + break; + + if (cu_sp->Extract(debug_info_data, &offset) == false) + break; + + m_compile_units.push_back(cu_sp); + + offset = cu_sp->GetNextCompileUnitOffset(); + } + } + } +} + +uint32_t +DWARFDebugInfo::GetNumCompileUnits() +{ + ParseCompileUnitHeadersIfNeeded(); + return m_compile_units.size(); +} + +DWARFCompileUnit* +DWARFDebugInfo::GetCompileUnitAtIndex(uint32_t idx) +{ + DWARFCompileUnit* cu = NULL; + if (idx < GetNumCompileUnits()) + cu = m_compile_units[idx].get(); + return cu; +} + +static bool CompileUnitOffsetLessThan (const DWARFCompileUnitSP& a, const DWARFCompileUnitSP& b) +{ + return a->GetOffset() < b->GetOffset(); +} + + +static int +CompareDWARFCompileUnitSPOffset (const void *key, const void *arrmem) +{ + const dw_offset_t key_cu_offset = *(dw_offset_t*) key; + const dw_offset_t cu_offset = ((DWARFCompileUnitSP *)arrmem)->get()->GetOffset(); + if (key_cu_offset < cu_offset) + return -1; + if (key_cu_offset > cu_offset) + return 1; + return 0; +} + +DWARFCompileUnitSP +DWARFDebugInfo::GetCompileUnit(dw_offset_t cu_offset, uint32_t* idx_ptr) +{ + DWARFCompileUnitSP cu_sp; + uint32_t cu_idx = DW_INVALID_INDEX; + if (cu_offset != DW_INVALID_OFFSET) + { + ParseCompileUnitHeadersIfNeeded(); + + DWARFCompileUnitSP* match = (DWARFCompileUnitSP*)bsearch(&cu_offset, &m_compile_units[0], m_compile_units.size(), sizeof(DWARFCompileUnitSP), CompareDWARFCompileUnitSPOffset); + if (match) + { + cu_sp = *match; + cu_idx = match - &m_compile_units[0]; + } + } + if (idx_ptr) + *idx_ptr = cu_idx; + return cu_sp; +} + +DWARFCompileUnitSP +DWARFDebugInfo::GetCompileUnitContainingDIE(dw_offset_t die_offset) +{ + DWARFCompileUnitSP cu_sp; + if (die_offset != DW_INVALID_OFFSET) + { + ParseCompileUnitHeadersIfNeeded(); + + CompileUnitColl::const_iterator end_pos = m_compile_units.end(); + CompileUnitColl::const_iterator pos; + + for (pos = m_compile_units.begin(); pos != end_pos; ++pos) + { + dw_offset_t cu_start_offset = (*pos)->GetOffset(); + dw_offset_t cu_end_offset = (*pos)->GetNextCompileUnitOffset(); + if (cu_start_offset <= die_offset && die_offset < cu_end_offset) + { + cu_sp = *pos; + break; + } + } + } + return cu_sp; +} + +//---------------------------------------------------------------------- +// Compare function DWARFDebugAranges::Range structures +//---------------------------------------------------------------------- +static bool CompareDIEOffset (const DWARFDebugInfoEntry& die1, const DWARFDebugInfoEntry& die2) +{ + return die1.GetOffset() < die2.GetOffset(); +} + + +//---------------------------------------------------------------------- +// GetDIE() +// +// Get the DIE (Debug Information Entry) with the specified offset. +//---------------------------------------------------------------------- +DWARFDebugInfoEntry* +DWARFDebugInfo::GetDIEPtr(dw_offset_t die_offset, DWARFCompileUnitSP* cu_sp_ptr) +{ + DWARFCompileUnitSP cu_sp(GetCompileUnitContainingDIE(die_offset)); + if (cu_sp_ptr) + *cu_sp_ptr = cu_sp; + if (cu_sp.get()) + return cu_sp->GetDIEPtr(die_offset); + return NULL; // Not found in any compile units +} + +const DWARFDebugInfoEntry* +DWARFDebugInfo::GetDIEPtrContainingOffset(dw_offset_t die_offset, DWARFCompileUnitSP* cu_sp_ptr) +{ + DWARFCompileUnitSP cu_sp(GetCompileUnitContainingDIE(die_offset)); + if (cu_sp_ptr) + *cu_sp_ptr = cu_sp; + if (cu_sp.get()) + return cu_sp->GetDIEPtrContainingOffset(die_offset); + + return NULL; // Not found in any compile units + +} + +//---------------------------------------------------------------------- +// DWARFDebugInfo_ParseCallback +// +// A callback function for the static DWARFDebugInfo::Parse() function +// that gets parses all compile units and DIE's into an internate +// representation for further modification. +//---------------------------------------------------------------------- + +static dw_offset_t +DWARFDebugInfo_ParseCallback +( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnitSP& cu_sp, + DWARFDebugInfoEntry* die, + const dw_offset_t next_offset, + const uint32_t curr_depth, + void* userData +) +{ + DWARFDebugInfo* debug_info = (DWARFDebugInfo*)userData; + DWARFCompileUnit* cu = cu_sp.get(); + if (die) + { + cu->AddDIE(*die); + } + else if (cu) + { + debug_info->AddCompileUnit(cu_sp); + } + + // Just return the current offset to parse the next CU or DIE entry + return next_offset; +} + +//---------------------------------------------------------------------- +// AddCompileUnit +//---------------------------------------------------------------------- +void +DWARFDebugInfo::AddCompileUnit(DWARFCompileUnitSP& cu) +{ + m_compile_units.push_back(cu); +} + +/* +void +DWARFDebugInfo::AddDIE(DWARFDebugInfoEntry& die) +{ + m_die_array.push_back(die); +} +*/ + + + + +//---------------------------------------------------------------------- +// Parse +// +// Parses the .debug_info section and uses the .debug_abbrev section +// and various other sections in the SymbolFileDWARF class and calls the +// supplied callback function each time a compile unit header, or debug +// information entry is successfully parsed. This function can be used +// for different tasks such as parsing the file contents into a +// structured data, dumping, verifying and much more. +//---------------------------------------------------------------------- +void +DWARFDebugInfo::Parse(SymbolFileDWARF* dwarf2Data, Callback callback, void* userData) +{ + if (dwarf2Data) + { + uint32_t offset = 0; + uint32_t depth = 0; + DWARFCompileUnitSP cu(new DWARFCompileUnit(dwarf2Data)); + if (cu.get() == NULL) + return; + DWARFDebugInfoEntry die; + + while (cu->Extract(dwarf2Data->get_debug_info_data(), &offset)) + { + const dw_offset_t next_cu_offset = cu->GetNextCompileUnitOffset(); + + depth = 0; + // Call the callback funtion with no DIE pointer for the compile unit + // and get the offset that we are to continue to parse from + offset = callback(dwarf2Data, cu, NULL, offset, depth, userData); + + // Make sure we are within our compile unit + if (offset < next_cu_offset) + { + // We are in our compile unit, parse starting at the offset + // we were told to parse + bool done = false; + while (!done && die.Extract(dwarf2Data, cu.get(), &offset)) + { + // Call the callback funtion with DIE pointer that falls within the compile unit + offset = callback(dwarf2Data, cu, &die, offset, depth, userData); + + if (die.IsNULL()) + { + if (depth) + --depth; + else + done = true; // We are done with this compile unit! + } + else if (die.HasChildren()) + ++depth; + } + } + + // Make sure the offset returned is valid, and if not stop parsing. + // Returning DW_INVALID_OFFSET from this callback is a good way to end + // all parsing + if (!dwarf2Data->get_debug_info_data().ValidOffset(offset)) + break; + + // See if during the callback anyone retained a copy of the compile + // unit other than ourselves and if so, let whomever did own the object + // and create a new one for our own use! + if (!cu.unique()) + cu.reset(new DWARFCompileUnit(dwarf2Data)); + + + // Make sure we start on a propper + offset = next_cu_offset; + } + } +} + +/* +typedef struct AddressRangeTag +{ + dw_addr_t lo_pc; + dw_addr_t hi_pc; + dw_offset_t die_offset; +} AddressRange; +*/ +struct DIERange +{ + DIERange() : + range(), + lo_die_offset(), + hi_die_offset() + { + } + + DWARFDebugAranges::Range range; + dw_offset_t lo_die_offset; + dw_offset_t hi_die_offset; +}; + +typedef struct DwarfStat +{ + DwarfStat() : count(0), byte_size(0) {} + uint32_t count; + uint32_t byte_size; +} DwarfStat; + +typedef map<dw_attr_t, DwarfStat> DwarfAttrStatMap; + +typedef struct DIEStat +{ + DIEStat() : count(0), byte_size(0), attr_stats() {} + uint32_t count; + uint32_t byte_size; + DwarfAttrStatMap attr_stats; +} DIEStat; + +typedef map<dw_tag_t, DIEStat> DIEStatMap; +struct VerifyInfo +{ + VerifyInfo(Stream* the_strm) : + strm(the_strm), + die_ranges(), + addr_range_errors(0), + sibling_errors(0), + die_stats() + { + } + + Stream* strm; + vector<DIERange> die_ranges; + uint32_t addr_range_errors; + uint32_t sibling_errors; + DIEStatMap die_stats; + + DISALLOW_COPY_AND_ASSIGN(VerifyInfo); + +}; + + +//---------------------------------------------------------------------- +// VerifyCallback +// +// A callback function for the static DWARFDebugInfo::Parse() function +// that gets called each time a compile unit header or debug information +// entry is successfully parsed. +// +// This function will verify the DWARF information is well formed by +// making sure that any DW_TAG_compile_unit tags that have valid address +// ranges (DW_AT_low_pc and DW_AT_high_pc) have no gaps in the address +// ranges of it contained DW_TAG_subprogram tags. Also the sibling chain +// and relationships are verified to make sure nothing gets hosed up +// when dead stripping occurs. +//---------------------------------------------------------------------- + +static dw_offset_t +VerifyCallback +( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnitSP& cu_sp, + DWARFDebugInfoEntry* die, + const dw_offset_t next_offset, + const uint32_t curr_depth, + void* userData +) +{ + VerifyInfo* verifyInfo = (VerifyInfo*)userData; + + const DWARFCompileUnit* cu = cu_sp.get(); + Stream *s = verifyInfo->strm; + bool verbose = s->GetVerbose(); + if (die) + { + // die->Dump(dwarf2Data, cu, f); + const DWARFAbbreviationDeclaration* abbrevDecl = die->GetAbbreviationDeclarationPtr(); + // We have a DIE entry + if (abbrevDecl) + { + const dw_offset_t die_offset = die->GetOffset(); + const dw_offset_t sibling = die->GetAttributeValueAsReference(dwarf2Data, cu, DW_AT_sibling, DW_INVALID_OFFSET); + + if (sibling != DW_INVALID_OFFSET) + { + if (sibling <= next_offset) + { + if (verifyInfo->sibling_errors++ == 0) + s->Printf("ERROR\n"); + s->Printf(" 0x%8.8x: sibling attribyte (0x%8.8x) in this die is not valid: it is less than this DIE or some of its contents.\n", die->GetOffset(), sibling); + } + else if (sibling > verifyInfo->die_ranges.back().hi_die_offset) + { + if (verifyInfo->sibling_errors++ == 0) + s->Printf("ERROR\n"); + s->Printf(" 0x%8.8x: sibling attribute (0x%8.8x) in this DIE is not valid: it is greater than the end of the parent scope.\n", die->GetOffset(), sibling); + } + } + + if ((die_offset < verifyInfo->die_ranges.back().lo_die_offset) || (die_offset >= verifyInfo->die_ranges.back().hi_die_offset)) + { + if (verifyInfo->sibling_errors++ == 0) + s->Printf("ERROR\n"); + s->Printf(" 0x%8.8x: DIE offset is not within the parent DIE range {0x%8.8x}: (0x%8.8x - 0x%8.8x)\n", + die->GetOffset(), + verifyInfo->die_ranges.back().range.offset, + verifyInfo->die_ranges.back().lo_die_offset, + verifyInfo->die_ranges.back().hi_die_offset); + + } + + dw_tag_t tag = abbrevDecl->Tag(); + + // Keep some stats on this DWARF file + verifyInfo->die_stats[tag].count++; + verifyInfo->die_stats[tag].byte_size += (next_offset - die->GetOffset()); + + if (verbose) + { + DIEStat& tag_stat = verifyInfo->die_stats[tag]; + + const DataExtractor& debug_info = dwarf2Data->get_debug_info_data(); + + dw_offset_t offset = die->GetOffset(); + // Skip the abbreviation code so we are at the data for the attributes + debug_info.Skip_LEB128(&offset); + + const uint32_t numAttributes = abbrevDecl->NumAttributes(); + dw_attr_t attr; + dw_form_t form; + for (uint32_t idx = 0; idx < numAttributes; ++idx) + { + dw_offset_t start_offset = offset; + abbrevDecl->GetAttrAndFormByIndexUnchecked(idx, attr, form); + DWARFFormValue::SkipValue(form, debug_info, &offset, cu); + + if (tag_stat.attr_stats.find(attr) == tag_stat.attr_stats.end()) + { + tag_stat.attr_stats[attr].count = 0; + tag_stat.attr_stats[attr].byte_size = 0; + } + + tag_stat.attr_stats[attr].count++; + tag_stat.attr_stats[attr].byte_size += offset - start_offset; + } + } + + DWARFDebugAranges::Range range; + range.offset = die->GetOffset(); + + switch (tag) + { + case DW_TAG_compile_unit: + // Check for previous subroutines that were within a previous + // + // VerifyAddressRangesForCU(verifyInfo); + // Remember which compile unit we are dealing with so we can verify + // the address ranges within it (if any) are contiguous. The DWARF + // spec states that if a compile unit TAG has high and low PC + // attributes, there must be no gaps in the address ranges of it's + // contained subtroutines. If there are gaps, the high and low PC + // must not be in the DW_TAG_compile_unit's attributes. Errors like + // this can crop up when optimized code is dead stripped and the debug + // information isn't properly fixed up for output. + range.lo_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, DW_INVALID_ADDRESS); + if (range.lo_pc != DW_INVALID_ADDRESS) + { + range.hi_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_high_pc, DW_INVALID_ADDRESS); + if (s->GetVerbose()) + { + s->Printf("\n CU "); + range.Dump(s); + } + } + else + { + range.lo_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_entry_pc, DW_INVALID_ADDRESS); + } + break; + + case DW_TAG_subprogram: + // If the DW_TAG_compile_unit that contained this function had a + // valid address range, add all of the valid subroutine address + // ranges to a collection of addresses which will be sorted + // and verified right before the next DW_TAG_compile_unit is + // processed to make sure that there are no gaps in the address + // range. + range.lo_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, DW_INVALID_ADDRESS); + if (range.lo_pc != DW_INVALID_ADDRESS) + { + range.hi_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_high_pc, DW_INVALID_ADDRESS); + if (range.hi_pc != DW_INVALID_ADDRESS) + { + range.offset = die->GetOffset(); + bool valid = range.ValidRange(); + if (!valid || s->GetVerbose()) + { + s->Printf("\n FUNC "); + range.Dump(s); + if (!valid) + { + ++verifyInfo->addr_range_errors; + s->Printf(" ERROR: Invalid address range for function."); + } + } + + // Only add to our subroutine ranges if our compile unit has a valid address range + // if (valid && verifyInfo->die_ranges.size() >= 2 && verifyInfo->die_ranges[1].range.ValidRange()) + // verifyInfo->subroutine_ranges.InsertRange(range); + } + } + break; + + case DW_TAG_lexical_block: + case DW_TAG_inlined_subroutine: + { + range.lo_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, DW_INVALID_ADDRESS); + if (range.lo_pc != DW_INVALID_ADDRESS) + { + range.hi_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_high_pc, DW_INVALID_ADDRESS); + if (range.hi_pc != DW_INVALID_ADDRESS) + { + range.offset = die->GetOffset(); + bool valid = range.ValidRange(); + if (!valid || s->GetVerbose()) + { + s->Printf("\n BLCK "); + range.Dump(s); + if (!valid) + { + ++verifyInfo->addr_range_errors; + s->Printf(" ERROR: Invalid address range for block or inlined subroutine."); + } + } + } + } + } + break; + } + + if (range.ValidRange() && verifyInfo->die_ranges.back().range.ValidRange()) + { + if (!verifyInfo->die_ranges.back().range.Contains(range)) + { + ++verifyInfo->addr_range_errors; + s->Printf("\n "); + range.Dump(s); + s->Printf(" ERROR: Range is not in parent"); + verifyInfo->die_ranges.back().range.Dump(s); + } + } + + if (die->HasChildren()) + { + // Keep tabs on the valid address ranges for the current item to make + // sure that it all fits (make sure the sibling offsets got fixed up + // correctly if any functions were dead stripped). + DIERange die_range; + die_range.range = range; + die_range.lo_die_offset = next_offset; + die_range.hi_die_offset = sibling; + if (die_range.hi_die_offset == DW_INVALID_OFFSET) + die_range.hi_die_offset = verifyInfo->die_ranges.back().hi_die_offset; + verifyInfo->die_ranges.push_back(die_range); + } + } + else + { + // NULL entry + verifyInfo->die_ranges.pop_back(); + } + } + else + { + // cu->Dump(ostrm_ptr); // Dump the compile unit for the DIE + // We have a new comile unit header + verifyInfo->die_ranges.clear(); + DIERange die_range; + die_range.range.offset = cu->GetOffset(); + die_range.lo_die_offset = next_offset; + die_range.hi_die_offset = cu->GetNextCompileUnitOffset(); + verifyInfo->die_ranges.push_back(die_range); + } + + // Just return the current offset to parse the next CU or DIE entry + return next_offset; +} + + +class CompareDIEStatSizes +{ +public: + bool operator() (const DIEStatMap::const_iterator& pos1, const DIEStatMap::const_iterator& pos2) const + { + return pos1->second.byte_size <= pos2->second.byte_size; + } +}; + +class CompareAttrDIEStatSizes +{ +public: + bool operator() (const DwarfAttrStatMap::const_iterator& pos1, const DwarfAttrStatMap::const_iterator& pos2) const + { + return pos1->second.byte_size <= pos2->second.byte_size; + } +}; + +//---------------------------------------------------------------------- +// Verify +// +// Verifies the DWARF information is valid. +//---------------------------------------------------------------------- +void +DWARFDebugInfo::Verify(Stream *s, SymbolFileDWARF* dwarf2Data) +{ + s->Printf("Verifying Compile Unit Header chain....."); + VerifyInfo verifyInfo(s); + verifyInfo.addr_range_errors = 0; + verifyInfo.sibling_errors = 0; + + bool verbose = s->GetVerbose(); + + uint32_t offset = 0; + if (verbose) + s->EOL(); +// vector<dw_offset_t> valid_cu_offsets; + DWARFCompileUnit cu (dwarf2Data); + bool success = true; + while ( success && dwarf2Data->get_debug_info_data().ValidOffset(offset+cu.Size()) ) + { + success = cu.Extract (dwarf2Data->get_debug_info_data(), &offset); + if (!success) + s->Printf("ERROR\n"); + // else + // valid_cu_offsets.push_back(cu.GetOffset()); + + cu.Verify(verifyInfo.strm); + offset = cu.GetNextCompileUnitOffset(); + } + + if (success) + s->Printf("OK\n"); + + s->Printf("Verifying address ranges and siblings..."); + if (verbose) + s->EOL(); + DWARFDebugInfo::Parse(dwarf2Data, VerifyCallback, &verifyInfo); + +// VerifyAddressRangesForCU(&verifyInfo); + + if (verifyInfo.addr_range_errors > 0) + s->Printf("\nERRORS - %u error(s) were found.\n", verifyInfo.addr_range_errors); + else + s->Printf("OK\n"); + + uint32_t total_category_sizes[kNumTagCategories] = {0}; + uint32_t total_category_count[kNumTagCategories] = {0}; + uint32_t total_die_count = 0; + uint32_t total_die_size = 0; + + typedef set<DIEStatMap::const_iterator, CompareDIEStatSizes> DIEStatBySizeMap; + + s->PutCString( "\n" + "DWARF Statistics\n" + "Count Size Size % Tag\n" + "-------- -------- -------- -------------------------------------------\n"); + DIEStatBySizeMap statBySizeMap; + DIEStatMap::const_iterator pos; + DIEStatMap::const_iterator end_pos = verifyInfo.die_stats.end(); + for (pos = verifyInfo.die_stats.begin(); pos != end_pos; ++pos) + { + const uint32_t die_count = pos->second.count; + const uint32_t die_size = pos->second.byte_size; + + statBySizeMap.insert(pos); + total_die_count += die_count; + total_die_size += die_size; + DW_TAG_CategoryEnum category = get_tag_category(pos->first); + total_category_sizes[category] += die_size; + total_category_count[category] += die_count; + } + + float total_die_size_float = total_die_size; + + DIEStatBySizeMap::const_reverse_iterator size_pos; + DIEStatBySizeMap::const_reverse_iterator size_pos_end = statBySizeMap.rend(); + float percentage; + for (size_pos = statBySizeMap.rbegin(); size_pos != size_pos_end; ++size_pos) + { + pos = *size_pos; + + const DIEStat& tag_stat = pos->second; + + const uint32_t die_count = tag_stat.count; + const uint32_t die_size = tag_stat.byte_size; + percentage = ((float)die_size/total_die_size_float)*100.0; + s->Printf("%7u %8u %2.2f%% %s\n", die_count, die_size, percentage, DW_TAG_value_to_name(pos->first)); + + const DwarfAttrStatMap& attr_stats = tag_stat.attr_stats; + if (!attr_stats.empty()) + { + typedef set<DwarfAttrStatMap::const_iterator, CompareAttrDIEStatSizes> DwarfAttrStatBySizeMap; + DwarfAttrStatBySizeMap attrStatBySizeMap; + DwarfAttrStatMap::const_iterator attr_stat_pos; + DwarfAttrStatMap::const_iterator attr_stat_pos_end = attr_stats.end(); + for (attr_stat_pos = attr_stats.begin(); attr_stat_pos != attr_stat_pos_end; ++attr_stat_pos) + { + attrStatBySizeMap.insert(attr_stat_pos); + } + + DwarfAttrStatBySizeMap::const_reverse_iterator attr_size_pos; + DwarfAttrStatBySizeMap::const_reverse_iterator attr_size_pos_end = attrStatBySizeMap.rend(); + for (attr_size_pos = attrStatBySizeMap.rbegin(); attr_size_pos != attr_size_pos_end; ++attr_size_pos) + { + attr_stat_pos = *attr_size_pos; + percentage = ((float)attr_stat_pos->second.byte_size/die_size)*100.0; + s->Printf("%7u %8u %2.2f%% %s\n", attr_stat_pos->second.count, attr_stat_pos->second.byte_size, percentage, DW_AT_value_to_name(attr_stat_pos->first)); + } + s->EOL(); + } + } + + s->Printf("-------- -------- -------- -------------------------------------------\n"); + s->Printf("%7u %8u 100.00% Total for all DIEs\n", total_die_count, total_die_size); + + float total_category_percentages[kNumTagCategories] = + { + ((float)total_category_sizes[TagCategoryVariable]/total_die_size_float)*100.0, + ((float)total_category_sizes[TagCategoryType]/total_die_size_float)*100.0, + ((float)total_category_sizes[TagCategoryProgram]/total_die_size_float)*100.0 + }; + + s->EOL(); + s->Printf("%7u %8u %2.2f%% %s\n", total_category_count[TagCategoryVariable], total_category_sizes[TagCategoryVariable], total_category_percentages[TagCategoryVariable], "Total for variable related DIEs"); + s->Printf("%7u %8u %2.2f%% %s\n", total_category_count[TagCategoryType], total_category_sizes[TagCategoryType], total_category_percentages[TagCategoryType], "Total for type related DIEs"); + s->Printf("%7u %8u %2.2f%% %s\n", total_category_count[TagCategoryProgram], total_category_sizes[TagCategoryProgram], total_category_percentages[TagCategoryProgram], "Total for program related DIEs"); + s->Printf("\n\n"); +} + +typedef struct DumpInfo +{ + DumpInfo(Stream* init_strm, uint32_t off, uint32_t depth) : + strm(init_strm), + die_offset(off), + recurse_depth(depth), + found_depth(UINT_MAX), + found_die(false), + ancestors() + { + } + Stream* strm; + const uint32_t die_offset; + const uint32_t recurse_depth; + uint32_t found_depth; + bool found_die; + std::vector<DWARFDebugInfoEntry> ancestors; + + DISALLOW_COPY_AND_ASSIGN(DumpInfo); +} DumpInfo; + +//---------------------------------------------------------------------- +// DumpCallback +// +// A callback function for the static DWARFDebugInfo::Parse() function +// that gets called each time a compile unit header or debug information +// entry is successfully parsed. +// +// This function dump DWARF information and obey recurse depth and +// wether a single DIE is to be dumped (or all of the data). +//---------------------------------------------------------------------- +static dw_offset_t DumpCallback +( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnitSP& cu_sp, + DWARFDebugInfoEntry* die, + const dw_offset_t next_offset, + const uint32_t curr_depth, + void* userData +) +{ + DumpInfo* dumpInfo = (DumpInfo*)userData; + + const DWARFCompileUnit* cu = cu_sp.get(); + + Stream *s = dumpInfo->strm; + bool show_parents = s->GetFlags().IsSet(DWARFDebugInfo::eDumpFlag_ShowAncestors); + + if (die) + { + // Are we dumping everything? + if (dumpInfo->die_offset == DW_INVALID_OFFSET) + { + // Yes we are dumping everything. Obey our recurse level though + if (curr_depth < dumpInfo->recurse_depth) + die->Dump(dwarf2Data, cu, s, 0); + } + else + { + // We are dumping a specific DIE entry by offset + if (dumpInfo->die_offset == die->GetOffset()) + { + // We found the DIE we were looking for, dump it! + if (show_parents) + { + s->SetIndentLevel(0); + const uint32_t num_ancestors = dumpInfo->ancestors.size(); + if (num_ancestors > 0) + { + for (uint32_t i=0; i<num_ancestors-1; ++i) + { + dumpInfo->ancestors[i].Dump(dwarf2Data, cu, s, 0); + s->IndentMore(); + } + } + } + + dumpInfo->found_depth = curr_depth; + + die->Dump(dwarf2Data, cu, s, 0); + + // Note that we found the DIE we were looking for + dumpInfo->found_die = true; + + // Since we are dumping a single DIE, if there are no children we are done! + if (!die->HasChildren() || dumpInfo->recurse_depth == 0) + return DW_INVALID_OFFSET; // Return an invalid address to end parsing + } + else if (dumpInfo->found_die) + { + // Are we done with all the children? + if (curr_depth <= dumpInfo->found_depth) + return DW_INVALID_OFFSET; + + // We have already found our DIE and are printing it's children. Obey + // our recurse depth and return an invalid offset if we get done + // dumping all the the children + if (dumpInfo->recurse_depth == UINT_MAX || curr_depth <= dumpInfo->found_depth + dumpInfo->recurse_depth) + die->Dump(dwarf2Data, cu, s, 0); + } + else if (dumpInfo->die_offset > die->GetOffset()) + { + if (show_parents) + dumpInfo->ancestors.back() = *die; + } + } + + // Keep up with our indent level + if (die->IsNULL()) + { + if (show_parents) + dumpInfo->ancestors.pop_back(); + + if (curr_depth <= 1) + return cu->GetNextCompileUnitOffset(); + else + s->IndentLess(); + } + else if (die->HasChildren()) + { + if (show_parents) + { + DWARFDebugInfoEntry null_die; + dumpInfo->ancestors.push_back(null_die); + } + s->IndentMore(); + } + } + else + { + if (cu == NULL) + s->PutCString("NULL - cu"); + // We have a compile unit, reset our indent level to zero just in case + s->SetIndentLevel(0); + + // See if we are dumping everything? + if (dumpInfo->die_offset == DW_INVALID_OFFSET) + { + // We are dumping everything + cu->Dump(s); + return cu->GetFirstDIEOffset(); // Return true to parse all DIEs in this Compile Unit + } + else + { + if (show_parents) + { + dumpInfo->ancestors.clear(); + dumpInfo->ancestors.resize(1); + } + + // We are dumping only a single DIE possibly with it's children and + // we must find it's compile unit before we can dump it properly + if (dumpInfo->die_offset < cu->GetFirstDIEOffset()) + { + // Not found, maybe the DIE offset provided wasn't correct? + // *ostrm_ptr << "DIE at offset " << HEX32 << dumpInfo->die_offset << " was not found." << endl; + return DW_INVALID_OFFSET; + } + else + { + // See if the DIE is in this compile unit? + if (dumpInfo->die_offset < cu->GetNextCompileUnitOffset()) + { + // This DIE is in this compile unit! + if (s->GetVerbose()) + cu->Dump(s); // Dump the compile unit for the DIE in verbose mode + + return next_offset; + // // We found our compile unit that contains our DIE, just skip to dumping the requested DIE... + // return dumpInfo->die_offset; + } + else + { + // Skip to the next compile unit as the DIE isn't in the current one! + return cu->GetNextCompileUnitOffset(); + } + } + } + } + + // Just return the current offset to parse the next CU or DIE entry + return next_offset; +} + +//---------------------------------------------------------------------- +// Dump +// +// Dump the information in the .debug_info section to the specified +// ostream. If die_offset is valid, a single DIE will be dumped. If the +// die_offset is invalid, all the DWARF information will be dumped. Both +// cases will obey a "recurse_depth" or how deep to traverse into the +// children of each DIE entry. A recurse_depth of zero will dump all +// compile unit headers. A recurse_depth of 1 will dump all compile unit +// headers and the DW_TAG_compile unit tags. A depth of 2 will also +// dump all types and functions. +//---------------------------------------------------------------------- +void +DWARFDebugInfo::Dump +( + Stream *s, + SymbolFileDWARF* dwarf2Data, + const uint32_t die_offset, + const uint32_t recurse_depth +) +{ + DumpInfo dumpInfo(s, die_offset, recurse_depth); + s->PutCString(".debug_info contents"); + if (dwarf2Data->get_debug_info_data().GetByteSize() > 0) + { + if (die_offset == DW_INVALID_OFFSET) + s->PutCString(":\n"); + else + { + s->Printf(" for DIE entry at .debug_info[0x%8.8x]", die_offset); + if (recurse_depth != UINT_MAX) + s->Printf(" recursing %u levels deep.", recurse_depth); + s->EOL(); + } + } + else + { + s->PutCString(": < EMPTY >\n"); + return; + } + DWARFDebugInfo::Parse(dwarf2Data, DumpCallback, &dumpInfo); +} + + +//---------------------------------------------------------------------- +// Dump +// +// Dump the contents of this DWARFDebugInfo object as has been parsed +// and/or modified after it has been parsed. +//---------------------------------------------------------------------- +void +DWARFDebugInfo::Dump (Stream *s, const uint32_t die_offset, const uint32_t recurse_depth) +{ + DumpInfo dumpInfo(s, die_offset, recurse_depth); + + s->PutCString("Dumping .debug_info section from internal representation\n"); + + CompileUnitColl::const_iterator pos; + uint32_t curr_depth = 0; + ParseCompileUnitHeadersIfNeeded(); + for (pos = m_compile_units.begin(); pos != m_compile_units.end(); ++pos) + { + const DWARFCompileUnitSP& cu_sp = *pos; + DumpCallback(m_dwarf2Data, (DWARFCompileUnitSP&)cu_sp, NULL, 0, curr_depth, &dumpInfo); + cu_sp->DIE()->Dump(m_dwarf2Data, cu_sp.get(), s, recurse_depth); + } +} + + +//---------------------------------------------------------------------- +// FindCallbackString +// +// A callback function for the static DWARFDebugInfo::Parse() function +// that gets called each time a compile unit header or debug information +// entry is successfully parsed. +// +// This function will find the die_offset of any items whose DW_AT_name +// matches the given string +//---------------------------------------------------------------------- +typedef struct FindCallbackStringInfoTag +{ + const char* name; + bool ignore_case; + RegularExpression* regex; + vector<dw_offset_t>& die_offsets; +} FindCallbackStringInfo; + +static dw_offset_t FindCallbackString +( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnitSP& cu_sp, + DWARFDebugInfoEntry* die, + const dw_offset_t next_offset, + const uint32_t curr_depth, + void* userData +) +{ + FindCallbackStringInfo* info = (FindCallbackStringInfo*)userData; + const DWARFCompileUnit* cu = cu_sp.get(); + + if (die) + { + const char* die_name = die->GetName(dwarf2Data, cu); + if (die_name) + { + if (info->regex) + { + if (info->regex->Execute(die_name)) + info->die_offsets.push_back(die->GetOffset()); + } + else + { + if ((info->ignore_case ? strcasecmp(die_name, info->name) : strcmp(die_name, info->name)) == 0) + info->die_offsets.push_back(die->GetOffset()); + } + } + } + + // Just return the current offset to parse the next CU or DIE entry + return next_offset; +} + +//---------------------------------------------------------------------- +// Find +// +// Finds all DIE that have a specific DW_AT_name attribute by manually +// searching through the debug information (not using the +// .debug_pubnames section). The string must match the entire name +// and case sensitive searches are an option. +//---------------------------------------------------------------------- +bool +DWARFDebugInfo::Find(const char* name, bool ignore_case, vector<dw_offset_t>& die_offsets) const +{ + die_offsets.clear(); + if (name && name[0]) + { + FindCallbackStringInfo info = { name, ignore_case, NULL, die_offsets }; + DWARFDebugInfo::Parse(m_dwarf2Data, FindCallbackString, &info); + } + return !die_offsets.empty(); +} + +//---------------------------------------------------------------------- +// Find +// +// Finds all DIE that have a specific DW_AT_name attribute by manually +// searching through the debug information (not using the +// .debug_pubnames section). The string must match the supplied regular +// expression. +//---------------------------------------------------------------------- +bool +DWARFDebugInfo::Find(RegularExpression& re, vector<dw_offset_t>& die_offsets) const +{ + die_offsets.clear(); + FindCallbackStringInfo info = { NULL, false, &re, die_offsets }; + DWARFDebugInfo::Parse(m_dwarf2Data, FindCallbackString, &info); + return !die_offsets.empty(); +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h new file mode 100644 index 00000000000..f506a3de6e0 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h @@ -0,0 +1,86 @@ +//===-- DWARFDebugInfo.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_DWARFDebugInfo_h_ +#define SymbolFileDWARF_DWARFDebugInfo_h_ + +#include <vector> +#include <map> + +#include "lldb/lldb-private.h" +#include "lldb/lldb-private.h" +#include "SymbolFileDWARF.h" + +typedef std::multimap<const char*, dw_offset_t, CStringCompareFunctionObject> CStringToDIEMap; +typedef CStringToDIEMap::iterator CStringToDIEMapIter; +typedef CStringToDIEMap::const_iterator CStringToDIEMapConstIter; + +typedef lldb::SharedPtr<DWARFCompileUnit>::Type DWARFCompileUnitSP; + +class DWARFDebugInfo +{ +public: + typedef dw_offset_t (*Callback)( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnitSP& cu_shared_ptr, + DWARFDebugInfoEntry* die, + const dw_offset_t next_offset, + const uint32_t depth, + void* userData); + + DWARFDebugInfo(); + void SetDwarfData(SymbolFileDWARF* dwarf2Data); + bool BuildFunctionAddressRangeTable(DWARFDebugAranges* debug_aranges); + + bool LookupAddress( + const dw_addr_t address, + const dw_offset_t cu_offset, // Can be valid (find in .debug_aranges), or DW_INVALID_OFFSET if we need to search manually + DWARFCompileUnitSP& cu_shared_ptr, + DWARFDebugInfoEntry** function_die, + DWARFDebugInfoEntry** block_die); + + void AddCompileUnit(DWARFCompileUnitSP& cu); + uint32_t GetNumCompileUnits(); + DWARFCompileUnit* GetCompileUnitAtIndex(uint32_t idx); + DWARFCompileUnitSP GetCompileUnit(dw_offset_t cu_offset, uint32_t* idx_ptr = NULL); + DWARFCompileUnitSP GetCompileUnitContainingDIE(dw_offset_t die_offset); + + DWARFDebugInfoEntry* GetDIEPtr(dw_offset_t die_offset, DWARFCompileUnitSP* cu_sp_ptr); + const DWARFDebugInfoEntry* GetDIEPtrContainingOffset(dw_offset_t die_offset, DWARFCompileUnitSP* cu_sp_ptr); + + void Dump(lldb_private::Stream *s, const uint32_t die_offset, const uint32_t recurse_depth); + static void Parse(SymbolFileDWARF* parser, Callback callback, void* userData); + static void Verify(lldb_private::Stream *s, SymbolFileDWARF* dwarf2Data); + static void Dump(lldb_private::Stream *s, SymbolFileDWARF* dwarf2Data, const uint32_t die_offset, const uint32_t recurse_depth); + bool Find(const char* name, bool ignore_case, std::vector<dw_offset_t>& die_offsets) const; + bool Find(lldb_private::RegularExpression& re, std::vector<dw_offset_t>& die_offsets) const; + + enum + { + eDumpFlag_Verbose = (1<<0), // Verbose dumping + eDumpFlag_ShowForm = (1<<1), // Show the DW_form type + eDumpFlag_EnglishyNames = (1<<2), // Show the DW_TAG, DW_AT and DW_FORM types in more englishy names instead of as DWARF definitions values + eDumpFlag_ShowAncestors = (1<<3) // Show all parent DIEs when dumping single DIEs + }; + + +protected: + SymbolFileDWARF* m_dwarf2Data; + typedef std::vector<DWARFCompileUnitSP> CompileUnitColl; + + CompileUnitColl m_compile_units; + +private: + // All parsing needs to be done partially any managed by this class as accessors are called. + void ParseCompileUnitHeadersIfNeeded(); + + DISALLOW_COPY_AND_ASSIGN (DWARFDebugInfo); +}; + +#endif // SymbolFileDWARF_DWARFDebugInfo_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp new file mode 100644 index 00000000000..19eef06d3d1 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp @@ -0,0 +1,1929 @@ +//===-- DWARFDebugInfoEntry.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugInfoEntry.h" + +#include <assert.h> + +#include <algorithm> + +#include "lldb/Core/Stream.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/ObjectFile.h" + +#include "DWARFCompileUnit.h" +#include "SymbolFileDWARF.h" +#include "DWARFDebugAbbrev.h" +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfo.h" +#include "DWARFDIECollection.h" +#include "DWARFFormValue.h" +#include "DWARFLocationDescription.h" +#include "DWARFLocationList.h" +#include "DWARFDebugRanges.h" + +using namespace lldb_private; +using namespace std; +extern int g_verbose; + + + +DWARFDebugInfoEntry::Attributes::Attributes() : + m_infos() +{ + m_infos.reserve(20); +} + +DWARFDebugInfoEntry::Attributes::~Attributes() +{ +} + + +uint32_t +DWARFDebugInfoEntry::Attributes::FindAttributeIndex(dw_attr_t attr) const +{ + std::vector<Info>::const_iterator end = m_infos.end(); + std::vector<Info>::const_iterator beg = m_infos.begin(); + std::vector<Info>::const_iterator pos; + for (pos = beg; pos != end; ++pos) + { + if (pos->attr == attr) + return std::distance(beg, pos); + } + return UINT_MAX; +} + +void +DWARFDebugInfoEntry::Attributes::Append(const DWARFCompileUnit *cu, dw_offset_t attr_die_offset, dw_attr_t attr, dw_form_t form) +{ + Info info = { cu, attr_die_offset, attr, form }; + m_infos.push_back(info); +} + +bool +DWARFDebugInfoEntry::Attributes::ContainsAttribute(dw_attr_t attr) const +{ + return FindAttributeIndex(attr) != UINT_MAX; +} + +bool +DWARFDebugInfoEntry::Attributes::RemoveAttribute(dw_attr_t attr) +{ + uint32_t attr_index = FindAttributeIndex(attr); + if (attr_index != UINT_MAX) + { + m_infos.erase(m_infos.begin() + attr_index); + return true; + } + return false; +} + +bool +DWARFDebugInfoEntry::Attributes::ExtractFormValueAtIndex (SymbolFileDWARF* dwarf2Data, uint32_t i, DWARFFormValue &form_value) const +{ + form_value.SetForm(FormAtIndex(i)); + dw_offset_t offset = DIEOffsetAtIndex(i); + return form_value.ExtractValue(dwarf2Data->get_debug_info_data(), &offset, CompileUnitAtIndex(i)); +} + +uint64_t +DWARFDebugInfoEntry::Attributes::FormValueAsUnsignedAtIndex(SymbolFileDWARF* dwarf2Data, uint32_t i, uint64_t fail_value) const +{ + DWARFFormValue form_value; + if (ExtractFormValueAtIndex(dwarf2Data, i, form_value)) + return form_value.Reference(CompileUnitAtIndex(i)); + return fail_value; +} + + +//---------------------------------------------------------------------- +// Extract +// +// Extract a debug info entry for a given compile unit from the +// .debug_info and .debug_abbrev data within the SymbolFileDWARF class +// starting at the given offset +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::Extract +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + uint32_t* offset_ptr +) +{ + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); +// const DataExtractor& debug_str_data = dwarf2Data->get_debug_str_data(); + const uint32_t cu_end_offset = cu->GetNextCompileUnitOffset(); + const uint8_t cu_addr_size = cu->GetAddressByteSize(); + uint32_t offset = *offset_ptr; +// if (offset >= cu_end_offset) +// Log::Error("DIE at offset 0x%8.8x is beyond the end of the current compile unit (0x%8.8x)", m_offset, cu_end_offset); + if ((offset < cu_end_offset) && debug_info_data.ValidOffset(offset)) + { + m_offset = offset; + + dw_uleb128_t abbrCode = debug_info_data.GetULEB128(&offset); + + if (abbrCode) + { + m_abbrevDecl = cu->GetAbbreviations()->GetAbbreviationDeclaration(abbrCode); + + if (m_abbrevDecl) + { + dw_tag_t tag = m_abbrevDecl->Tag(); + + bool isCompileUnitTag = tag == DW_TAG_compile_unit; + if (cu && isCompileUnitTag) + ((DWARFCompileUnit*)cu)->SetBaseAddress(0); + + // Skip all data in the .debug_info for the attributes + const uint32_t numAttributes = m_abbrevDecl->NumAttributes(); + uint32_t i; + dw_attr_t attr; + dw_form_t form; + for (i=0; i<numAttributes; ++i) + { + m_abbrevDecl->GetAttrAndFormByIndexUnchecked(i, attr, form); + + if (isCompileUnitTag && ((attr == DW_AT_entry_pc) || (attr == DW_AT_low_pc))) + { + DWARFFormValue form_value(form); + if (form_value.ExtractValue(debug_info_data, &offset, cu)) + { + if (attr == DW_AT_low_pc || attr == DW_AT_entry_pc) + ((DWARFCompileUnit*)cu)->SetBaseAddress(form_value.Unsigned()); + } + } + else + { +die_extract_indirect_form: + register uint32_t form_size = 0; + switch (form) + { + // Blocks if inlined data that have a length field and the data bytes + // inlined in the .debug_info + case DW_FORM_block : form_size = debug_info_data.GetULEB128(&offset); break; + case DW_FORM_block1 : form_size = debug_info_data.GetU8(&offset); break; + case DW_FORM_block2 : form_size = debug_info_data.GetU16(&offset); break; + case DW_FORM_block4 : form_size = debug_info_data.GetU32(&offset); break; + + // Inlined NULL terminated C-strings + case DW_FORM_string : + { +// const char *s = + debug_info_data.GetCStr(&offset); +// switch (attr) +// { +// case DW_AT_name: m_name = s; break; +// case DW_AT_MIPS_linkage_name: m_linkage_name = s; break; +// default: break; +// } + } + break; + + // Compile unit address sized values + case DW_FORM_addr : + case DW_FORM_ref_addr : + form_size = cu_addr_size; + break; + + // 1 byte values + case DW_FORM_data1 : + case DW_FORM_flag : + case DW_FORM_ref1 : + form_size = 1; + break; + + // 2 byte values + case DW_FORM_data2 : + case DW_FORM_ref2 : + form_size = 2; + break; + + // 4 byte values + case DW_FORM_strp : +// switch (attr) +// { +// case DW_AT_name: +// m_name = debug_str_data.PeekCStr(debug_info_data.GetU32(&offset)); +// break; +// case DW_AT_MIPS_linkage_name: +// m_linkage_name = debug_str_data.PeekCStr(debug_info_data.GetU32(&offset)); +// break; +// +// default: + form_size = 4; +// break; +// } + break; + + case DW_FORM_data4 : + case DW_FORM_ref4 : + form_size = 4; + break; + + // 8 byte values + case DW_FORM_data8 : + case DW_FORM_ref8 : + form_size = 8; + break; + + // signed or unsigned LEB 128 values + // case DW_FORM_APPLE_db_str: + case DW_FORM_sdata : + case DW_FORM_udata : + case DW_FORM_ref_udata : + debug_info_data.Skip_LEB128(&offset); + break; + + case DW_FORM_indirect : + form = debug_info_data.GetULEB128(&offset); + goto die_extract_indirect_form; + + default: + *offset_ptr = offset; + return false; + } + + offset += form_size; + } + } + *offset_ptr = offset; + return true; + } + } + else + { + m_abbrevDecl = NULL; + *offset_ptr = offset; + return true; // NULL debug tag entry + } + } + + return false; +} + +//---------------------------------------------------------------------- +// AppendDependentDIES() +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::AppendDependentDIES +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const bool add_children, + DWARFDIECollection& dependent_dies +) const +{ + // Add this object's DIE offset + // The line below is the only place that should add a die to the + // dependent_dies collection as we have to be careful of recursion! + if ( !dependent_dies.Insert(this) ) + return false; // This DIE already exists in the collection, nothing to do! + + //DEBUG_PRINTF(" dependent_dies.Insert(0x%8.8x)\n", GetOffset());/// + + if (m_abbrevDecl) + { + // Keep adding parent DIE offsets as long as the offsets do not + // already exist in the collection + const DWARFDebugInfoEntry* die = GetParent(); + while ( die && die->AppendDependentDIES(dwarf2Data, cu, false, dependent_dies) ) + die = die->GetParent(); + + bool add_non_subprogram_children = false; + bool add_children_override = false; + + if (!add_children) + { + switch (m_abbrevDecl->Tag()) + { + case DW_TAG_array_type: break; + case DW_TAG_class_type: add_non_subprogram_children = true; break; + case DW_TAG_entry_point: break; + case DW_TAG_enumeration_type: break; + case DW_TAG_formal_parameter: break; + case DW_TAG_imported_declaration: break; + case DW_TAG_label: break; + case DW_TAG_lexical_block: add_children_override = true; break; + case DW_TAG_member: break; + case DW_TAG_pointer_type: break; + case DW_TAG_reference_type: break; + case DW_TAG_compile_unit: break; + case DW_TAG_string_type: break; + case DW_TAG_structure_type: add_non_subprogram_children = true; break; + case DW_TAG_subroutine_type: add_children_override = true; break; + case DW_TAG_typedef: break; + case DW_TAG_union_type: add_non_subprogram_children = true; break; + case DW_TAG_unspecified_parameters: break; + case DW_TAG_variant: break; + case DW_TAG_common_block: break; + case DW_TAG_common_inclusion: break; + case DW_TAG_inheritance: break; + case DW_TAG_inlined_subroutine: break; + case DW_TAG_module: break; + case DW_TAG_ptr_to_member_type: break; + case DW_TAG_set_type: break; + case DW_TAG_subrange_type: break; + case DW_TAG_with_stmt: break; + case DW_TAG_access_declaration: break; + case DW_TAG_base_type: break; + case DW_TAG_catch_block: break; + case DW_TAG_const_type: break; + case DW_TAG_constant: break; + case DW_TAG_enumerator: break; + case DW_TAG_file_type: break; + case DW_TAG_friend: break; + case DW_TAG_namelist: break; + case DW_TAG_namelist_item: break; + case DW_TAG_packed_type: break; + case DW_TAG_subprogram: add_children_override = true; break; + case DW_TAG_template_type_parameter: break; + case DW_TAG_template_value_parameter: break; + case DW_TAG_thrown_type: break; + case DW_TAG_try_block: break; + case DW_TAG_variant_part: break; + case DW_TAG_variable: break; + case DW_TAG_volatile_type: break; + case DW_TAG_dwarf_procedure: break; + case DW_TAG_restrict_type: break; + case DW_TAG_interface_type: break; + case DW_TAG_namespace: break; + case DW_TAG_imported_module: break; + case DW_TAG_unspecified_type: break; + case DW_TAG_partial_unit: break; + case DW_TAG_imported_unit: break; + case DW_TAG_shared_type: break; + } + } + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); + + // Dump all data in the .debug_info for the attributes + const uint32_t numAttributes = m_abbrevDecl->NumAttributes(); + uint32_t i; + dw_offset_t offset = GetOffset(); + debug_info_data.Skip_LEB128(&offset); // Skip abbreviation code + + dw_attr_t attr; + dw_form_t form; + for (i=0; i<numAttributes; ++i) + { + m_abbrevDecl->GetAttrAndFormByIndexUnchecked(i, attr, form); + DWARFFormValue form_value(form); + + switch (attr) + { + // All cases that use refer to another DIE should use this case + // without + // having to check the FORM of the attribute to tell if it refers to another + // DIE + case DW_AT_abstract_origin: + case DW_AT_import: + case DW_AT_discr: + case DW_AT_containing_type: + case DW_AT_base_types: + case DW_AT_friend: + case DW_AT_specification: + case DW_AT_type: + case DW_AT_common_reference: + case DW_AT_default_value: + { + form_value.ExtractValue(debug_info_data, &offset, cu); + DWARFCompileUnitSP cu_sp_ptr; + const DWARFDebugInfoEntry* ref_die = const_cast<SymbolFileDWARF*>(dwarf2Data)->DebugInfo()->GetDIEPtr(form_value.Reference(cu), &cu_sp_ptr); + if (ref_die) + ref_die->AppendDependentDIES(dwarf2Data, cu_sp_ptr.get(), true, dependent_dies); + } + break; + + default: + if (attr != DW_AT_sibling) + { + switch (form_value.Form()) + { + case DW_FORM_ref_addr: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: +// Log::WarningVerbose("DWARFDebugInfoEntry::AppendDependentDIES() -- check on this item %s: attr = %s form = %s", +// DW_TAG_value_to_name(m_abbrevDecl->Tag()), +// DW_AT_value_to_name(attr), +// DW_FORM_value_to_name(form)); + break; + } + } + form_value.SkipValue(debug_info_data, &offset, cu); + break; + } + } + + if (m_abbrevDecl->HasChildren()) + { + const DWARFDebugInfoEntry* child; + for (child = GetFirstChild(); child != NULL; child = child->GetSibling()) + { + bool add = add_children || add_children_override; + + if (!add) + { + if (add_non_subprogram_children) + { + // add_non_subprogram_children is used for classes and structs + // that may contain children that are the member variables that + // may have functions as children and whom may add the class or + // struct by adding their parent. We don't want to add any + // functions though since they may have been optimized out. But + // we do need to watch for declarations and keep them. + if (child->Tag() == DW_TAG_subprogram) + { + // Check if this subprogram TAG had a DW_AT_declaration attribute set to 1. + // If so we need to include this DIE so that we always have a complete view + // of a class definition so debuggers can track down any weak symbols that + // may not have had weak definition entries. + if (child->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_declaration, 0) == 1) + add = true; + } + else + { + // Add all other items inside a class/struct + add = true; + } + } + else + { + // We don't need to add this child, only add it if it's a NULL tag + add = child->IsNULL(); + } + } + + if (add) + child->AppendDependentDIES(dwarf2Data, cu, true, dependent_dies); + } + } + } + return true; +} + +//---------------------------------------------------------------------- +// DumpAncestry +// +// Dumps all of a debug information entries parents up until oldest and +// all of it's attributes to the specified stream. +//---------------------------------------------------------------------- +void +DWARFDebugInfoEntry::DumpAncestry +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const DWARFDebugInfoEntry* oldest, + Stream *s, + uint32_t recurse_depth +) const +{ + const DWARFDebugInfoEntry* parent = GetParent(); + if (parent && parent != oldest) + parent->DumpAncestry(dwarf2Data, cu, oldest, s, 0); + Dump(dwarf2Data, cu, s, recurse_depth); +} + +//---------------------------------------------------------------------- +// Compare two DIE by comparing all their attributes values, and +// following all DW_FORM_ref attributes and comparing their contents as +// well (except for DW_AT_sibling attributes. +// +// DWARFDebugInfoEntry::CompareState compare_state; +// int result = DWARFDebugInfoEntry::Compare(this, 0x00017ccb, 0x0001eb2b, compare_state, false, true); +//---------------------------------------------------------------------- +int +DWARFDebugInfoEntry::Compare +( + SymbolFileDWARF* dwarf2Data, + dw_offset_t a_die_offset, + dw_offset_t b_die_offset, + CompareState &compare_state, + bool compare_siblings, + bool compare_children +) +{ + if (a_die_offset == b_die_offset) + return 0; + + DWARFCompileUnitSP a_cu_sp; + DWARFCompileUnitSP b_cu_sp; + const DWARFDebugInfoEntry* a_die = dwarf2Data->DebugInfo()->GetDIEPtr(a_die_offset, &a_cu_sp); + const DWARFDebugInfoEntry* b_die = dwarf2Data->DebugInfo()->GetDIEPtr(b_die_offset, &b_cu_sp); + + return Compare(dwarf2Data, a_cu_sp.get(), a_die, b_cu_sp.get(), b_die, compare_state, compare_siblings, compare_children); +} + +int +DWARFDebugInfoEntry::Compare +( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* a_cu, const DWARFDebugInfoEntry* a_die, + DWARFCompileUnit* b_cu, const DWARFDebugInfoEntry* b_die, + CompareState &compare_state, + bool compare_siblings, + bool compare_children +) +{ + if (a_die == b_die) + return 0; + + if (!compare_state.AddTypePair(a_die->GetOffset(), b_die->GetOffset())) + { + // We are already comparing both of these types, so let + // compares complete for the real result + return 0; + } + + //printf("DWARFDebugInfoEntry::Compare(0x%8.8x, 0x%8.8x)\n", a_die->GetOffset(), b_die->GetOffset()); + + // Do we have two valid DIEs? + if (a_die && b_die) + { + // Both DIE are valid + int result = 0; + + const dw_tag_t a_tag = a_die->Tag(); + const dw_tag_t b_tag = b_die->Tag(); + if (a_tag == 0 && b_tag == 0) + return 0; + + //printf(" comparing tags: %s and %s\n", DW_TAG_value_to_name(a_tag), DW_TAG_value_to_name(b_tag)); + + if (a_tag < b_tag) + return -1; + else if (a_tag > b_tag) + return 1; + + DWARFDebugInfoEntry::Attributes a_attrs; + DWARFDebugInfoEntry::Attributes b_attrs; + size_t a_attr_count = a_die->GetAttributes(dwarf2Data, a_cu, a_attrs); + size_t b_attr_count = b_die->GetAttributes(dwarf2Data, b_cu, b_attrs); + if (a_attr_count != b_attr_count) + { + a_attrs.RemoveAttribute(DW_AT_sibling); + b_attrs.RemoveAttribute(DW_AT_sibling); + } + + a_attr_count = a_attrs.Size(); + b_attr_count = b_attrs.Size(); + + DWARFFormValue a_form_value; + DWARFFormValue b_form_value; + + if (a_attr_count != b_attr_count) + { + uint32_t is_decl_index = a_attrs.FindAttributeIndex(DW_AT_declaration); + uint32_t a_name_index = UINT_MAX; + uint32_t b_name_index = UINT_MAX; + if (is_decl_index != UINT_MAX) + { + if (a_attr_count == 2) + { + a_name_index = a_attrs.FindAttributeIndex(DW_AT_name); + b_name_index = b_attrs.FindAttributeIndex(DW_AT_name); + } + } + else + { + is_decl_index = b_attrs.FindAttributeIndex(DW_AT_declaration); + if (is_decl_index != UINT_MAX && a_attr_count == 2) + { + a_name_index = a_attrs.FindAttributeIndex(DW_AT_name); + b_name_index = b_attrs.FindAttributeIndex(DW_AT_name); + } + } + if (a_name_index != UINT_MAX && b_name_index != UINT_MAX) + { + if (a_attrs.ExtractFormValueAtIndex(dwarf2Data, a_name_index, a_form_value) && + b_attrs.ExtractFormValueAtIndex(dwarf2Data, b_name_index, b_form_value)) + { + result = DWARFFormValue::Compare (a_form_value, b_form_value, a_cu, b_cu, &dwarf2Data->get_debug_str_data()); + if (result == 0) + { + a_attr_count = b_attr_count = 0; + compare_children = false; + } + } + } + } + + if (a_attr_count < b_attr_count) + return -1; + if (a_attr_count > b_attr_count) + return 1; + + + // The number of attributes are the same... + if (a_attr_count > 0) + { + const DataExtractor* debug_str_data_ptr = &dwarf2Data->get_debug_str_data(); + + uint32_t i; + for (i=0; i<a_attr_count; ++i) + { + const dw_attr_t a_attr = a_attrs.AttributeAtIndex(i); + const dw_attr_t b_attr = b_attrs.AttributeAtIndex(i); + //printf(" comparing attributes\n\t\t0x%8.8x: %s %s\t\t0x%8.8x: %s %s\n", + // a_attrs.DIEOffsetAtIndex(i), DW_FORM_value_to_name(a_attrs.FormAtIndex(i)), DW_AT_value_to_name(a_attr), + // b_attrs.DIEOffsetAtIndex(i), DW_FORM_value_to_name(b_attrs.FormAtIndex(i)), DW_AT_value_to_name(b_attr)); + + if (a_attr < b_attr) + return -1; + else if (a_attr > b_attr) + return 1; + + switch (a_attr) + { + // Since we call a form of GetAttributes which inlines the + // attributes from DW_AT_abstract_origin and DW_AT_specification + // we don't care if their values mismatch... + case DW_AT_abstract_origin: + case DW_AT_specification: + case DW_AT_sibling: + case DW_AT_containing_type: + //printf(" action = IGNORE\n"); + result = 0; + break; // ignore + + default: + if (a_attrs.ExtractFormValueAtIndex(dwarf2Data, i, a_form_value) && + b_attrs.ExtractFormValueAtIndex(dwarf2Data, i, b_form_value)) + result = DWARFFormValue::Compare (a_form_value, b_form_value, a_cu, b_cu, debug_str_data_ptr); + break; + } + + //printf("\t result = %i\n", result); + + if (result != 0) + { + // Attributes weren't equal, lets see if we care? + switch (a_attr) + { + case DW_AT_decl_file: + // TODO: add the ability to compare files in two different compile units + if (a_cu == b_cu) + { + //printf(" action = RETURN RESULT\n"); + return result; // Only return the compare results when the compile units are the same and the decl_file attributes can be compared + } + else + { + result = 0; + //printf(" action = IGNORE\n"); + } + break; + + default: + switch (a_attrs.FormAtIndex(i)) + { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + case DW_FORM_ref_addr: + //printf(" action = COMPARE DIEs 0x%8.8x 0x%8.8x\n", (dw_offset_t)a_form_value.Reference(a_cu), (dw_offset_t)b_form_value.Reference(b_cu)); + // These attribute values refer to other DIEs, so lets compare those instead of their DIE offsets... + result = Compare(dwarf2Data, a_form_value.Reference(a_cu), b_form_value.Reference(b_cu), compare_state, false, true); + if (result != 0) + return result; + break; + + default: + // We do care that they were different, return this result... + //printf(" action = RETURN RESULT\n"); + return result; + } + } + } + } + } + //printf(" SUCCESS\n\t\t0x%8.8x: %s\n\t\t0x%8.8x: %s\n", a_die->GetOffset(), DW_TAG_value_to_name(a_tag), b_die->GetOffset(), DW_TAG_value_to_name(b_tag)); + + if (compare_children) + { + bool a_has_children = a_die->HasChildren(); + bool b_has_children = b_die->HasChildren(); + if (a_has_children == b_has_children) + { + // Both either have kids or don't + if (a_has_children) + result = Compare( dwarf2Data, + a_cu, a_die->GetFirstChild(), + b_cu, b_die->GetFirstChild(), + compare_state, true, compare_children); + else + result = 0; + } + else if (!a_has_children) + result = -1; // A doesn't have kids, but B does + else + result = 1; // A has kids, but B doesn't + } + + if (compare_siblings) + { + result = Compare( dwarf2Data, + a_cu, a_die->GetSibling(), + b_cu, b_die->GetSibling(), + compare_state, true, compare_children); + } + + return result; + } + + if (a_die == NULL) + return -1; // a_die is NULL, yet b_die is non-NULL + else + return 1; // a_die is non-NULL, yet b_die is NULL + +} + +// +//int +//DWARFDebugInfoEntry::Compare +//( +// SymbolFileDWARF* dwarf2Data, +// const DWARFCompileUnit* cu_a, +// const DWARFDebugInfoEntry* die_a, +// const DWARFCompileUnit* cu_a, +// const DWARFDebugInfoEntry* die_b, +// CompareState &compare_state +//) +//{ +//} + +//---------------------------------------------------------------------- +// GetDIENamesAndRanges +// +// Gets the valid address ranges for a given DIE by looking for a +// DW_AT_low_pc/DW_AT_high_pc pair, DW_AT_entry_pc, or DW_AT_ranges +// attributes. +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::GetDIENamesAndRanges +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const char * &name, + const char * &mangled, + DWARFDebugRanges::RangeList& ranges, + int& decl_file, + int& decl_line, + int& decl_column, + int& call_file, + int& call_line, + int& call_column, + DWARFExpression *frame_base +) const +{ + if (dwarf2Data == NULL) + return false; + + dw_addr_t lo_pc = DW_INVALID_ADDRESS; + dw_addr_t hi_pc = DW_INVALID_ADDRESS; + std::vector<dw_offset_t> die_offsets; + if (m_abbrevDecl) + { + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); + uint32_t offset = m_offset; + + if (!debug_info_data.ValidOffset(offset)) + return false; + + // Skip the abbreviation code + debug_info_data.Skip_LEB128(&offset); + + const uint32_t numAttributes = m_abbrevDecl->NumAttributes(); + uint32_t i; + dw_attr_t attr; + dw_form_t form; + for (i=0; i<numAttributes; ++i) + { + m_abbrevDecl->GetAttrAndFormByIndexUnchecked(i, attr, form); + DWARFFormValue form_value(form); + if (form_value.ExtractValue(debug_info_data, &offset, cu)) + { + switch (attr) + { + case DW_AT_low_pc: + case DW_AT_entry_pc: + lo_pc = form_value.Unsigned(); + break; + + case DW_AT_high_pc: + hi_pc = form_value.Unsigned(); + break; + + case DW_AT_ranges: + { + const DWARFDebugRanges* debug_ranges = dwarf2Data->DebugRanges(); + debug_ranges->FindRanges(form_value.Unsigned(), ranges); + // All DW_AT_ranges are relative to the base address of the + // compile unit. We add the compile unit base address to make + // sure all the addresses are properly fixed up. + ranges.AddOffset(cu->GetBaseAddress()); + } + break; + + case DW_AT_name: + if (name == NULL) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + break; + + case DW_AT_MIPS_linkage_name: + if (mangled == NULL) + mangled = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + break; + + case DW_AT_abstract_origin: + die_offsets.push_back(form_value.Reference(cu)); + break; + + case DW_AT_specification: + die_offsets.push_back(form_value.Reference(cu)); + break; + + case DW_AT_decl_file: + if (decl_file == 0) + decl_file = form_value.Unsigned(); + break; + + case DW_AT_decl_line: + if (decl_line == 0) + decl_line = form_value.Unsigned(); + break; + + case DW_AT_decl_column: + if (decl_column == 0) + decl_column = form_value.Unsigned(); + break; + + case DW_AT_call_file: + if (call_file == 0) + call_file = form_value.Unsigned(); + break; + + case DW_AT_call_line: + if (call_line == 0) + call_line = form_value.Unsigned(); + break; + + case DW_AT_call_column: + if (call_column == 0) + call_column = form_value.Unsigned(); + break; + + case DW_AT_frame_base: + if (frame_base) + { + if (form_value.BlockData()) + { + uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); + uint32_t block_length = form_value.Unsigned(); + frame_base->SetOpcodeData(debug_info_data, block_offset, block_length, NULL); + } + else + { + const DataExtractor& debug_loc_data = dwarf2Data->get_debug_loc_data(); + const dw_offset_t debug_loc_offset = form_value.Unsigned(); + + size_t loc_list_length = DWARFLocationList::Size(debug_loc_data, debug_loc_offset); + if (loc_list_length > 0) + { + Address base_address(cu->GetBaseAddress(), dwarf2Data->GetObjectFile()->GetSectionList()); + frame_base->SetOpcodeData(debug_loc_data, debug_loc_offset, loc_list_length, &base_address); + } + } + } + break; + + default: + break; + } + } + } + } + + size_t numRanges = ranges.Size(); + + if (numRanges == 0) + { + if (lo_pc != DW_INVALID_ADDRESS) + { + if (hi_pc != DW_INVALID_ADDRESS) + ranges.AddRange(lo_pc, hi_pc); + else + ranges.AddRange(lo_pc, lo_pc); + } + } + + if (ranges.Size() == 0 || (name == NULL) || (mangled == NULL)) + { + std::vector<dw_offset_t>::const_iterator pos; + std::vector<dw_offset_t>::const_iterator end = die_offsets.end(); + for (pos = die_offsets.begin(); pos != end; ++pos) + { + DWARFCompileUnitSP cu_sp_ptr; + const DWARFDebugInfoEntry* die = NULL; + dw_offset_t die_offset = *pos; + if (die_offset != DW_INVALID_OFFSET) + { + die = dwarf2Data->DebugInfo()->GetDIEPtr(die_offset, &cu_sp_ptr); + if (die) + die->GetDIENamesAndRanges(dwarf2Data, cu_sp_ptr.get(), name, mangled, ranges, decl_file, decl_line, decl_column, call_file, call_line, call_column); + } + } + } + return ranges.Size() > 0; +} + +//---------------------------------------------------------------------- +// Dump +// +// Dumps a debug information entry and all of it's attributes to the +// specified stream. +//---------------------------------------------------------------------- +void +DWARFDebugInfoEntry::Dump +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + Stream *s, + uint32_t recurse_depth +) const +{ + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); + uint32_t offset = m_offset; + bool english = s->GetFlags().IsSet (DWARFDebugInfo::eDumpFlag_EnglishyNames); + + if (debug_info_data.ValidOffset(offset)) + { + dw_uleb128_t abbrCode = debug_info_data.GetULEB128(&offset); + + s->Printf("\n0x%8.8x: ", m_offset); + s->Indent(); + if (abbrCode) + { + if (m_abbrevDecl) + { + if (english) + s->PutCString(DW_TAG_value_to_englishy_name(m_abbrevDecl->Tag())); + else + s->PutCString(DW_TAG_value_to_name(m_abbrevDecl->Tag())); + s->Printf( " [%u] %c\n", abbrCode, m_abbrevDecl->HasChildren() ? '*':' '); + + // Dump all data in the .debug_info for the attributes + const uint32_t numAttributes = m_abbrevDecl->NumAttributes(); + uint32_t i; + dw_attr_t attr; + dw_form_t form; + for (i=0; i<numAttributes; ++i) + { + m_abbrevDecl->GetAttrAndFormByIndexUnchecked(i, attr, form); + + DumpAttribute(dwarf2Data, cu, debug_info_data, &offset, s, attr, form); + } + + const DWARFDebugInfoEntry* child = GetFirstChild(); + if (recurse_depth > 0 && child) + { + s->IndentMore(); + + while (child) + { + child->Dump(dwarf2Data, cu, s, recurse_depth-1); + child = child->GetSibling(); + } + s->IndentLess(); + } + } + else + s->Printf( "Abbreviation code note found in 'debug_abbrev' class for code: %u\n", abbrCode); + } + else + { + s->Printf( "NULL\n"); + } + } +} + +//---------------------------------------------------------------------- +// DumpAttribute +// +// Dumps a debug information entry attribute along with it's form. Any +// special display of attributes is done (disassemble location lists, +// show enumeration values for attributes, etc). +//---------------------------------------------------------------------- +void +DWARFDebugInfoEntry::DumpAttribute +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const DataExtractor& debug_info_data, + uint32_t* offset_ptr, + Stream *s, + dw_attr_t attr, + dw_form_t form +) +{ + bool verbose = s->GetVerbose(); + bool show_form = s->GetFlags().IsSet(DWARFDebugInfo::eDumpFlag_ShowForm); + bool english = s->GetFlags().IsSet(DWARFDebugInfo::eDumpFlag_EnglishyNames); + const DataExtractor* debug_str_data = dwarf2Data ? &dwarf2Data->get_debug_str_data() : NULL; + if (verbose) + s->Offset(*offset_ptr); + else + s->Printf( " "); + s->Indent(); + + if (english) + s->PutCString(DW_AT_value_to_englishy_name(attr)); + else + s->PutCString(DW_AT_value_to_name(attr)); + + if (show_form) + { + s->Printf( "[%s", english ? DW_FORM_value_to_englishy_name(form) : DW_FORM_value_to_name(form)); + } + + DWARFFormValue form_value(form); + + if (!form_value.ExtractValue(debug_info_data, offset_ptr, cu)) + return; + + if (show_form) + { + if (form == DW_FORM_indirect) + { + s->Printf( " [%s]", english ? DW_FORM_value_to_englishy_name(form_value.Form()) : DW_FORM_value_to_name(form_value.Form())); + } + + s->PutCString("] "); + } + + s->PutCString("( "); + + // Always dump form value if verbose is enabled + if (verbose) + { + form_value.Dump(s, debug_str_data, cu); + } + + + // Check to see if we have any special attribute formatters + switch (attr) + { + case DW_AT_stmt_list: + if ( verbose ) s->PutCString(" ( "); + s->Printf( "0x%8.8x", form_value.Unsigned()); + if ( verbose ) s->PutCString(" )"); + break; + + case DW_AT_language: + if ( verbose ) s->PutCString(" ( "); + s->PutCString(DW_LANG_value_to_name(form_value.Unsigned())); + if ( verbose ) s->PutCString(" )"); + break; + + case DW_AT_encoding: + if ( verbose ) s->PutCString(" ( "); + s->PutCString(DW_ATE_value_to_name(form_value.Unsigned())); + if ( verbose ) s->PutCString(" )"); + break; + + case DW_AT_frame_base: + case DW_AT_location: + case DW_AT_data_member_location: + { + const uint8_t* blockData = form_value.BlockData(); + if (blockData) + { + if (!verbose) + form_value.Dump(s, debug_str_data, cu); + + // Location description is inlined in data in the form value + DataExtractor locationData(debug_info_data, (*offset_ptr) - form_value.Unsigned(), form_value.Unsigned()); + if ( verbose ) s->PutCString(" ( "); + print_dwarf_expression (s, locationData, DWARFCompileUnit::GetAddressByteSize(cu), 4, false); + if ( verbose ) s->PutCString(" )"); + } + else + { + // We have a location list offset as the value that is + // the offset into the .debug_loc section that describes + // the value over it's lifetime + uint64_t debug_loc_offset = form_value.Unsigned(); + if (dwarf2Data) + { + if ( !verbose ) + form_value.Dump(s, debug_str_data, cu); + DWARFLocationList::Dump(s, cu, dwarf2Data->get_debug_loc_data(), debug_loc_offset); + } + else + { + if ( !verbose ) + form_value.Dump(s, NULL, cu); + } + } + } + break; + + case DW_AT_abstract_origin: + case DW_AT_specification: + { + uint64_t abstract_die_offset = form_value.Reference(cu); + form_value.Dump(s, debug_str_data, cu); + // *ostrm_ptr << HEX32 << abstract_die_offset << " ( "; + if ( verbose ) s->PutCString(" ( "); + GetName(dwarf2Data, cu, abstract_die_offset, s); + if ( verbose ) s->PutCString(" )"); + } + break; + + case DW_AT_type: + { + uint64_t type_die_offset = form_value.Reference(cu); + if (!verbose) + form_value.Dump(s, debug_str_data, cu); + s->PutCString(" ( "); + AppendTypeName(dwarf2Data, cu, type_die_offset, s); + s->PutCString(" )"); + } + break; + + case DW_AT_ranges: + { + if ( !verbose ) + form_value.Dump(s, debug_str_data, cu); + uint32_t ranges_offset = form_value.Unsigned(); + dw_addr_t base_addr = cu ? cu->GetBaseAddress() : 0; + DWARFDebugRanges::Dump(s, dwarf2Data->get_debug_ranges_data(), &ranges_offset, base_addr); + } + break; + + default: + if ( !verbose ) + form_value.Dump(s, debug_str_data, cu); + break; + } + + s->PutCString(" )\n"); +} + +//---------------------------------------------------------------------- +// Get all attribute values for a given DIE, including following any +// specification or abstract origin attributes and including those in +// the results. Any duplicate attributes will have the first instance +// take precedence (this can happen for declaration attributes). +//---------------------------------------------------------------------- +size_t +DWARFDebugInfoEntry::GetAttributes +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugInfoEntry::Attributes& attributes +) const +{ + if (m_abbrevDecl) + { + uint32_t offset = GetOffset(); + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); + + // Skip the abbreviation code so we are at the data for the attributes + debug_info_data.Skip_LEB128(&offset); + + const uint32_t num_attributes = m_abbrevDecl->NumAttributes(); + uint32_t i; + dw_attr_t attr; + dw_form_t form; + DWARFFormValue form_value; + for (i=0; i<num_attributes; ++i) + { + m_abbrevDecl->GetAttrAndFormByIndexUnchecked (i, attr, form); + attributes.Append(cu, offset, attr, form); + if ((attr == DW_AT_specification) || (attr == DW_AT_abstract_origin)) + { + form_value.SetForm(form); + if (form_value.ExtractValue(debug_info_data, &offset, cu)) + { + const DWARFDebugInfoEntry* die = NULL; + dw_offset_t die_offset = form_value.Reference(cu); + if (cu->ContainsDIEOffset(die_offset)) + { + die = const_cast<DWARFCompileUnit*>(cu)->GetDIEPtr(die_offset); + if (die) + die->GetAttributes(dwarf2Data, cu, attributes); + } + else + { + DWARFCompileUnitSP cu_sp_ptr; + die = const_cast<SymbolFileDWARF*>(dwarf2Data)->DebugInfo()->GetDIEPtr(die_offset, &cu_sp_ptr); + if (die) + die->GetAttributes(dwarf2Data, cu_sp_ptr.get(), attributes); + } + } + } + else + { + assert(DWARFFormValue::SkipValue(form, debug_info_data, &offset, cu)); + } + } + } + else + { + attributes.Clear(); + } + return attributes.Size(); + +} + +//---------------------------------------------------------------------- +// GetAttributeValue +// +// Get the value of an attribute and return the .debug_info offset of the +// attribute if it was properly extracted into form_value, or zero +// if we fail since an offset of zero is invalid for an attribute (it +// would be a compile unit header). +//---------------------------------------------------------------------- +dw_offset_t +DWARFDebugInfoEntry::GetAttributeValue +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + DWARFFormValue& form_value, + dw_offset_t* end_attr_offset_ptr +) const +{ + if (m_abbrevDecl) + { + uint32_t attr_idx = m_abbrevDecl->FindAttributeIndex(attr); + + if (attr_idx != DW_INVALID_INDEX) + { + uint32_t offset = GetOffset(); + + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); + + // Skip the abbreviation code so we are at the data for the attributes + debug_info_data.Skip_LEB128(&offset); + + uint32_t idx=0; + while (idx<attr_idx) + DWARFFormValue::SkipValue(m_abbrevDecl->GetFormByIndex(idx++), debug_info_data, &offset, cu); + + const dw_offset_t attr_offset = offset; + form_value.SetForm(m_abbrevDecl->GetFormByIndex(idx)); + if (form_value.ExtractValue(debug_info_data, &offset, cu)) + { + if (end_attr_offset_ptr) + *end_attr_offset_ptr = offset; + return attr_offset; + } + } + } + + return 0; +} + +//---------------------------------------------------------------------- +// GetAttributeValueAsString +// +// Get the value of an attribute as a string return it. The resulting +// pointer to the string data exists within the supplied SymbolFileDWARF +// and will only be available as long as the SymbolFileDWARF is still around +// and it's content doesn't change. +//---------------------------------------------------------------------- +const char* +DWARFDebugInfoEntry::GetAttributeValueAsString +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + const char* fail_value) const +{ + DWARFFormValue form_value; + if (GetAttributeValue(dwarf2Data, cu, attr, form_value)) + return form_value.AsCString(&dwarf2Data->get_debug_str_data()); + return fail_value; +} + +//---------------------------------------------------------------------- +// GetAttributeValueAsUnsigned +// +// Get the value of an attribute as unsigned and return it. +//---------------------------------------------------------------------- +uint64_t +DWARFDebugInfoEntry::GetAttributeValueAsUnsigned +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + uint64_t fail_value +) const +{ + DWARFFormValue form_value; + if (GetAttributeValue(dwarf2Data, cu, attr, form_value)) + return form_value.Unsigned(); + return fail_value; +} + +//---------------------------------------------------------------------- +// GetAttributeValueAsSigned +// +// Get the value of an attribute a signed value and return it. +//---------------------------------------------------------------------- +int64_t +DWARFDebugInfoEntry::GetAttributeValueAsSigned +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + int64_t fail_value +) const +{ + DWARFFormValue form_value; + if (GetAttributeValue(dwarf2Data, cu, attr, form_value)) + return form_value.Signed(); + return fail_value; +} + +//---------------------------------------------------------------------- +// GetAttributeValueAsReference +// +// Get the value of an attribute as reference and fix up and compile +// unit relative offsets as needed. +//---------------------------------------------------------------------- +uint64_t +DWARFDebugInfoEntry::GetAttributeValueAsReference +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + uint64_t fail_value +) const +{ + DWARFFormValue form_value; + if (GetAttributeValue(dwarf2Data, cu, attr, form_value)) + return form_value.Reference(cu); + return fail_value; +} + +//---------------------------------------------------------------------- +// GetAttributeValueAsLocation +// +// Get the value of an attribute as reference and fix up and compile +// unit relative offsets as needed. +//---------------------------------------------------------------------- +dw_offset_t +DWARFDebugInfoEntry::GetAttributeValueAsLocation +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + DataExtractor& location_data, + uint32_t &block_size +) const +{ + block_size = 0; + DWARFFormValue form_value; + + // Empty out data in case we don't find anything + location_data.Clear(); + dw_offset_t end_addr_offset = DW_INVALID_OFFSET; + const dw_offset_t attr_offset = GetAttributeValue(dwarf2Data, cu, attr, form_value, &end_addr_offset); + if (attr_offset) + { + const uint8_t* blockData = form_value.BlockData(); + if (blockData) + { + // We have an inlined location list in the .debug_info section + const DataExtractor& debug_info = dwarf2Data->get_debug_info_data(); + dw_offset_t block_offset = blockData - debug_info.GetDataStart(); + block_size = (end_addr_offset - attr_offset) - form_value.Unsigned(); + location_data.SetData(debug_info, block_offset, block_size); + } + else + { + // We have a location list offset as the value that is + // the offset into the .debug_loc section that describes + // the value over it's lifetime + dw_offset_t debug_loc_offset = form_value.Unsigned(); + if (dwarf2Data) + { + assert(dwarf2Data->get_debug_loc_data().GetAddressByteSize() == cu->GetAddressByteSize()); + return DWARFLocationList::Extract(dwarf2Data->get_debug_loc_data(), &debug_loc_offset, location_data); + } + } + } + return attr_offset; +} + +//---------------------------------------------------------------------- +// GetName +// +// Get value of the DW_AT_name attribute and return it if one exists, +// else return NULL. +//---------------------------------------------------------------------- +const char* +DWARFDebugInfoEntry::GetName +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu +) const +{ + DWARFFormValue form_value; + if (GetAttributeValue(dwarf2Data, cu, DW_AT_name, form_value)) + return form_value.AsCString(&dwarf2Data->get_debug_str_data()); + return NULL; +} + + +//---------------------------------------------------------------------- +// GetMangledName +// +// Get value of the DW_AT_MIPS_linkage_name attribute and return it if +// one exists, else return the value of the DW_AT_name attribute +//---------------------------------------------------------------------- +const char* +DWARFDebugInfoEntry::GetMangledName +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + bool substitute_name_allowed +) const +{ + const char* name = NULL; + DWARFFormValue form_value; + + if (GetAttributeValue(dwarf2Data, cu, DW_AT_MIPS_linkage_name, form_value)) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + + if (substitute_name_allowed && name == NULL) + { + if (GetAttributeValue(dwarf2Data, cu, DW_AT_name, form_value)) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + } + return name; +} + + +//---------------------------------------------------------------------- +// GetPubname +// +// Get value the name for a DIE as it should appear for a +// .debug_pubnames or .debug_pubtypes section. +//---------------------------------------------------------------------- +const char* +DWARFDebugInfoEntry::GetPubname +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu +) const +{ + const char* name = NULL; + DWARFFormValue form_value; + + if (GetAttributeValue(dwarf2Data, cu, DW_AT_MIPS_linkage_name, form_value)) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + else if (GetAttributeValue(dwarf2Data, cu, DW_AT_name, form_value)) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + else if (GetAttributeValue(dwarf2Data, cu, DW_AT_specification, form_value)) + { + // The specification DIE may be in another compile unit so we need + // to get a die and its compile unit. + DWARFCompileUnitSP cu_sp_ptr; + const DWARFDebugInfoEntry* die = const_cast<SymbolFileDWARF*>(dwarf2Data)->DebugInfo()->GetDIEPtr(form_value.Reference(cu), &cu_sp_ptr); + if (die) + return die->GetPubname(dwarf2Data, cu_sp_ptr.get()); + } + return name; +} + + +//---------------------------------------------------------------------- +// GetName +// +// Get value of the DW_AT_name attribute for a debug information entry +// that exists at offset "die_offset" and place that value into the +// supplied stream object. If the DIE is a NULL object "NULL" is placed +// into the stream, and if no DW_AT_name attribute exists for the DIE +// then nothing is printed. +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::GetName +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const uint32_t die_offset, + Stream *s +) +{ + DWARFDebugInfoEntry die; + uint32_t offset = die_offset; + if (die.Extract(dwarf2Data, cu, &offset)) + { + if (die.IsNULL()) + { + s->PutCString("NULL"); + return true; + } + else + { + DWARFFormValue form_value; + if (die.GetAttributeValue(dwarf2Data, cu, DW_AT_name, form_value)) + { + const char* name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + if (name) + { + s->PutCString(name); + return true; + } + } + } + } + return false; +} + +//---------------------------------------------------------------------- +// AppendTypeName +// +// Follows the type name definition down through all needed tags to +// end up with a fully qualified type name and dump the results to +// the supplied stream. This is used to show the name of types given +// a type identifier. +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::AppendTypeName +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const uint32_t die_offset, + Stream *s +) +{ + DWARFDebugInfoEntry die; + uint32_t offset = die_offset; + if (die.Extract(dwarf2Data, cu, &offset)) + { + if (die.IsNULL()) + { + s->PutCString("NULL"); + return true; + } + else + { + const char* name = die.GetPubname(dwarf2Data, cu); + // if (die.GetAttributeValue(dwarf2Data, cu, DW_AT_name, form_value)) + // name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + if (name) + s->PutCString(name); + else + { + bool result = true; + const DWARFAbbreviationDeclaration* abbrevDecl = die.GetAbbreviationDeclarationPtr(); + + switch (abbrevDecl->Tag()) + { + case DW_TAG_array_type: break; // print out a "[]" after printing the full type of the element below + case DW_TAG_base_type: s->PutCString("base "); break; + case DW_TAG_class_type: s->PutCString("class "); break; + case DW_TAG_const_type: s->PutCString("const "); break; + case DW_TAG_enumeration_type: s->PutCString("enum "); break; + case DW_TAG_file_type: s->PutCString("file "); break; + case DW_TAG_interface_type: s->PutCString("interface "); break; + case DW_TAG_packed_type: s->PutCString("packed "); break; + case DW_TAG_pointer_type: break; // print out a '*' after printing the full type below + case DW_TAG_ptr_to_member_type: break; // print out a '*' after printing the full type below + case DW_TAG_reference_type: break; // print out a '&' after printing the full type below + case DW_TAG_restrict_type: s->PutCString("restrict "); break; + case DW_TAG_set_type: s->PutCString("set "); break; + case DW_TAG_shared_type: s->PutCString("shared "); break; + case DW_TAG_string_type: s->PutCString("string "); break; + case DW_TAG_structure_type: s->PutCString("struct "); break; + case DW_TAG_subrange_type: s->PutCString("subrange "); break; + case DW_TAG_subroutine_type: s->PutCString("function "); break; + case DW_TAG_thrown_type: s->PutCString("thrown "); break; + case DW_TAG_union_type: s->PutCString("union "); break; + case DW_TAG_unspecified_type: s->PutCString("unspecified "); break; + case DW_TAG_volatile_type: s->PutCString("volatile "); break; + default: + return false; + } + + // Follow the DW_AT_type if possible + DWARFFormValue form_value; + if (die.GetAttributeValue(dwarf2Data, cu, DW_AT_type, form_value)) + { + uint64_t next_die_offset = form_value.Reference(cu); + result = AppendTypeName(dwarf2Data, cu, next_die_offset, s); + } + + switch (abbrevDecl->Tag()) + { + case DW_TAG_array_type: s->PutCString("[]"); break; + case DW_TAG_pointer_type: s->PutChar('*'); break; + case DW_TAG_ptr_to_member_type: s->PutChar('*'); break; + case DW_TAG_reference_type: s->PutChar('&'); break; + default: + break; + } + return result; + } + } + } + return false; +} + +//---------------------------------------------------------------------- +// BuildAddressRangeTable +//---------------------------------------------------------------------- +void +DWARFDebugInfoEntry::BuildAddressRangeTable +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugAranges* debug_aranges +) const +{ + if (m_abbrevDecl) + { + dw_tag_t tag = m_abbrevDecl->Tag(); + if (tag == DW_TAG_subprogram) + { + dw_addr_t hi_pc = DW_INVALID_ADDRESS; + dw_addr_t lo_pc = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, DW_INVALID_ADDRESS); + if (lo_pc != DW_INVALID_ADDRESS) + hi_pc = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_high_pc, DW_INVALID_ADDRESS); + if (hi_pc != DW_INVALID_ADDRESS) + { + /// printf("BuildAddressRangeTable() 0x%8.8x: %30s: [0x%8.8x - 0x%8.8x)\n", m_offset, DW_TAG_value_to_name(tag), lo_pc, hi_pc); + debug_aranges->InsertRange(cu->GetOffset(), lo_pc, hi_pc); + } + } + + + const DWARFDebugInfoEntry* child = GetFirstChild(); + while (child) + { + child->BuildAddressRangeTable(dwarf2Data, cu, debug_aranges); + child = child->GetSibling(); + } + } +} + +//---------------------------------------------------------------------- +// BuildFunctionAddressRangeTable +// +// This function is very similar to the BuildAddressRangeTable function +// except that the actual DIE offset for the function is placed in the +// table instead of the compile unit offset (which is the way the +// standard .debug_aranges section does it). +//---------------------------------------------------------------------- +void +DWARFDebugInfoEntry::BuildFunctionAddressRangeTable +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugAranges* debug_aranges +) const +{ + if (m_abbrevDecl) + { + dw_tag_t tag = m_abbrevDecl->Tag(); + if (tag == DW_TAG_subprogram) + { + dw_addr_t hi_pc = DW_INVALID_ADDRESS; + dw_addr_t lo_pc = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, DW_INVALID_ADDRESS); + if (lo_pc != DW_INVALID_ADDRESS) + hi_pc = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_high_pc, DW_INVALID_ADDRESS); + if (hi_pc != DW_INVALID_ADDRESS) + { + // printf("BuildAddressRangeTable() 0x%8.8x: [0x%16.16llx - 0x%16.16llx)\n", m_offset, lo_pc, hi_pc); // DEBUG ONLY + debug_aranges->InsertRange(GetOffset(), lo_pc, hi_pc); + } + } + + const DWARFDebugInfoEntry* child = GetFirstChild(); + while (child) + { + child->BuildFunctionAddressRangeTable(dwarf2Data, cu, debug_aranges); + child = child->GetSibling(); + } + } +} + + +//---------------------------------------------------------------------- +// LookupAddress +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::LookupAddress +( + const dw_addr_t address, + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugInfoEntry** function_die, + DWARFDebugInfoEntry** block_die +) +{ + bool found_address = false; + if (m_abbrevDecl) + { + bool check_children = false; + bool match_addr_range = false; + dw_tag_t tag = m_abbrevDecl->Tag(); + // printf("0x%8.8x: %30s: address = 0x%8.8x - ", m_offset, DW_TAG_value_to_name(tag), address); + switch (tag) + { + case DW_TAG_array_type : break; + case DW_TAG_class_type : check_children = true; break; + case DW_TAG_entry_point : break; + case DW_TAG_enumeration_type : break; + case DW_TAG_formal_parameter : break; + case DW_TAG_imported_declaration : break; + case DW_TAG_label : break; + case DW_TAG_lexical_block : check_children = true; match_addr_range = true; break; + case DW_TAG_member : break; + case DW_TAG_pointer_type : break; + case DW_TAG_reference_type : break; + case DW_TAG_compile_unit : match_addr_range = true; break; + case DW_TAG_string_type : break; + case DW_TAG_structure_type : check_children = true; break; + case DW_TAG_subroutine_type : break; + case DW_TAG_typedef : break; + case DW_TAG_union_type : break; + case DW_TAG_unspecified_parameters : break; + case DW_TAG_variant : break; + case DW_TAG_common_block : check_children = true; break; + case DW_TAG_common_inclusion : break; + case DW_TAG_inheritance : break; + case DW_TAG_inlined_subroutine : check_children = true; match_addr_range = true; break; + case DW_TAG_module : match_addr_range = true; break; + case DW_TAG_ptr_to_member_type : break; + case DW_TAG_set_type : break; + case DW_TAG_subrange_type : break; + case DW_TAG_with_stmt : break; + case DW_TAG_access_declaration : break; + case DW_TAG_base_type : break; + case DW_TAG_catch_block : match_addr_range = true; break; + case DW_TAG_const_type : break; + case DW_TAG_constant : break; + case DW_TAG_enumerator : break; + case DW_TAG_file_type : break; + case DW_TAG_friend : break; + case DW_TAG_namelist : break; + case DW_TAG_namelist_item : break; + case DW_TAG_packed_type : break; + case DW_TAG_subprogram : match_addr_range = true; break; + case DW_TAG_template_type_parameter : break; + case DW_TAG_template_value_parameter : break; + case DW_TAG_thrown_type : break; + case DW_TAG_try_block : match_addr_range = true; break; + case DW_TAG_variant_part : break; + case DW_TAG_variable : break; + case DW_TAG_volatile_type : break; + case DW_TAG_dwarf_procedure : break; + case DW_TAG_restrict_type : break; + case DW_TAG_interface_type : break; + case DW_TAG_namespace : check_children = true; break; + case DW_TAG_imported_module : break; + case DW_TAG_unspecified_type : break; + case DW_TAG_partial_unit : break; + case DW_TAG_imported_unit : break; + case DW_TAG_shared_type : break; + default: break; + } + + if (match_addr_range) + { + dw_addr_t lo_pc = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, DW_INVALID_ADDRESS); + if (lo_pc != DW_INVALID_ADDRESS) + { + dw_addr_t hi_pc = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_high_pc, DW_INVALID_ADDRESS); + if (hi_pc != DW_INVALID_ADDRESS) + { + // printf("\n0x%8.8x: %30s: address = 0x%8.8x [0x%8.8x - 0x%8.8x) ", m_offset, DW_TAG_value_to_name(tag), address, lo_pc, hi_pc); + if ((lo_pc <= address) && (address < hi_pc)) + { + found_address = true; + // puts("***MATCH***"); + switch (tag) + { + case DW_TAG_compile_unit: // File + check_children = ((function_die != NULL) || (block_die != NULL)); + break; + + case DW_TAG_subprogram: // Function + if (function_die) + *function_die = this; + check_children = (block_die != NULL); + break; + + case DW_TAG_inlined_subroutine: // Inlined Function + case DW_TAG_lexical_block: // Block { } in code + if (block_die) + { + *block_die = this; + check_children = true; + } + break; + + default: + check_children = true; + break; + } + } + } + else + { // compile units may not have a valid high/low pc when there + // are address gaps in subtroutines so we must always search + // if there is no valid high and low PC + check_children = (tag == DW_TAG_compile_unit) && ((function_die != NULL) || (block_die != NULL)); + } + } + else + { + dw_offset_t debug_ranges_offset = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_ranges, DW_INVALID_OFFSET); + if (debug_ranges_offset != DW_INVALID_OFFSET) + { + DWARFDebugRanges::RangeList ranges; + DWARFDebugRanges* debug_ranges = dwarf2Data->DebugRanges(); + debug_ranges->FindRanges(debug_ranges_offset, ranges); + // All DW_AT_ranges are relative to the base address of the + // compile unit. We add the compile unit base address to make + // sure all the addresses are properly fixed up. + ranges.AddOffset(cu->GetBaseAddress()); + if (ranges.Lookup(address)) + { + found_address = true; + // puts("***MATCH***"); + switch (tag) + { + case DW_TAG_compile_unit: // File + check_children = ((function_die != NULL) || (block_die != NULL)); + break; + + case DW_TAG_subprogram: // Function + if (function_die) + *function_die = this; + check_children = (block_die != NULL); + break; + + case DW_TAG_inlined_subroutine: // Inlined Function + case DW_TAG_lexical_block: // Block { } in code + if (block_die) + { + *block_die = this; + check_children = true; + } + break; + + default: + check_children = true; + break; + } + } + else + { + check_children = false; + } + } + } + } + + + if (check_children) + { + // printf("checking children\n"); + DWARFDebugInfoEntry* child = GetFirstChild(); + while (child) + { + if (child->LookupAddress(address, dwarf2Data, cu, function_die, block_die)) + return true; + child = child->GetSibling(); + } + } + } + return found_address; +} + + +bool +DWARFDebugInfoEntry::OffsetLessThan (const DWARFDebugInfoEntry& a, const DWARFDebugInfoEntry& b) +{ + return a.GetOffset() < b.GetOffset(); +} + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h new file mode 100644 index 00000000000..8340acc427e --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h @@ -0,0 +1,320 @@ +//===-- DWARFDebugInfoEntry.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_DWARFDebugInfoEntry_h_ +#define liblldb_DWARFDebugInfoEntry_h_ + +#include "SymbolFileDWARF.h" +#include "DWARFAbbreviationDeclaration.h" +#include "DWARFDebugRanges.h" +#include <vector> +#include <map> +#include <set> + +typedef std::map<const DWARFDebugInfoEntry*, dw_addr_t> DIEToAddressMap; +typedef DIEToAddressMap::iterator DIEToAddressMapIter; +typedef DIEToAddressMap::const_iterator DIEToAddressMapConstIter; + +typedef std::map<dw_addr_t, const DWARFDebugInfoEntry*> AddressToDIEMap; +typedef AddressToDIEMap::iterator AddressToDIEMapIter; +typedef AddressToDIEMap::const_iterator AddressToDIEMapConstIter; + + +typedef std::map<dw_offset_t, dw_offset_t> DIEToDIEMap; +typedef DIEToDIEMap::iterator DIEToDIEMapIter; +typedef DIEToDIEMap::const_iterator DIEToDIEMapConstIter; + +typedef std::map<uint32_t, const DWARFDebugInfoEntry*> UInt32ToDIEMap; +typedef UInt32ToDIEMap::iterator UInt32ToDIEMapIter; +typedef UInt32ToDIEMap::const_iterator UInt32ToDIEMapConstIter; + +typedef std::multimap<uint32_t, const DWARFDebugInfoEntry*> UInt32ToDIEMMap; +typedef UInt32ToDIEMMap::iterator UInt32ToDIEMMapIter; +typedef UInt32ToDIEMMap::const_iterator UInt32ToDIEMMapConstIter; + +class DWARFDebugInfoEntry +{ +public: + typedef std::vector<DWARFDebugInfoEntry> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + typedef std::vector<dw_offset_t> offset_collection; + typedef offset_collection::iterator offset_collection_iterator; + typedef offset_collection::const_iterator offset_collection_const_iterator; + + class Attributes + { + public: + Attributes(); + ~Attributes(); + + void Append(const DWARFCompileUnit *cu, dw_offset_t attr_die_offset, dw_attr_t attr, dw_form_t form); + const DWARFCompileUnit * CompileUnitAtIndex(uint32_t i) const { return m_infos[i].cu; } + dw_offset_t DIEOffsetAtIndex(uint32_t i) const { return m_infos[i].die_offset; } + dw_attr_t AttributeAtIndex(uint32_t i) const { return m_infos[i].attr; } + dw_attr_t FormAtIndex(uint32_t i) const { return m_infos[i].form; } + bool ExtractFormValueAtIndex (SymbolFileDWARF* dwarf2Data, uint32_t i, DWARFFormValue &form_value) const; + uint64_t FormValueAsUnsignedAtIndex (SymbolFileDWARF* dwarf2Data, uint32_t i, uint64_t fail_value) const; + uint32_t FindAttributeIndex(dw_attr_t attr) const; + bool ContainsAttribute(dw_attr_t attr) const; + bool RemoveAttribute(dw_attr_t attr); + void Clear() { m_infos.clear(); } + uint32_t Size() const { return m_infos.size(); } + + protected: + struct Info + { + const DWARFCompileUnit *cu; // Keep the compile unit with each attribute in case we have DW_FORM_ref_addr values + dw_offset_t die_offset; + dw_attr_t attr; + dw_form_t form; + }; + std::vector<Info> m_infos; + }; + + struct CompareState + { + CompareState() : + die_offset_pairs() + { + assert(sizeof(dw_offset_t)*2 == sizeof(uint64_t)); + } + + bool AddTypePair(dw_offset_t a, dw_offset_t b) + { + uint64_t a_b_offsets = (uint64_t)a << 32 | (uint64_t)b; + // Return true if this type was inserted, false otherwise + return die_offset_pairs.insert(a_b_offsets).second; + } + std::set< uint64_t > die_offset_pairs; + }; + + DWARFDebugInfoEntry(): + m_offset (DW_INVALID_OFFSET), + m_parent_idx (0), + m_sibling_idx (0), + m_abbrevDecl (NULL), + m_user_data (NULL) + { + } + + + void BuildAddressRangeTable( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugAranges* debug_aranges) const; + + void BuildFunctionAddressRangeTable( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugAranges* debug_aranges) const; + + bool Extract( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + dw_offset_t* offset_ptr); + + bool LookupAddress( + const dw_addr_t address, + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugInfoEntry** function_die, + DWARFDebugInfoEntry** block_die); + + size_t GetAttributes( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugInfoEntry::Attributes& attrs) const; + + dw_offset_t GetAttributeValue( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + DWARFFormValue& formValue, + dw_offset_t* end_attr_offset_ptr = NULL) const; + + const char* GetAttributeValueAsString( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + const char* fail_value) const; + + uint64_t GetAttributeValueAsUnsigned( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + uint64_t fail_value) const; + + uint64_t GetAttributeValueAsReference( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + uint64_t fail_value) const; + + int64_t GetAttributeValueAsSigned( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + int64_t fail_value) const; + + dw_offset_t GetAttributeValueAsLocation( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + lldb_private::DataExtractor& data, + uint32_t &block_size) const; + + const char* GetName( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu) const; + + const char* GetMangledName( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + bool substitute_name_allowed = true) const; + + const char* GetPubname( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu) const; + + static bool GetName( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_offset_t die_offset, + lldb_private::Stream *s); + + static bool AppendTypeName( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_offset_t die_offset, + lldb_private::Stream *s); + + static int Compare( + SymbolFileDWARF* dwarf2Data, + dw_offset_t a_die_offset, + dw_offset_t b_die_offset, + CompareState &compare_state, + bool compare_siblings, + bool compare_children); + + static int Compare( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* a_cu, const DWARFDebugInfoEntry* a_die, + DWARFCompileUnit* b_cu, const DWARFDebugInfoEntry* b_die, + CompareState &compare_state, + bool compare_siblings, + bool compare_children); + + static bool OffsetLessThan ( + const DWARFDebugInfoEntry& a, + const DWARFDebugInfoEntry& b); + + bool AppendDependentDIES( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const bool add_children, + DWARFDIECollection& die_offsets) const; + + void Dump( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + lldb_private::Stream *s, + uint32_t recurse_depth) const; + + void DumpAncestry( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const DWARFDebugInfoEntry* oldest, + lldb_private::Stream *s, + uint32_t recurse_depth) const; + + static void DumpAttribute( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const lldb_private::DataExtractor& debug_info_data, + uint32_t* offset_ptr, + lldb_private::Stream *s, + dw_attr_t attr, + dw_form_t form); + + bool GetDIENamesAndRanges( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const char * &name, + const char * &mangled, + DWARFDebugRanges::RangeList& rangeList, + int& decl_file, + int& decl_line, + int& decl_column, + int& call_file, + int& call_line, + int& call_column, + lldb_private::DWARFExpression *frame_base = NULL) const; + + + dw_tag_t Tag() const { return m_abbrevDecl ? m_abbrevDecl->Tag() : 0; } + bool IsNULL() const { return m_abbrevDecl == NULL; } + dw_offset_t GetOffset() const { return m_offset; } + void SetOffset(dw_offset_t offset) { m_offset = offset; } + uint32_t NumAttributes() const { return m_abbrevDecl ? m_abbrevDecl->NumAttributes() : 0; } + bool HasChildren() const { return m_abbrevDecl != NULL && m_abbrevDecl->HasChildren(); } + + // We know we are kept in a vector of contiguous entries, so we know + // our parent will be some index behind "this". + DWARFDebugInfoEntry* GetParent() { return m_parent_idx > 0 ? this - m_parent_idx : NULL; } + const DWARFDebugInfoEntry* GetParent() const { return m_parent_idx > 0 ? this - m_parent_idx : NULL; } + // We know we are kept in a vector of contiguous entries, so we know + // our sibling will be some index after "this". + DWARFDebugInfoEntry* GetSibling() { return m_sibling_idx > 0 ? this + m_sibling_idx : NULL; } + const DWARFDebugInfoEntry* GetSibling() const { return m_sibling_idx > 0 ? this + m_sibling_idx : NULL; } + // We know we are kept in a vector of contiguous entries, so we know + // we don't need to store our child pointer, if we have a child it will + // be the next entry in the list... + DWARFDebugInfoEntry* GetFirstChild() { return HasChildren() ? this + 1 : NULL; } + const DWARFDebugInfoEntry* GetFirstChild() const { return HasChildren() ? this + 1 : NULL; } + + void + SetParent (DWARFDebugInfoEntry* parent) + { + if (parent) + { + // We know we are kept in a vector of contiguous entries, so we know + // our parent will be some index behind "this". + m_parent_idx = this - parent; + } + else + m_parent_idx = 0; + } + void + SetSibling (DWARFDebugInfoEntry* sibling) + { + if (sibling) + { + // We know we are kept in a vector of contiguous entries, so we know + // our sibling will be some index after "this". + m_sibling_idx = sibling - this; + sibling->SetParent(GetParent()); + } + else + m_sibling_idx = 0; + } + const DWARFAbbreviationDeclaration* GetAbbreviationDeclarationPtr() const { return m_abbrevDecl; } + + void * GetUserData() const { return m_user_data; } + void SetUserData(void *d) const { m_user_data = d; } +protected: + dw_offset_t m_offset; // Offset within the .debug_info of the start of this entry + uint32_t m_parent_idx; // How many to subtract from "this" to get the parent. If zero this die has no parent + uint32_t m_sibling_idx; // How many to add to "this" to get the sibling. + const DWARFAbbreviationDeclaration* m_abbrevDecl; + mutable void * m_user_data; // Flags for use by the parsers +}; + +#endif // liblldb_DWARFDebugInfoEntry_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp new file mode 100644 index 00000000000..2b3f39b24f3 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp @@ -0,0 +1,1410 @@ +//===-- DWARFDebugLine.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugLine.h" + +//#define ENABLE_DEBUG_PRINTF // DO NOT LEAVE THIS DEFINED: DEBUG ONLY!!! +#include <assert.h> + +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Timer.h" + +#include "SymbolFileDWARF.h" +#include "LogChannelDWARF.h" + +using namespace lldb_private; +using namespace std; + +//---------------------------------------------------------------------- +// Parse +// +// Parse all information in the debug_line_data into an internal +// representation. +//---------------------------------------------------------------------- +void +DWARFDebugLine::Parse(const DataExtractor& debug_line_data) +{ + m_lineTableMap.clear(); + dw_offset_t offset = 0; + LineTable::shared_ptr line_table_sp(new LineTable); + while (debug_line_data.ValidOffset(offset)) + { + const uint32_t debug_line_offset = offset; + + if (line_table_sp.get() == NULL) + break; + + if (ParseStatementTable(debug_line_data, &offset, line_table_sp.get())) + { + // Make sure we don't don't loop infinitely + if (offset <= debug_line_offset) + break; + //DEBUG_PRINTF("m_lineTableMap[0x%8.8x] = line_table_sp\n", debug_line_offset); + m_lineTableMap[debug_line_offset] = line_table_sp; + line_table_sp.reset(new LineTable); + } + else + ++offset; // Try next byte in line table + } +} + +void +DWARFDebugLine::ParseIfNeeded(const DataExtractor& debug_line_data) +{ + if (m_lineTableMap.empty()) + Parse(debug_line_data); +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::GetLineTable +//---------------------------------------------------------------------- +DWARFDebugLine::LineTable::shared_ptr +DWARFDebugLine::GetLineTable(const dw_offset_t offset) const +{ + DWARFDebugLine::LineTable::shared_ptr line_table_shared_ptr; + LineTableConstIter pos = m_lineTableMap.find(offset); + if (pos != m_lineTableMap.end()) + line_table_shared_ptr = pos->second; + return line_table_shared_ptr; +} + + +//---------------------------------------------------------------------- +// DumpStateToFile +//---------------------------------------------------------------------- +static void +DumpStateToFile (dw_offset_t offset, const DWARFDebugLine::State& state, void* userData) +{ + Log *log = (Log *)userData; + if (state.row == DWARFDebugLine::State::StartParsingLineTable) + { + // If the row is zero we are being called with the prologue only + state.prologue->Dump (log); + log->PutCString ("Address Line Column File"); + log->PutCString ("------------------ ------ ------ ------"); + } + else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) + { + // Done parsing line table + } + else + { + log->Printf( "0x%16.16llx %6u %6u %6u%s\n", state.address, state.line, state.column, state.file, state.end_sequence ? " END" : ""); + } +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::DumpLineTableRows +//---------------------------------------------------------------------- +bool +DWARFDebugLine::DumpLineTableRows(Log *log, SymbolFileDWARF* dwarf2Data, dw_offset_t debug_line_offset) +{ + const DataExtractor& debug_line_data = dwarf2Data->get_debug_line_data(); + + if (debug_line_offset == DW_INVALID_OFFSET) + { + // Dump line table to a single file only + debug_line_offset = 0; + while (debug_line_data.ValidOffset(debug_line_offset)) + debug_line_offset = DumpStatementTable (log, debug_line_data, debug_line_offset); + } + else + { + // Dump line table to a single file only + DumpStatementTable (log, debug_line_data, debug_line_offset); + } + return false; +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::DumpStatementTable +//---------------------------------------------------------------------- +dw_offset_t +DWARFDebugLine::DumpStatementTable(Log *log, const DataExtractor& debug_line_data, const dw_offset_t debug_line_offset) +{ + if (debug_line_data.ValidOffset(debug_line_offset)) + { + uint32_t offset = debug_line_offset; + log->Printf( "----------------------------------------------------------------------\n" + "debug_line[0x%8.8x]\n" + "----------------------------------------------------------------------\n", debug_line_offset); + + if (ParseStatementTable(debug_line_data, &offset, DumpStateToFile, log)) + return offset; + else + return debug_line_offset + 1; // Skip to next byte in .debug_line section + } + + return DW_INVALID_OFFSET; +} + + +//---------------------------------------------------------------------- +// DumpOpcodes +//---------------------------------------------------------------------- +bool +DWARFDebugLine::DumpOpcodes(Log *log, SymbolFileDWARF* dwarf2Data, dw_offset_t debug_line_offset, uint32_t dump_flags) +{ + const DataExtractor& debug_line_data = dwarf2Data->get_debug_line_data(); + + if (debug_line_data.GetByteSize() == 0) + { + log->Printf( "< EMPTY >\n"); + return false; + } + + if (debug_line_offset == DW_INVALID_OFFSET) + { + // Dump line table to a single file only + debug_line_offset = 0; + while (debug_line_data.ValidOffset(debug_line_offset)) + debug_line_offset = DumpStatementOpcodes (log, debug_line_data, debug_line_offset, dump_flags); + } + else + { + // Dump line table to a single file only + DumpStatementOpcodes (log, debug_line_data, debug_line_offset, dump_flags); + } + return false; +} + +//---------------------------------------------------------------------- +// DumpStatementOpcodes +//---------------------------------------------------------------------- +dw_offset_t +DWARFDebugLine::DumpStatementOpcodes(Log *log, const DataExtractor& debug_line_data, const dw_offset_t debug_line_offset, uint32_t flags) +{ + uint32_t offset = debug_line_offset; + if (debug_line_data.ValidOffset(offset)) + { + Prologue prologue; + + if (ParsePrologue(debug_line_data, &offset, &prologue)) + { + log->PutCString ("----------------------------------------------------------------------"); + log->Printf ("debug_line[0x%8.8x]", debug_line_offset); + log->PutCString ("----------------------------------------------------------------------\n"); + prologue.Dump (log); + } + else + { + offset = debug_line_offset; + log->Printf( "0x%8.8x: skipping pad byte %2.2x", offset, debug_line_data.GetU8(&offset)); + return offset; + } + + Row row(prologue.default_is_stmt); + const dw_offset_t end_offset = debug_line_offset + prologue.total_length + sizeof(prologue.total_length); + + assert(debug_line_data.ValidOffset(end_offset-1)); + + while (offset < end_offset) + { + const uint32_t op_offset = offset; + uint8_t opcode = debug_line_data.GetU8(&offset); + switch (opcode) + { + case 0: // Extended Opcodes always start with a zero opcode followed by + { // a uleb128 length so you can skip ones you don't know about + + dw_offset_t ext_offset = offset; + dw_uleb128_t len = debug_line_data.GetULEB128(&offset); + dw_offset_t arg_size = len - (offset - ext_offset); + uint8_t sub_opcode = debug_line_data.GetU8(&offset); +// if (verbose) +// log->Printf( "Extended: <%u> %2.2x ", len, sub_opcode); + + switch (sub_opcode) + { + case DW_LNE_end_sequence : + log->Printf( "0x%8.8x: DW_LNE_end_sequence", op_offset); + row.Dump(log); + row.Reset(prologue.default_is_stmt); + break; + + case DW_LNE_set_address : + { + row.address = debug_line_data.GetMaxU64(&offset, arg_size); + log->Printf( "0x%8.8x: DW_LNE_set_address (0x%llx)", op_offset, row.address); + } + break; + + case DW_LNE_define_file: + { + FileNameEntry fileEntry; + fileEntry.name = debug_line_data.GetCStr(&offset); + fileEntry.dir_idx = debug_line_data.GetULEB128(&offset); + fileEntry.mod_time = debug_line_data.GetULEB128(&offset); + fileEntry.length = debug_line_data.GetULEB128(&offset); + log->Printf( "0x%8.8x: DW_LNE_define_file('%s', dir=%i, mod_time=0x%8.8x, length=%i )", + op_offset, + fileEntry.name.c_str(), + fileEntry.dir_idx, + fileEntry.mod_time, + fileEntry.length); + prologue.file_names.push_back(fileEntry); + } + break; + + default: + log->Printf( "0x%8.8x: DW_LNE_??? (%2.2x) - Skipping unknown upcode", op_offset, opcode); + // Length doesn't include the zero opcode byte or the length itself, but + // it does include the sub_opcode, so we have to adjust for that below + offset += arg_size; + break; + } + } + break; + + // Standard Opcodes + case DW_LNS_copy: + log->Printf( "0x%8.8x: DW_LNS_copy", op_offset); + row.Dump (log); + break; + + case DW_LNS_advance_pc: + { + dw_uleb128_t addr_offset_n = debug_line_data.GetULEB128(&offset); + dw_uleb128_t addr_offset = addr_offset_n * prologue.min_inst_length; + log->Printf( "0x%8.8x: DW_LNS_advance_pc (0x%llx)", op_offset, addr_offset); + row.address += addr_offset; + } + break; + + case DW_LNS_advance_line: + { + dw_sleb128_t line_offset = debug_line_data.GetSLEB128(&offset); + log->Printf( "0x%8.8x: DW_LNS_advance_line (%i)", op_offset, line_offset); + row.line += line_offset; + } + break; + + case DW_LNS_set_file: + row.file = debug_line_data.GetULEB128(&offset); + log->Printf( "0x%8.8x: DW_LNS_set_file (%u)", op_offset, row.file); + break; + + case DW_LNS_set_column: + row.column = debug_line_data.GetULEB128(&offset); + log->Printf( "0x%8.8x: DW_LNS_set_column (%u)", op_offset, row.column); + break; + + case DW_LNS_negate_stmt: + row.is_stmt = !row.is_stmt; + log->Printf( "0x%8.8x: DW_LNS_negate_stmt", op_offset); + break; + + case DW_LNS_set_basic_block: + row.basic_block = true; + log->Printf( "0x%8.8x: DW_LNS_set_basic_block", op_offset); + break; + + case DW_LNS_const_add_pc: + { + uint8_t adjust_opcode = 255 - prologue.opcode_base; + dw_addr_t addr_offset = (adjust_opcode / prologue.line_range) * prologue.min_inst_length; + log->Printf( "0x%8.8x: DW_LNS_const_add_pc (0x%8.8llx)", op_offset, addr_offset); + row.address += addr_offset; + } + break; + + case DW_LNS_fixed_advance_pc: + { + uint16_t pc_offset = debug_line_data.GetU16(&offset); + log->Printf( "0x%8.8x: DW_LNS_fixed_advance_pc (0x%4.4x)", op_offset, pc_offset); + row.address += pc_offset; + } + break; + + case DW_LNS_set_prologue_end: + row.prologue_end = true; + log->Printf( "0x%8.8x: DW_LNS_set_prologue_end", op_offset); + break; + + case DW_LNS_set_epilogue_begin: + row.epilogue_begin = true; + log->Printf( "0x%8.8x: DW_LNS_set_epilogue_begin", op_offset); + break; + + case DW_LNS_set_isa: + row.isa = debug_line_data.GetULEB128(&offset); + log->Printf( "0x%8.8x: DW_LNS_set_isa (%u)", op_offset, row.isa); + break; + + // Special Opcodes + default: + if (opcode < prologue.opcode_base) + { + // We have an opcode that this parser doesn't know about, skip + // the number of ULEB128 numbers that is says to skip in the + // prologue's standard_opcode_lengths array + uint8_t n = prologue.standard_opcode_lengths[opcode-1]; + log->Printf( "0x%8.8x: Special : Unknown skipping %u ULEB128 values.", op_offset, n); + while (n > 0) + { + debug_line_data.GetULEB128(&offset); + --n; + } + } + else + { + uint8_t adjust_opcode = opcode - prologue.opcode_base; + dw_addr_t addr_offset = (adjust_opcode / prologue.line_range) * prologue.min_inst_length; + int32_t line_offset = prologue.line_base + (adjust_opcode % prologue.line_range); + log->Printf("0x%8.8x: address += 0x%llx, line += %i\n", op_offset, (uint64_t)addr_offset, line_offset); + row.address += addr_offset; + row.line += line_offset; + row.Dump (log); + } + break; + } + } + return end_offset; + } + return DW_INVALID_OFFSET; +} + + + + +//---------------------------------------------------------------------- +// Parse +// +// Parse the entire line table contents calling callback each time a +// new prologue is parsed and every time a new row is to be added to +// the line table. +//---------------------------------------------------------------------- +void +DWARFDebugLine::Parse(const DataExtractor& debug_line_data, DWARFDebugLine::State::Callback callback, void* userData) +{ + uint32_t offset = 0; + if (debug_line_data.ValidOffset(offset)) + { + if (!ParseStatementTable(debug_line_data, &offset, callback, userData)) + ++offset; // Skip to next byte in .debug_line section + } +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::ParsePrologue +//---------------------------------------------------------------------- +bool +DWARFDebugLine::ParsePrologue(const DataExtractor& debug_line_data, dw_offset_t* offset_ptr, Prologue* prologue) +{ +// const uint32_t prologue_offset = *offset_ptr; + + //DEBUG_PRINTF("0x%8.8x: ParsePrologue()\n", *offset_ptr); + + prologue->Clear(); + uint32_t i; + const char * s; + prologue->total_length = debug_line_data.GetU32(offset_ptr); + prologue->version = debug_line_data.GetU16(offset_ptr); + if (prologue->version != 2) + return false; + + prologue->prologue_length = debug_line_data.GetU32(offset_ptr); + const dw_offset_t end_prologue_offset = prologue->prologue_length + *offset_ptr; + prologue->min_inst_length = debug_line_data.GetU8(offset_ptr); + prologue->default_is_stmt = debug_line_data.GetU8(offset_ptr); + prologue->line_base = debug_line_data.GetU8(offset_ptr); + prologue->line_range = debug_line_data.GetU8(offset_ptr); + prologue->opcode_base = debug_line_data.GetU8(offset_ptr); + + prologue->standard_opcode_lengths.reserve(prologue->opcode_base-1); + + for (i=1; i<prologue->opcode_base; ++i) + { + uint8_t op_len = debug_line_data.GetU8(offset_ptr); + prologue->standard_opcode_lengths.push_back(op_len); + } + + while (*offset_ptr < end_prologue_offset) + { + s = debug_line_data.GetCStr(offset_ptr); + if (s && s[0]) + prologue->include_directories.push_back(s); + else + break; + } + + while (*offset_ptr < end_prologue_offset) + { + const char* name = debug_line_data.GetCStr( offset_ptr ); + if (name && name[0]) + { + FileNameEntry fileEntry; + fileEntry.name = name; + fileEntry.dir_idx = debug_line_data.GetULEB128( offset_ptr ); + fileEntry.mod_time = debug_line_data.GetULEB128( offset_ptr ); + fileEntry.length = debug_line_data.GetULEB128( offset_ptr ); + prologue->file_names.push_back(fileEntry); + } + else + break; + } + + assert(*offset_ptr == end_prologue_offset); + return end_prologue_offset; +} + +bool +DWARFDebugLine::ParseSupportFiles(const DataExtractor& debug_line_data, const char *cu_comp_dir, dw_offset_t stmt_list, FileSpecList &support_files) +{ + uint32_t offset = stmt_list + 4; // Skip the total length + const char * s; + uint32_t version = debug_line_data.GetU16(&offset); + if (version != 2) + return false; + + const dw_offset_t end_prologue_offset = debug_line_data.GetU32(&offset) + offset; + // Skip instruction length, default is stmt, line base, line range and + // opcode base, and all opcode lengths + offset += 4; + const uint8_t opcode_base = debug_line_data.GetU8(&offset); + offset += opcode_base - 1; + std::vector<std::string> include_directories; + include_directories.push_back(""); // Directory at index zero doesn't exist + while (offset < end_prologue_offset) + { + s = debug_line_data.GetCStr(&offset); + if (s && s[0]) + include_directories.push_back(s); + else + break; + } + std::string fullpath; + while (offset < end_prologue_offset) + { + const char* path = debug_line_data.GetCStr( &offset ); + if (path && path[0]) + { + uint32_t dir_idx = debug_line_data.GetULEB128( &offset ); + debug_line_data.Skip_LEB128(&offset); // Skip mod_time + debug_line_data.Skip_LEB128(&offset); // Skip length + + if (path[0] == '/') + { + // The path starts with a directory delimiter, so we are done. + fullpath = path; + } + else + { + if (dir_idx > 0 && dir_idx < include_directories.size()) + { + if (cu_comp_dir && include_directories[dir_idx][0] != '/') + { + fullpath = cu_comp_dir; + + if (*fullpath.rbegin() != '/') + fullpath += '/'; + fullpath += include_directories[dir_idx]; + + } + else + fullpath = include_directories[dir_idx]; + } + else if (cu_comp_dir && cu_comp_dir[0]) + { + fullpath = cu_comp_dir; + } + + if (!fullpath.empty()) + { + if (*fullpath.rbegin() != '/') + fullpath += '/'; + } + fullpath += path; + } + FileSpec file_spec(fullpath.c_str()); + support_files.Append(file_spec); + } + } + + assert(offset == end_prologue_offset); + return end_prologue_offset; +} + +//---------------------------------------------------------------------- +// ParseStatementTable +// +// Parse a single line table (prologue and all rows) and call the +// callback function once for the prologue (row in state will be zero) +// and each time a row is to be added to the line table. +//---------------------------------------------------------------------- +bool +DWARFDebugLine::ParseStatementTable +( + const DataExtractor& debug_line_data, + dw_offset_t* offset_ptr, + DWARFDebugLine::State::Callback callback, + void* userData +) +{ + Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_LINE); + Prologue::shared_ptr prologue(new Prologue()); + + + const dw_offset_t debug_line_offset = *offset_ptr; + + Timer scoped_timer (__PRETTY_FUNCTION__, + "DWARFDebugLine::ParseStatementTable (.debug_line[0x%8.8x])", + debug_line_offset); + + if (!ParsePrologue(debug_line_data, offset_ptr, prologue.get())) + { + if (log) + log->Error ("failed to parse DWARF line table prologue"); + // Restore our offset and return false to indicate failure! + *offset_ptr = debug_line_offset; + return false; + } + + if (log) + prologue->Dump (log); + + const dw_offset_t end_offset = debug_line_offset + prologue->total_length + sizeof(prologue->total_length); + + assert(debug_line_data.ValidOffset(end_offset-1)); + + State state(prologue, log, callback, userData); + + while (*offset_ptr < end_offset) + { + //DEBUG_PRINTF("0x%8.8x: ", *offset_ptr); + uint8_t opcode = debug_line_data.GetU8(offset_ptr); + + if (opcode == 0) + { + // Extended Opcodes always start with a zero opcode followed by + // a uleb128 length so you can skip ones you don't know about + dw_offset_t ext_offset = *offset_ptr; + dw_uleb128_t len = debug_line_data.GetULEB128(offset_ptr); + dw_offset_t arg_size = len - (*offset_ptr - ext_offset); + + //DEBUG_PRINTF("Extended: <%2u> ", len); + uint8_t sub_opcode = debug_line_data.GetU8(offset_ptr); + switch (sub_opcode) + { + case DW_LNE_end_sequence: + // Set the end_sequence register of the state machine to true and + // append a row to the matrix using the current values of the + // state-machine registers. Then reset the registers to the initial + // values specified above. Every statement program sequence must end + // with a DW_LNE_end_sequence instruction which creates a row whose + // address is that of the byte after the last target machine instruction + // of the sequence. + state.end_sequence = true; + state.AppendRowToMatrix(*offset_ptr); + state.Reset(); + break; + + case DW_LNE_set_address: + // Takes a single relocatable address as an operand. The size of the + // operand is the size appropriate to hold an address on the target + // machine. Set the address register to the value given by the + // relocatable address. All of the other statement program opcodes + // that affect the address register add a delta to it. This instruction + // stores a relocatable value into it instead. + state.address = debug_line_data.GetAddress(offset_ptr); + break; + + case DW_LNE_define_file: + // Takes 4 arguments. The first is a null terminated string containing + // a source file name. The second is an unsigned LEB128 number representing + // the directory index of the directory in which the file was found. The + // third is an unsigned LEB128 number representing the time of last + // modification of the file. The fourth is an unsigned LEB128 number + // representing the length in bytes of the file. The time and length + // fields may contain LEB128(0) if the information is not available. + // + // The directory index represents an entry in the include_directories + // section of the statement program prologue. The index is LEB128(0) + // if the file was found in the current directory of the compilation, + // LEB128(1) if it was found in the first directory in the + // include_directories section, and so on. The directory index is + // ignored for file names that represent full path names. + // + // The files are numbered, starting at 1, in the order in which they + // appear; the names in the prologue come before names defined by + // the DW_LNE_define_file instruction. These numbers are used in the + // the file register of the state machine. + { + FileNameEntry fileEntry; + fileEntry.name = debug_line_data.GetCStr(offset_ptr); + fileEntry.dir_idx = debug_line_data.GetULEB128(offset_ptr); + fileEntry.mod_time = debug_line_data.GetULEB128(offset_ptr); + fileEntry.length = debug_line_data.GetULEB128(offset_ptr); + state.prologue->file_names.push_back(fileEntry); + } + break; + + default: + // Length doesn't include the zero opcode byte or the length itself, but + // it does include the sub_opcode, so we have to adjust for that below + (*offset_ptr) += arg_size; + break; + } + } + else if (opcode < prologue->opcode_base) + { + switch (opcode) + { + // Standard Opcodes + case DW_LNS_copy: + // Takes no arguments. Append a row to the matrix using the + // current values of the state-machine registers. Then set + // the basic_block register to false. + state.AppendRowToMatrix(*offset_ptr); + break; + + case DW_LNS_advance_pc: + // Takes a single unsigned LEB128 operand, multiplies it by the + // min_inst_length field of the prologue, and adds the + // result to the address register of the state machine. + state.address += debug_line_data.GetULEB128(offset_ptr) * prologue->min_inst_length; + break; + + case DW_LNS_advance_line: + // Takes a single signed LEB128 operand and adds that value to + // the line register of the state machine. + state.line += debug_line_data.GetSLEB128(offset_ptr); + break; + + case DW_LNS_set_file: + // Takes a single unsigned LEB128 operand and stores it in the file + // register of the state machine. + state.file = debug_line_data.GetULEB128(offset_ptr); + break; + + case DW_LNS_set_column: + // Takes a single unsigned LEB128 operand and stores it in the + // column register of the state machine. + state.column = debug_line_data.GetULEB128(offset_ptr); + break; + + case DW_LNS_negate_stmt: + // Takes no arguments. Set the is_stmt register of the state + // machine to the logical negation of its current value. + state.is_stmt = !state.is_stmt; + break; + + case DW_LNS_set_basic_block: + // Takes no arguments. Set the basic_block register of the + // state machine to true + state.basic_block = true; + break; + + case DW_LNS_const_add_pc: + // Takes no arguments. Add to the address register of the state + // machine the address increment value corresponding to special + // opcode 255. The motivation for DW_LNS_const_add_pc is this: + // when the statement program needs to advance the address by a + // small amount, it can use a single special opcode, which occupies + // a single byte. When it needs to advance the address by up to + // twice the range of the last special opcode, it can use + // DW_LNS_const_add_pc followed by a special opcode, for a total + // of two bytes. Only if it needs to advance the address by more + // than twice that range will it need to use both DW_LNS_advance_pc + // and a special opcode, requiring three or more bytes. + { + uint8_t adjust_opcode = 255 - prologue->opcode_base; + dw_addr_t addr_offset = (adjust_opcode / prologue->line_range) * prologue->min_inst_length; + state.address += addr_offset; + } + break; + + case DW_LNS_fixed_advance_pc: + // Takes a single uhalf operand. Add to the address register of + // the state machine the value of the (unencoded) operand. This + // is the only extended opcode that takes an argument that is not + // a variable length number. The motivation for DW_LNS_fixed_advance_pc + // is this: existing assemblers cannot emit DW_LNS_advance_pc or + // special opcodes because they cannot encode LEB128 numbers or + // judge when the computation of a special opcode overflows and + // requires the use of DW_LNS_advance_pc. Such assemblers, however, + // can use DW_LNS_fixed_advance_pc instead, sacrificing compression. + state.address += debug_line_data.GetU16(offset_ptr); + break; + + case DW_LNS_set_prologue_end: + // Takes no arguments. Set the prologue_end register of the + // state machine to true + state.prologue_end = true; + break; + + case DW_LNS_set_epilogue_begin: + // Takes no arguments. Set the basic_block register of the + // state machine to true + state.epilogue_begin = true; + break; + + case DW_LNS_set_isa: + // Takes a single unsigned LEB128 operand and stores it in the + // column register of the state machine. + state.isa = debug_line_data.GetULEB128(offset_ptr); + break; + + default: + // Handle any unknown standard opcodes here. We know the lengths + // of such opcodes because they are specified in the prologue + // as a multiple of LEB128 operands for each opcode. + { + uint8_t i; + assert (opcode - 1 < prologue->standard_opcode_lengths.size()); + const uint8_t opcode_length = prologue->standard_opcode_lengths[opcode - 1]; + for (i=0; i<opcode_length; ++i) + debug_line_data.Skip_LEB128(offset_ptr); + } + break; + } + } + else + { + // Special Opcodes + + // A special opcode value is chosen based on the amount that needs + // to be added to the line and address registers. The maximum line + // increment for a special opcode is the value of the line_base + // field in the header, plus the value of the line_range field, + // minus 1 (line base + line range - 1). If the desired line + // increment is greater than the maximum line increment, a standard + // opcode must be used instead of a special opcode. The “address + // advance” is calculated by dividing the desired address increment + // by the minimum_instruction_length field from the header. The + // special opcode is then calculated using the following formula: + // + // opcode = (desired line increment - line_base) + (line_range * address advance) + opcode_base + // + // If the resulting opcode is greater than 255, a standard opcode + // must be used instead. + // + // To decode a special opcode, subtract the opcode_base from the + // opcode itself to give the adjusted opcode. The amount to + // increment the address register is the result of the adjusted + // opcode divided by the line_range multiplied by the + // minimum_instruction_length field from the header. That is: + // + // address increment = (adjusted opcode / line_range) * minimim_instruction_length + // + // The amount to increment the line register is the line_base plus + // the result of the adjusted opcode modulo the line_range. That is: + // + // line increment = line_base + (adjusted opcode % line_range) + + uint8_t adjust_opcode = opcode - prologue->opcode_base; + dw_addr_t addr_offset = (adjust_opcode / prologue->line_range) * prologue->min_inst_length; + int32_t line_offset = prologue->line_base + (adjust_opcode % prologue->line_range); + state.line += line_offset; + state.address += addr_offset; + state.AppendRowToMatrix(*offset_ptr); + } + } + + state.Finalize( *offset_ptr ); + + return end_offset; +} + + +//---------------------------------------------------------------------- +// ParseStatementTableCallback +//---------------------------------------------------------------------- +static void +ParseStatementTableCallback(dw_offset_t offset, const DWARFDebugLine::State& state, void* userData) +{ + DWARFDebugLine::LineTable* line_table = (DWARFDebugLine::LineTable*)userData; + if (state.row == DWARFDebugLine::State::StartParsingLineTable) + { + // Just started parsing the line table, so lets keep a reference to + // the prologue using the supplied shared pointer + line_table->prologue = state.prologue; + } + else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) + { + // Done parsing line table, nothing to do for the cleanup + } + else + { + // We have a new row, lets append it + line_table->AppendRow(state); + } +} + +//---------------------------------------------------------------------- +// ParseStatementTable +// +// Parse a line table at offset and populate the LineTable class with +// the prologue and all rows. +//---------------------------------------------------------------------- +bool +DWARFDebugLine::ParseStatementTable(const DataExtractor& debug_line_data, uint32_t* offset_ptr, LineTable* line_table) +{ + return ParseStatementTable(debug_line_data, offset_ptr, ParseStatementTableCallback, line_table); +} + + +inline bool +DWARFDebugLine::Prologue::IsValid() const +{ + return SymbolFileDWARF::SupportedVersion(version); +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::Prologue::Dump +//---------------------------------------------------------------------- +void +DWARFDebugLine::Prologue::Dump(Log *log) +{ + uint32_t i; + + log->Printf( "Line table prologue:"); + log->Printf( " total_length: 0x%8.8x", total_length); + log->Printf( " version: %u", version); + log->Printf( "prologue_length: 0x%8.8x", prologue_length); + log->Printf( "min_inst_length: %u", min_inst_length); + log->Printf( "default_is_stmt: %u", default_is_stmt); + log->Printf( " line_base: %i", line_base); + log->Printf( " line_range: %u", line_range); + log->Printf( " opcode_base: %u", opcode_base); + + for (i=0; i<standard_opcode_lengths.size(); ++i) + { + log->Printf( "standard_opcode_lengths[%s] = %u", DW_LNS_value_to_name(i+1), standard_opcode_lengths[i]); + } + + if (!include_directories.empty()) + { + for (i=0; i<include_directories.size(); ++i) + { + log->Printf( "include_directories[%3u] = '%s'", i+1, include_directories[i].c_str()); + } + } + + if (!file_names.empty()) + { + log->PutCString (" Dir Mod Time File Len File Name"); + log->PutCString (" ---- ---------- ---------- ---------------------------"); + for (i=0; i<file_names.size(); ++i) + { + const FileNameEntry& fileEntry = file_names[i]; + log->Printf ("file_names[%3u] %4u 0x%8.8x 0x%8.8x %s", + i+1, + fileEntry.dir_idx, + fileEntry.mod_time, + fileEntry.length, + fileEntry.name.c_str()); + } + } +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::ParsePrologue::Append +// +// Append the contents of the prologue to the binary stream buffer +//---------------------------------------------------------------------- +//void +//DWARFDebugLine::Prologue::Append(BinaryStreamBuf& buff) const +//{ +// uint32_t i; +// +// buff.Append32(total_length); +// buff.Append16(version); +// buff.Append32(prologue_length); +// buff.Append8(min_inst_length); +// buff.Append8(default_is_stmt); +// buff.Append8(line_base); +// buff.Append8(line_range); +// buff.Append8(opcode_base); +// +// for (i=0; i<standard_opcode_lengths.size(); ++i) +// buff.Append8(standard_opcode_lengths[i]); +// +// for (i=0; i<include_directories.size(); ++i) +// buff.AppendCStr(include_directories[i].c_str()); +// buff.Append8(0); // Terminate the include directory section with empty string +// +// for (i=0; i<file_names.size(); ++i) +// { +// buff.AppendCStr(file_names[i].name.c_str()); +// buff.Append32_as_ULEB128(file_names[i].dir_idx); +// buff.Append32_as_ULEB128(file_names[i].mod_time); +// buff.Append32_as_ULEB128(file_names[i].length); +// } +// buff.Append8(0); // Terminate the file names section with empty string +//} + + +bool DWARFDebugLine::Prologue::GetFile(uint32_t file_idx, std::string& path, std::string& directory) const +{ + uint32_t idx = file_idx - 1; // File indexes are 1 based... + if (idx < file_names.size()) + { + path = file_names[idx].name; + uint32_t dir_idx = file_names[idx].dir_idx - 1; + if (dir_idx < include_directories.size()) + directory = include_directories[dir_idx]; + else + directory.clear(); + return true; + } + return false; +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::LineTable::Dump +//---------------------------------------------------------------------- +void +DWARFDebugLine::LineTable::Dump(Log *log) const +{ + if (prologue.get()) + prologue->Dump (log); + + if (!rows.empty()) + { + log->PutCString ("Address Line Column File ISA Flags"); + log->PutCString ("------------------ ------ ------ ------ --- -------------"); + Row::const_iterator pos = rows.begin(); + Row::const_iterator end = rows.end(); + while (pos != end) + { + (*pos).Dump (log); + ++pos; + } + } +} + + +void +DWARFDebugLine::LineTable::AppendRow(const DWARFDebugLine::Row& state) +{ + rows.push_back(state); +} + + + +//---------------------------------------------------------------------- +// Compare function for the binary search in DWARFDebugLine::LineTable::LookupAddress() +//---------------------------------------------------------------------- +static bool FindMatchingAddress (const DWARFDebugLine::Row& row1, const DWARFDebugLine::Row& row2) +{ + return row1.address < row2.address; +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::LineTable::LookupAddress +//---------------------------------------------------------------------- +uint32_t +DWARFDebugLine::LineTable::LookupAddress(dw_addr_t address, dw_addr_t cu_high_pc) const +{ + uint32_t index = UINT_MAX; + if (!rows.empty()) + { + // Use the lower_bound algorithm to perform a binary search since we know + // that our line table data is ordered by address. + DWARFDebugLine::Row row; + row.address = address; + Row::const_iterator begin_pos = rows.begin(); + Row::const_iterator end_pos = rows.end(); + Row::const_iterator pos = lower_bound(begin_pos, end_pos, row, FindMatchingAddress); + if (pos == end_pos) + { + if (address < cu_high_pc) + return rows.size()-1; + } + else + { + // Rely on fact that we are using a std::vector and we can do + // pointer arithmetic to find the row index (which will be one less + // that what we found since it will find the first position after + // the current address) since std::vector iterators are just + // pointers to the container type. + index = pos - begin_pos; + if (pos->address > address) + { + if (index > 0) + --index; + else + index = UINT_MAX; + } + } + } + return index; // Failed to find address +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::Row::Row +//---------------------------------------------------------------------- +DWARFDebugLine::Row::Row(bool default_is_stmt) : + address(0), + line(1), + column(0), + file(1), + is_stmt(default_is_stmt), + basic_block(false), + end_sequence(false), + prologue_end(false), + epilogue_begin(false), + isa(0) +{ +} + +//---------------------------------------------------------------------- +// Called after a row is appended to the matrix +//---------------------------------------------------------------------- +void +DWARFDebugLine::Row::PostAppend() +{ + basic_block = false; + prologue_end = false; + epilogue_begin = false; +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::Row::Reset +//---------------------------------------------------------------------- +void +DWARFDebugLine::Row::Reset(bool default_is_stmt) +{ + address = 0; + line = 1; + column = 0; + file = 1; + is_stmt = default_is_stmt; + basic_block = false; + end_sequence = false; + prologue_end = false; + epilogue_begin = false; + isa = 0; +} +//---------------------------------------------------------------------- +// DWARFDebugLine::Row::Dump +//---------------------------------------------------------------------- +void +DWARFDebugLine::Row::Dump(Log *log) const +{ + log->Printf( "0x%16.16llx %6u %6u %6u %3u %s%s%s%s%s", + address, + line, + column, + file, + isa, + is_stmt ? " is_stmt" : "", + basic_block ? " basic_block" : "", + prologue_end ? " prologue_end" : "", + epilogue_begin ? " epilogue_begin" : "", + end_sequence ? " end_sequence" : ""); +} + +//---------------------------------------------------------------------- +// Compare function LineTable structures +//---------------------------------------------------------------------- +static bool AddressLessThan (const DWARFDebugLine::Row& a, const DWARFDebugLine::Row& b) +{ + return a.address < b.address; +} + + + +// Insert a row at the correct address if the addresses can be out of +// order which can only happen when we are linking a line table that +// may have had it's contents rearranged. +void +DWARFDebugLine::Row::Insert(Row::collection& state_coll, const Row& state) +{ + // If we don't have anything yet, or if the address of the last state in our + // line table is less than the current one, just append the current state + if (state_coll.empty() || AddressLessThan(state_coll.back(), state)) + { + state_coll.push_back(state); + } + else + { + // Do a binary search for the correct entry + pair<Row::iterator, Row::iterator> range(equal_range(state_coll.begin(), state_coll.end(), state, AddressLessThan)); + + // If the addresses are equal, we can safely replace the previous entry + // with the current one if the one it is replacing is an end_sequence entry. + // We currently always place an extra end sequence when ever we exit a valid + // address range for a function in case the functions get rearranged by + // optmimizations or by order specifications. These extra end sequences will + // disappear by getting replaced with valid consecutive entries within a + // compile unit if there are no gaps. + if (range.first == range.second) + { + state_coll.insert(range.first, state); + } + else + { + if ((distance(range.first, range.second) == 1) && range.first->end_sequence == true) + { + *range.first = state; + } + else + { + state_coll.insert(range.second, state); + } + } + } +} + +void +DWARFDebugLine::Row::Dump(Log *log, const Row::collection& state_coll) +{ + std::for_each (state_coll.begin(), state_coll.end(), bind2nd(std::mem_fun_ref(&Row::Dump),log)); +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::State::State +//---------------------------------------------------------------------- +DWARFDebugLine::State::State(Prologue::shared_ptr& p, Log *l, DWARFDebugLine::State::Callback cb, void* userData) : + Row (p->default_is_stmt), + prologue (p), + log (l), + callback (cb), + callbackUserData (userData), + row (StartParsingLineTable) +{ + // Call the callback with the initial row state of zero for the prologue + if (callback) + callback(0, *this, callbackUserData); +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::State::Reset +//---------------------------------------------------------------------- +void +DWARFDebugLine::State::Reset() +{ + Row::Reset(prologue->default_is_stmt); +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::State::AppendRowToMatrix +//---------------------------------------------------------------------- +void +DWARFDebugLine::State::AppendRowToMatrix(dw_offset_t offset) +{ + // Each time we are to add an entry into the line table matrix + // call the callback funtion so that someone can do something with + // the current state of the state machine (like build a line table + // or dump the line table!) + if (log) + { + if (row == 0) + { + log->PutCString ("Address Line Column File ISA Flags"); + log->PutCString ("------------------ ------ ------ ------ --- -------------"); + } + Dump (log); + } + + ++row; // Increase the row number before we call our callback for a real row + if (callback) + callback(offset, *this, callbackUserData); + PostAppend(); +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::State::Finalize +//---------------------------------------------------------------------- +void +DWARFDebugLine::State::Finalize(dw_offset_t offset) +{ + // Call the callback with a special row state when we are done parsing a + // line table + row = DoneParsingLineTable; + if (callback) + callback(offset, *this, callbackUserData); +} + +//void +//DWARFDebugLine::AppendLineTableData +//( +// const DWARFDebugLine::Prologue* prologue, +// const DWARFDebugLine::Row::collection& state_coll, +// const uint32_t addr_size, +// BinaryStreamBuf &debug_line_data +//) +//{ +// if (state_coll.empty()) +// { +// // We have no entries, just make an empty line table +// debug_line_data.Append8(0); +// debug_line_data.Append8(1); +// debug_line_data.Append8(DW_LNE_end_sequence); +// } +// else +// { +// DWARFDebugLine::Row::const_iterator pos; +// Row::const_iterator end = state_coll.end(); +// bool default_is_stmt = prologue->default_is_stmt; +// const DWARFDebugLine::Row reset_state(default_is_stmt); +// const DWARFDebugLine::Row* prev_state = &reset_state; +// const int32_t max_line_increment_for_special_opcode = prologue->MaxLineIncrementForSpecialOpcode(); +// for (pos = state_coll.begin(); pos != end; ++pos) +// { +// const DWARFDebugLine::Row& curr_state = *pos; +// int32_t line_increment = 0; +// dw_addr_t addr_offset = curr_state.address - prev_state->address; +// dw_addr_t addr_advance = (addr_offset) / prologue->min_inst_length; +// line_increment = (int32_t)(curr_state.line - prev_state->line); +// +// // If our previous state was the reset state, then let's emit the +// // address to keep GDB's DWARF parser happy. If we don't start each +// // sequence with a DW_LNE_set_address opcode, the line table won't +// // get slid properly in GDB. +// +// if (prev_state == &reset_state) +// { +// debug_line_data.Append8(0); // Extended opcode +// debug_line_data.Append32_as_ULEB128(addr_size + 1); // Length of opcode bytes +// debug_line_data.Append8(DW_LNE_set_address); +// debug_line_data.AppendMax64(curr_state.address, addr_size); +// addr_advance = 0; +// } +// +// if (prev_state->file != curr_state.file) +// { +// debug_line_data.Append8(DW_LNS_set_file); +// debug_line_data.Append32_as_ULEB128(curr_state.file); +// } +// +// if (prev_state->column != curr_state.column) +// { +// debug_line_data.Append8(DW_LNS_set_column); +// debug_line_data.Append32_as_ULEB128(curr_state.column); +// } +// +// // Don't do anything fancy if we are at the end of a sequence +// // as we don't want to push any extra rows since the DW_LNE_end_sequence +// // will push a row itself! +// if (curr_state.end_sequence) +// { +// if (line_increment != 0) +// { +// debug_line_data.Append8(DW_LNS_advance_line); +// debug_line_data.Append32_as_SLEB128(line_increment); +// } +// +// if (addr_advance > 0) +// { +// debug_line_data.Append8(DW_LNS_advance_pc); +// debug_line_data.Append32_as_ULEB128(addr_advance); +// } +// +// // Now push the end sequence on! +// debug_line_data.Append8(0); +// debug_line_data.Append8(1); +// debug_line_data.Append8(DW_LNE_end_sequence); +// +// prev_state = &reset_state; +// } +// else +// { +// if (line_increment || addr_advance) +// { +// if (line_increment > max_line_increment_for_special_opcode) +// { +// debug_line_data.Append8(DW_LNS_advance_line); +// debug_line_data.Append32_as_SLEB128(line_increment); +// line_increment = 0; +// } +// +// uint32_t special_opcode = (line_increment >= prologue->line_base) ? ((line_increment - prologue->line_base) + (prologue->line_range * addr_advance) + prologue->opcode_base) : 256; +// if (special_opcode > 255) +// { +// // Both the address and line won't fit in one special opcode +// // check to see if just the line advance will? +// uint32_t special_opcode_line = ((line_increment >= prologue->line_base) && (line_increment != 0)) ? +// ((line_increment - prologue->line_base) + prologue->opcode_base) : 256; +// +// +// if (special_opcode_line > 255) +// { +// // Nope, the line advance won't fit by itself, check the address increment by itself +// uint32_t special_opcode_addr = addr_advance ? +// ((0 - prologue->line_base) + (prologue->line_range * addr_advance) + prologue->opcode_base) : 256; +// +// if (special_opcode_addr > 255) +// { +// // Neither the address nor the line will fit in a +// // special opcode, we must manually enter both then +// // do a DW_LNS_copy to push a row (special opcode +// // automatically imply a new row is pushed) +// if (line_increment != 0) +// { +// debug_line_data.Append8(DW_LNS_advance_line); +// debug_line_data.Append32_as_SLEB128(line_increment); +// } +// +// if (addr_advance > 0) +// { +// debug_line_data.Append8(DW_LNS_advance_pc); +// debug_line_data.Append32_as_ULEB128(addr_advance); +// } +// +// // Now push a row onto the line table manually +// debug_line_data.Append8(DW_LNS_copy); +// +// } +// else +// { +// // The address increment alone will fit into a special opcode +// // so modify our line change, then issue a special opcode +// // for the address increment and it will push a row into the +// // line table +// if (line_increment != 0) +// { +// debug_line_data.Append8(DW_LNS_advance_line); +// debug_line_data.Append32_as_SLEB128(line_increment); +// } +// +// // Advance of line and address will fit into a single byte special opcode +// // and this will also push a row onto the line table +// debug_line_data.Append8(special_opcode_addr); +// } +// } +// else +// { +// // The line change alone will fit into a special opcode +// // so modify our address increment first, then issue a +// // special opcode for the line change and it will push +// // a row into the line table +// if (addr_advance > 0) +// { +// debug_line_data.Append8(DW_LNS_advance_pc); +// debug_line_data.Append32_as_ULEB128(addr_advance); +// } +// +// // Advance of line and address will fit into a single byte special opcode +// // and this will also push a row onto the line table +// debug_line_data.Append8(special_opcode_line); +// } +// } +// else +// { +// // Advance of line and address will fit into a single byte special opcode +// // and this will also push a row onto the line table +// debug_line_data.Append8(special_opcode); +// } +// } +// prev_state = &curr_state; +// } +// } +// } +//} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h new file mode 100644 index 00000000000..57b2f159661 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h @@ -0,0 +1,225 @@ +//===-- DWARFDebugLine.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_DWARFDebugLine_h_ +#define liblldb_DWARFDebugLine_h_ + +#include <map> +#include <vector> +#include <string> + +#include "lldb/lldb-private.h" + +#include "DWARFDefines.h" + +class SymbolFileDWARF; +class DWARFDebugInfoEntry; + +//---------------------------------------------------------------------- +// DWARFDebugLine +//---------------------------------------------------------------------- +class DWARFDebugLine +{ +public: + //------------------------------------------------------------------ + // FileNameEntry + //------------------------------------------------------------------ + struct FileNameEntry + { + FileNameEntry() : + name(), + dir_idx(0), + mod_time(0), + length(0) + { + } + + std::string name; + dw_sleb128_t dir_idx; + dw_sleb128_t mod_time; + dw_sleb128_t length; + + }; + + //------------------------------------------------------------------ + // Prologue + //------------------------------------------------------------------ + struct Prologue + { + + Prologue() : + total_length(0), + version(0), + prologue_length(0), + min_inst_length(0), + default_is_stmt(0), + line_base(0), + line_range(0), + opcode_base(0), + standard_opcode_lengths(), + include_directories(), + file_names() + { + } + + typedef lldb::SharedPtr<Prologue>::Type shared_ptr; + + uint32_t total_length; // The size in bytes of the statement information for this compilation unit (not including the total_length field itself). + uint16_t version; // Version identifier for the statement information format. + uint32_t prologue_length;// The number of bytes following the prologue_length field to the beginning of the first byte of the statement program itself. + uint8_t min_inst_length;// The size in bytes of the smallest target machine instruction. Statement program opcodes that alter the address register first multiply their operands by this value. + uint8_t default_is_stmt;// The initial value of theis_stmtregister. + int8_t line_base; // This parameter affects the meaning of the special opcodes. See below. + uint8_t line_range; // This parameter affects the meaning of the special opcodes. See below. + uint8_t opcode_base; // The number assigned to the first special opcode. + std::vector<uint8_t> standard_opcode_lengths; + std::vector<std::string> include_directories; + std::vector<FileNameEntry> file_names; + + // Length of the prologue in bytes + uint32_t Length() const { return prologue_length + sizeof(total_length) + sizeof(version) + sizeof(prologue_length); } + // Length of the line table data in bytes (not including the prologue) + uint32_t StatementTableLength() const { return total_length + sizeof(total_length) - Length(); } + int32_t MaxLineIncrementForSpecialOpcode() const { return line_base + (int8_t)line_range - 1; }; + bool IsValid() const; +// void Append(BinaryStreamBuf& buff) const; + void Dump (lldb_private::Log *log); + void Clear() + { + total_length = version = prologue_length = min_inst_length = line_base = line_range = opcode_base = 0; + line_base = 0; + standard_opcode_lengths.clear(); + include_directories.clear(); + file_names.clear(); + } + bool GetFile(uint32_t file_idx, std::string& file, std::string& dir) const; + + }; + + // Standard .debug_line state machine structure + struct Row + { + typedef std::vector<Row> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + Row(bool default_is_stmt = false); + virtual ~Row() {} + void PostAppend (); + void Reset(bool default_is_stmt); + void Dump(lldb_private::Log *log) const; + static void Insert(Row::collection& state_coll, const Row& state); + static void Dump(lldb_private::Log *log, const Row::collection& state_coll); + + dw_addr_t address; // The program-counter value corresponding to a machine instruction generated by the compiler. + uint32_t line; // An unsigned integer indicating a source line number. Lines are numbered beginning at 1. The compiler may emit the value 0 in cases where an instruction cannot be attributed to any source line. + uint16_t column; // An unsigned integer indicating a column number within a source line. Columns are numbered beginning at 1. The value 0 is reserved to indicate that a statement begins at the ‘‘left edge’’ of the line. + uint16_t file; // An unsigned integer indicating the identity of the source file corresponding to a machine instruction. + uint8_t is_stmt:1, // A boolean indicating that the current instruction is the beginning of a statement. + basic_block:1, // A boolean indicating that the current instruction is the beginning of a basic block. + end_sequence:1, // A boolean indicating that the current address is that of the first byte after the end of a sequence of target machine instructions. + prologue_end:1, // A boolean indicating that the current address is one (of possibly many) where execution should be suspended for an entry breakpoint of a function. + epilogue_begin:1;// A boolean indicating that the current address is one (of possibly many) where execution should be suspended for an exit breakpoint of a function. + uint32_t isa; // An unsigned integer whose value encodes the applicable instruction set architecture for the current instruction. + }; + + + //------------------------------------------------------------------ + // LineTable + //------------------------------------------------------------------ + struct LineTable + { + typedef lldb::SharedPtr<LineTable>::Type shared_ptr; + + LineTable() : + prologue(), + rows() + { + } + + void AppendRow(const DWARFDebugLine::Row& state); + void Clear() + { + prologue.reset(); + rows.clear(); + } + + uint32_t LookupAddress(dw_addr_t address, dw_addr_t cu_high_pc) const; + void Dump(lldb_private::Log *log) const; + + Prologue::shared_ptr prologue; + Row::collection rows; + }; + + //------------------------------------------------------------------ + // State + //------------------------------------------------------------------ + struct State : public Row + { + typedef void (*Callback)(dw_offset_t offset, const State& state, void* userData); + + // Special row codes used when calling the callback + enum + { + StartParsingLineTable = 0, + DoneParsingLineTable = -1 + }; + + State (Prologue::shared_ptr& prologue_sp, + lldb_private::Log *log, + Callback callback, + void* userData); + + void + AppendRowToMatrix (dw_offset_t offset); + + void + Finalize (dw_offset_t offset); + + void + Reset (); + + Prologue::shared_ptr prologue; + lldb_private::Log *log; + Callback callback; // Callback funcation that gets called each time an entry it to be added to the matrix + void* callbackUserData; + int row; // The row number that starts at zero for the prologue, and increases for each row added to the matrix + private: + DISALLOW_COPY_AND_ASSIGN (State); + }; + + static bool DumpOpcodes(lldb_private::Log *log, SymbolFileDWARF* dwarf2Data, dw_offset_t line_offset = DW_INVALID_OFFSET, uint32_t dump_flags = 0); // If line_offset is invalid, dump everything + static bool DumpLineTableRows(lldb_private::Log *log, SymbolFileDWARF* dwarf2Data, dw_offset_t line_offset = DW_INVALID_OFFSET); // If line_offset is invalid, dump everything + static bool ParseSupportFiles(const lldb_private::DataExtractor& debug_line_data, const char *cu_comp_dir, dw_offset_t stmt_list, lldb_private::FileSpecList &support_files); + static bool ParsePrologue(const lldb_private::DataExtractor& debug_line_data, dw_offset_t* offset_ptr, Prologue* prologue); + static bool ParseStatementTable(const lldb_private::DataExtractor& debug_line_data, dw_offset_t* offset_ptr, State::Callback callback, void* userData); + static dw_offset_t DumpStatementTable(lldb_private::Log *log, const lldb_private::DataExtractor& debug_line_data, const dw_offset_t line_offset); + static dw_offset_t DumpStatementOpcodes(lldb_private::Log *log, const lldb_private::DataExtractor& debug_line_data, const dw_offset_t line_offset, uint32_t flags); + static bool ParseStatementTable(const lldb_private::DataExtractor& debug_line_data, uint32_t* offset_ptr, LineTable* line_table); + static void Parse(const lldb_private::DataExtractor& debug_line_data, DWARFDebugLine::State::Callback callback, void* userData); +// static void AppendLineTableData(const DWARFDebugLine::Prologue* prologue, const DWARFDebugLine::Row::collection& state_coll, const uint32_t addr_size, BinaryStreamBuf &debug_line_data); + + DWARFDebugLine() : + m_lineTableMap() + { + } + + void Parse(const lldb_private::DataExtractor& debug_line_data); + void ParseIfNeeded(const lldb_private::DataExtractor& debug_line_data); + LineTable::shared_ptr GetLineTable(const dw_offset_t offset) const; + +protected: + typedef std::map<dw_offset_t, LineTable::shared_ptr> LineTableMap; + typedef LineTableMap::iterator LineTableIter; + typedef LineTableMap::const_iterator LineTableConstIter; + + LineTableMap m_lineTableMap; +}; + +#endif // liblldb_DWARFDebugLine_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.cpp new file mode 100644 index 00000000000..0501da8fe40 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.cpp @@ -0,0 +1,48 @@ +//===-- DWARFDebugMacinfo.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugMacinfo.h" + +#include "DWARFDebugMacinfoEntry.h" +#include "SymbolFileDWARF.h" + +#include "lldb/Core/Stream.h" + +using namespace lldb_private; +using namespace std; + +DWARFDebugMacinfo::DWARFDebugMacinfo() +{ +} + +DWARFDebugMacinfo::~DWARFDebugMacinfo() +{ +} + +void +DWARFDebugMacinfo::Dump(Stream *s, const DataExtractor& macinfo_data, dw_offset_t offset) +{ + DWARFDebugMacinfoEntry maninfo_entry; + if (macinfo_data.GetByteSize() == 0) + { + s->PutCString("< EMPTY >\n"); + return; + } + if (offset == DW_INVALID_OFFSET) + { + offset = 0; + while (maninfo_entry.Extract(macinfo_data, &offset)) + maninfo_entry.Dump(s); + } + else + { + if (maninfo_entry.Extract(macinfo_data, &offset)) + maninfo_entry.Dump(s); + } +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.h new file mode 100644 index 00000000000..e420a5be84e --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.h @@ -0,0 +1,29 @@ +//===-- DWARFDebugMacinfo.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_DWARFDebugLine_h_ +#define SymbolFileDWARF_DWARFDebugLine_h_ + +#include "SymbolFileDWARF.h" + +class DWARFDebugMacinfo +{ +public: + DWARFDebugMacinfo(); + + ~DWARFDebugMacinfo(); + + static void + Dump (lldb_private::Stream *s, + const lldb_private::DataExtractor& macinfo_data, + dw_offset_t offset = DW_INVALID_OFFSET); +}; + + +#endif // SymbolFileDWARF_DWARFDebugLine_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.cpp new file mode 100644 index 00000000000..c07cec45c2a --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.cpp @@ -0,0 +1,132 @@ +//===-- DWARFDebugMacinfoEntry.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugMacinfoEntry.h" + +#include "lldb/Core/Stream.h" + +using namespace lldb_private; +using namespace std; + +DWARFDebugMacinfoEntry::DWARFDebugMacinfoEntry() : + m_type_code(0), + m_line(0), + m_op2() +{ + m_op2.cstr = NULL; +} + +DWARFDebugMacinfoEntry::~DWARFDebugMacinfoEntry() +{ +} + +const char* +DWARFDebugMacinfoEntry::GetCString() const +{ + switch (m_type_code) + { + case 0: + case DW_MACINFO_start_file: + case DW_MACINFO_end_file: + return NULL; + default: + break; + } + return m_op2.cstr; +} + + + +void +DWARFDebugMacinfoEntry::Dump(Stream *s) const +{ + if (m_type_code) + { + s->PutCString(DW_MACINFO_value_to_name(m_type_code)); + + switch (m_type_code) + { + case DW_MACINFO_define: + s->Printf(" line:%u #define %s\n", (uint32_t)m_line, m_op2.cstr); + break; + + case DW_MACINFO_undef: + s->Printf(" line:%u #undef %s\n", (uint32_t)m_line, m_op2.cstr); + break; + + default: + s->Printf(" line:%u str: '%s'\n", (uint32_t)m_line, m_op2.cstr); + break; + + case DW_MACINFO_start_file: + s->Printf(" line:%u file index: '%s'\n", (uint32_t)m_line, (uint32_t)m_op2.file_idx); + break; + + case DW_MACINFO_end_file: + break; + } + } + else + { + s->PutCString(" END\n"); + } +} + + +bool +DWARFDebugMacinfoEntry::Extract(const DataExtractor& mac_info_data, dw_offset_t* offset_ptr) +{ + if (mac_info_data.ValidOffset(*offset_ptr)) + { + m_type_code = mac_info_data.GetU8(offset_ptr); + + switch (m_type_code) + { + + case DW_MACINFO_define: + case DW_MACINFO_undef: + // 2 operands: + // Arg 1: operand encodes the line number of the source line on which + // the relevant defining or undefining pre-processor directives + // appeared. + m_line = mac_info_data.GetULEB128(offset_ptr); + // Arg 2: define string + m_op2.cstr = mac_info_data.GetCStr(offset_ptr); + break; + + case DW_MACINFO_start_file: + // 2 operands: + // Op 1: line number of the source line on which the inclusion + // pre-processor directive occurred. + m_line = mac_info_data.GetULEB128(offset_ptr); + // Op 2: a source file name index to a file number in the statement + // information table for the relevant compilation unit. + m_op2.file_idx = mac_info_data.GetULEB128(offset_ptr); + break; + + case 0: // End of list + case DW_MACINFO_end_file: + // No operands + m_line = DW_INVALID_OFFSET; + m_op2.cstr = NULL; + break; + default: + // Vendor specific entries always have a ULEB128 and a string + m_line = mac_info_data.GetULEB128(offset_ptr); + m_op2.cstr = mac_info_data.GetCStr(offset_ptr); + break; + } + return true; + } + else + m_type_code = 0; + + return false; +} + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.h new file mode 100644 index 00000000000..85dd6254833 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.h @@ -0,0 +1,57 @@ +//===-- DWARFDebugMacinfoEntry.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_DWARFDebugMacinfoEntry_h_ +#define SymbolFileDWARF_DWARFDebugMacinfoEntry_h_ + +#include "SymbolFileDWARF.h" + +class DWARFDebugMacinfoEntry +{ +public: + DWARFDebugMacinfoEntry(); + + ~DWARFDebugMacinfoEntry(); + + uint8_t + TypeCode() const + { + return m_type_code; + } + + uint8_t + GetLineNumber() const + { + return m_line; + } + + void + Dump(lldb_private::Stream *s) const; + + const char* + GetCString() const; + + bool + Extract(const lldb_private::DataExtractor& mac_info_data, + dw_offset_t* offset_ptr); + +protected: + +private: + uint8_t m_type_code; + dw_uleb128_t m_line; + union + { + dw_uleb128_t file_idx; + const char* cstr; + } m_op2; +}; + + +#endif // SymbolFileDWARF_DWARFDebugMacinfoEntry_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.cpp new file mode 100644 index 00000000000..93ecaed72c6 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.cpp @@ -0,0 +1,297 @@ +//===-- DWARFDebugPubnames.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugPubnames.h" + +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" + +#include "DWARFDebugInfo.h" +#include "DWARFDIECollection.h" +#include "DWARFFormValue.h" +#include "DWARFCompileUnit.h" +#include "LogChannelDWARF.h" +#include "SymbolFileDWARF.h" + + +using namespace lldb_private; + +DWARFDebugPubnames::DWARFDebugPubnames() : + m_sets() +{ +} + +bool +DWARFDebugPubnames::Extract(const DataExtractor& data) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "DWARFDebugPubnames::Extract (byte_size = %zu)", + data.GetByteSize()); + Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_PUBNAMES); + if (log) + log->Printf("DWARFDebugPubnames::Extract (byte_size = %zu)", data.GetByteSize()); + + if (data.ValidOffset(0)) + { + uint32_t offset = 0; + + DWARFDebugPubnamesSet set; + while (data.ValidOffset(offset)) + { + if (set.Extract(data, &offset)) + { + m_sets.push_back(set); + offset = set.GetOffsetOfNextEntry(); + } + else + break; + } + if (log) + Dump (log); + return true; + } + return false; +} + + +bool +DWARFDebugPubnames::GeneratePubnames(SymbolFileDWARF* dwarf2Data) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "DWARFDebugPubnames::GeneratePubnames (data = %p)", + dwarf2Data); + + Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_PUBNAMES); + if (log) + log->Printf("DWARFDebugPubnames::GeneratePubnames (data = %p)", dwarf2Data); + + m_sets.clear(); + DWARFDebugInfo* debug_info = dwarf2Data->DebugInfo(); + if (debug_info) + { + + const DataExtractor* debug_str = &dwarf2Data->get_debug_str_data(); + + uint32_t cu_idx = 0; + const uint32_t num_compile_units = dwarf2Data->GetNumCompileUnits(); + for (cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) + { + + DWARFCompileUnit* cu = debug_info->GetCompileUnitAtIndex(cu_idx); + + bool clear_dies = cu->ExtractDIEsIfNeeded (false) > 1; + + DWARFDIECollection dies; + const size_t die_count = cu->AppendDIEsWithTag (DW_TAG_subprogram, dies) + + cu->AppendDIEsWithTag (DW_TAG_variable, dies); + + dw_offset_t cu_offset = cu->GetOffset(); + DWARFDebugPubnamesSet pubnames_set(DW_INVALID_OFFSET, cu_offset, cu->GetNextCompileUnitOffset() - cu_offset); + + size_t die_idx; + for (die_idx = 0; die_idx < die_count; ++die_idx) + { + const DWARFDebugInfoEntry *die = dies.GetDIEPtrAtIndex(die_idx); + DWARFDebugInfoEntry::Attributes attributes; + const char *name = NULL; + const char *mangled = NULL; + bool add_die = false; + bool is_variable = false; + const size_t num_attributes = die->GetAttributes(dwarf2Data, cu, attributes); + if (num_attributes > 0) + { + uint32_t i; + + dw_tag_t tag = die->Tag(); + + is_variable = tag == DW_TAG_variable; + + for (i=0; i<num_attributes; ++i) + { + dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + switch (attr) + { + case DW_AT_name: + if (attributes.ExtractFormValueAtIndex(dwarf2Data, i, form_value)) + name = form_value.AsCString(debug_str); + break; + + case DW_AT_MIPS_linkage_name: + if (attributes.ExtractFormValueAtIndex(dwarf2Data, i, form_value)) + mangled = form_value.AsCString(debug_str); + break; + + case DW_AT_low_pc: + case DW_AT_ranges: + case DW_AT_entry_pc: + if (tag == DW_TAG_subprogram) + add_die = true; + break; + + case DW_AT_location: + if (tag == DW_TAG_variable) + { + const DWARFDebugInfoEntry* parent_die = die->GetParent(); + while ( parent_die != NULL ) + { + switch (parent_die->Tag()) + { + case DW_TAG_subprogram: + case DW_TAG_lexical_block: + case DW_TAG_inlined_subroutine: + // Even if this is a function level static, we don't add it. We could theoretically + // add these if we wanted to by introspecting into the DW_AT_location and seeing + // if the location describes a hard coded address, but we dont want the performance + // penalty of that right now. + add_die = false; +// if (attributes.ExtractFormValueAtIndex(dwarf2Data, i, form_value)) +// { +// // If we have valid block data, then we have location expression bytes +// // that are fixed (not a location list). +// const uint8_t *block_data = form_value.BlockData(); +// if (block_data) +// { +// uint32_t block_length = form_value.Unsigned(); +// if (block_length == 1 + attributes.CompileUnitAtIndex(i)->GetAddressByteSize()) +// { +// if (block_data[0] == DW_OP_addr) +// add_die = true; +// } +// } +// } + parent_die = NULL; // Terminate the while loop. + break; + + case DW_TAG_compile_unit: + add_die = true; + parent_die = NULL; // Terminate the while loop. + break; + + default: + parent_die = parent_die->GetParent(); // Keep going in the while loop. + break; + } + } + } + break; + } + } + } + + if (add_die && (name || mangled)) + { + if (is_variable) + cu->AddGlobal(die); + pubnames_set.AddDescriptor(die->GetOffset() - cu_offset, mangled ? mangled : name); + } + } + + if (pubnames_set.NumDescriptors() > 0) + { + m_sets.push_back(pubnames_set); + } + + // Keep memory down by clearing DIEs if this generate function + // caused them to be parsed + if (clear_dies) + cu->ClearDIEs (true); + } + } + if (m_sets.empty()) + return false; + if (log) + Dump (log); + return true; +} + +bool +DWARFDebugPubnames::GeneratePubBaseTypes(SymbolFileDWARF* dwarf2Data) +{ + m_sets.clear(); + DWARFDebugInfo* debug_info = dwarf2Data->DebugInfo(); + if (debug_info) + { + uint32_t cu_idx = 0; + const uint32_t num_compile_units = dwarf2Data->GetNumCompileUnits(); + for (cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) + { + DWARFCompileUnit* cu = debug_info->GetCompileUnitAtIndex(cu_idx); + DWARFDIECollection dies; + const size_t die_count = cu->AppendDIEsWithTag (DW_TAG_base_type, dies); + dw_offset_t cu_offset = cu->GetOffset(); + DWARFDebugPubnamesSet pubnames_set(DW_INVALID_OFFSET, cu_offset, cu->GetNextCompileUnitOffset() - cu_offset); + + size_t die_idx; + for (die_idx = 0; die_idx < die_count; ++die_idx) + { + const DWARFDebugInfoEntry *die = dies.GetDIEPtrAtIndex(die_idx); + const char *name = die->GetAttributeValueAsString(dwarf2Data, cu, DW_AT_name, NULL); + + if (name) + { + pubnames_set.AddDescriptor(die->GetOffset() - cu_offset, name); + } + } + + if (pubnames_set.NumDescriptors() > 0) + { + m_sets.push_back(pubnames_set); + } + } + } + return !m_sets.empty(); +} + +void +DWARFDebugPubnames::Dump(Log *s) const +{ + if (m_sets.empty()) + s->PutCString("< EMPTY >\n"); + else + { + const_iterator pos; + const_iterator end = m_sets.end(); + + for (pos = m_sets.begin(); pos != end; ++pos) + (*pos).Dump(s); + } +} + +bool +DWARFDebugPubnames::Find(const char* name, bool ignore_case, std::vector<dw_offset_t>& die_offsets) const +{ + const_iterator pos; + const_iterator end = m_sets.end(); + + die_offsets.clear(); + + for (pos = m_sets.begin(); pos != end; ++pos) + { + (*pos).Find(name, ignore_case, die_offsets); + } + + return !die_offsets.empty(); +} + +bool +DWARFDebugPubnames::Find(const RegularExpression& regex, std::vector<dw_offset_t>& die_offsets) const +{ + const_iterator pos; + const_iterator end = m_sets.end(); + + die_offsets.clear(); + + for (pos = m_sets.begin(); pos != end; ++pos) + { + (*pos).Find(regex, die_offsets); + } + + return !die_offsets.empty(); +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.h new file mode 100644 index 00000000000..7d09bf3b55a --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.h @@ -0,0 +1,38 @@ +//===-- DWARFDebugPubnames.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_DWARFDebugPubnames_h_ +#define SymbolFileDWARF_DWARFDebugPubnames_h_ + +#include "SymbolFileDWARF.h" + +#include <list> + +#include "DWARFDebugPubnamesSet.h" + +class DWARFDebugPubnames +{ +public: + DWARFDebugPubnames(); + bool Extract(const lldb_private::DataExtractor& data); + bool GeneratePubnames(SymbolFileDWARF* dwarf2Data); + bool GeneratePubBaseTypes(SymbolFileDWARF* dwarf2Data); + + void Dump(lldb_private::Log *s) const; + bool Find(const char* name, bool ignore_case, std::vector<dw_offset_t>& die_offset_coll) const; + bool Find(const lldb_private::RegularExpression& regex, std::vector<dw_offset_t>& die_offsets) const; +protected: + typedef std::list<DWARFDebugPubnamesSet> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + collection m_sets; +}; + +#endif // SymbolFileDWARF_DWARFDebugPubnames_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.cpp new file mode 100644 index 00000000000..0421ced55d4 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.cpp @@ -0,0 +1,166 @@ +//===-- DWARFDebugPubnamesSet.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugPubnamesSet.h" + +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Log.h" + +#include "SymbolFileDWARF.h" + +using namespace lldb_private; + +DWARFDebugPubnamesSet::DWARFDebugPubnamesSet() : + m_offset(DW_INVALID_OFFSET), + m_header(), + m_descriptors(), + m_name_to_descriptor_index() +{ +} + +DWARFDebugPubnamesSet::DWARFDebugPubnamesSet(dw_offset_t debug_aranges_offset, dw_offset_t cu_die_offset, dw_offset_t cu_die_length) : + m_offset(debug_aranges_offset), + m_header(), + m_descriptors(), + m_name_to_descriptor_index() +{ + m_header.length = 10; // set the length to only include the header right for now + m_header.version = 2; // The DWARF version number + m_header.die_offset = cu_die_offset;// compile unit .debug_info offset + m_header.die_length = cu_die_length;// compile unit .debug_info length +} + +void +DWARFDebugPubnamesSet::AddDescriptor(dw_offset_t cu_rel_offset, const char* name) +{ + if (name && name[0]) + { + // Adjust our header length + m_header.length += strlen(name) + 1 + sizeof(dw_offset_t); + Descriptor pubnameDesc(cu_rel_offset, name); + m_descriptors.push_back(pubnameDesc); + } +} + +void +DWARFDebugPubnamesSet::Clear() +{ + m_offset = DW_INVALID_OFFSET; + m_header.length = 10; + m_header.version = 2; + m_header.die_offset = DW_INVALID_OFFSET; + m_header.die_length = 0; + m_descriptors.clear(); +} + + +//---------------------------------------------------------------------- +// InitNameIndexes +//---------------------------------------------------------------------- +void +DWARFDebugPubnamesSet::InitNameIndexes() const +{ + // Create the name index vector to be able to quickly search by name + const size_t count = m_descriptors.size(); + for (uint32_t idx = 0; idx < count; ++idx) + { + const char* name = m_descriptors[idx].name.c_str(); + if (name && name[0]) + m_name_to_descriptor_index.insert(cstr_to_index_mmap::value_type(name, idx)); + } +} + + +bool +DWARFDebugPubnamesSet::Extract(const DataExtractor& data, uint32_t* offset_ptr) +{ + if (data.ValidOffset(*offset_ptr)) + { + m_descriptors.clear(); + m_offset = *offset_ptr; + m_header.length = data.GetU32(offset_ptr); + m_header.version = data.GetU16(offset_ptr); + m_header.die_offset = data.GetU32(offset_ptr); + m_header.die_length = data.GetU32(offset_ptr); + + Descriptor pubnameDesc; + while (data.ValidOffset(*offset_ptr)) + { + pubnameDesc.offset = data.GetU32(offset_ptr); + + if (pubnameDesc.offset) + { + const char* name = data.GetCStr(offset_ptr); + if (name && name[0]) + { + pubnameDesc.name = name; + m_descriptors.push_back(pubnameDesc); + } + } + else + break; // We are done if we get a zero 4 byte offset + } + + return !m_descriptors.empty(); + } + return false; +} + +dw_offset_t +DWARFDebugPubnamesSet::GetOffsetOfNextEntry() const +{ + return m_offset + m_header.length + 4; +} + +void +DWARFDebugPubnamesSet::Dump(Log *log) const +{ + log->Printf("Pubnames Header: length = 0x%8.8x, version = 0x%4.4x, die_offset = 0x%8.8x, die_length = 0x%8.8x", + m_header.length, + m_header.version, + m_header.die_offset, + m_header.die_length); + + bool verbose = log->GetVerbose(); + + DescriptorConstIter pos; + DescriptorConstIter end = m_descriptors.end(); + for (pos = m_descriptors.begin(); pos != end; ++pos) + { + if (verbose) + log->Printf("0x%8.8x + 0x%8.8x = 0x%8.8x: %s", pos->offset, m_header.die_offset, pos->offset + m_header.die_offset, pos->name.c_str()); + else + log->Printf("0x%8.8x: %s", pos->offset + m_header.die_offset, pos->name.c_str()); + } +} + + +void +DWARFDebugPubnamesSet::Find(const char* name, bool ignore_case, std::vector<dw_offset_t>& die_offset_coll) const +{ + if (!m_descriptors.empty() && m_name_to_descriptor_index.empty()) + InitNameIndexes(); + + std::pair<cstr_to_index_mmap::const_iterator, cstr_to_index_mmap::const_iterator> range(m_name_to_descriptor_index.equal_range(name)); + for (cstr_to_index_mmap::const_iterator pos = range.first; pos != range.second; ++pos) + die_offset_coll.push_back(m_header.die_offset + m_descriptors[(*pos).second].offset); +} + +void +DWARFDebugPubnamesSet::Find(const RegularExpression& regex, std::vector<dw_offset_t>& die_offset_coll) const +{ + DescriptorConstIter pos; + DescriptorConstIter end = m_descriptors.end(); + for (pos = m_descriptors.begin(); pos != end; ++pos) + { + if ( regex.Execute(pos->name.c_str()) ) + die_offset_coll.push_back(m_header.die_offset + pos->offset); + } +} + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.h new file mode 100644 index 00000000000..0597e368e1f --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.h @@ -0,0 +1,91 @@ +//===-- DWARFDebugPubnamesSet.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_DWARFDebugPubnamesSet_h_ +#define SymbolFileDWARF_DWARFDebugPubnamesSet_h_ + +#include "SymbolFileDWARF.h" +#include <string> +#include <vector> +#include <ext/hash_map> + +class DWARFDebugPubnamesSet +{ +public: + struct Header + { + uint32_t length; // length of the set of entries for this compilation unit, not including the length field itself + uint16_t version; // The DWARF version number + uint32_t die_offset; // compile unit .debug_info offset + uint32_t die_length; // compile unit .debug_info length + Header() : + length(10), + version(2), + die_offset(DW_INVALID_OFFSET), + die_length(0) + { + } + }; + + struct Descriptor + { + Descriptor() : + offset(), + name() + { + } + + Descriptor(dw_offset_t the_offset, const char *the_name) : + offset(the_offset), + name(the_name ? the_name : "") + { + } + + dw_offset_t offset; + std::string name; + }; + + DWARFDebugPubnamesSet(); + DWARFDebugPubnamesSet(dw_offset_t debug_aranges_offset, dw_offset_t cu_die_offset, dw_offset_t die_length); + dw_offset_t GetOffset() const { return m_offset; } + void SetOffset(dw_offset_t offset) { m_offset = offset; } + DWARFDebugPubnamesSet::Header& GetHeader() { return m_header; } + const DWARFDebugPubnamesSet::Header& GetHeader() const { return m_header; } + const DWARFDebugPubnamesSet::Descriptor* GetDescriptor(uint32_t i) const + { + if (i < m_descriptors.size()) + return &m_descriptors[i]; + return NULL; + } + uint32_t NumDescriptors() const { return m_descriptors.size(); } + void AddDescriptor(dw_offset_t cu_rel_offset, const char* name); + void Clear(); + bool Extract(const lldb_private::DataExtractor& debug_pubnames_data, uint32_t* offset_ptr); + void Dump(lldb_private::Log *s) const; + void InitNameIndexes() const; + void Find(const char* name, bool ignore_case, std::vector<dw_offset_t>& die_offset_coll) const; + void Find(const lldb_private::RegularExpression& regex, std::vector<dw_offset_t>& die_offsets) const; + dw_offset_t GetOffsetOfNextEntry() const; + + + +protected: + typedef std::vector<Descriptor> DescriptorColl; + typedef DescriptorColl::iterator DescriptorIter; + typedef DescriptorColl::const_iterator DescriptorConstIter; + + + dw_offset_t m_offset; + Header m_header; + typedef __gnu_cxx::hash_multimap<const char*, uint32_t, __gnu_cxx::hash<const char*>, CStringEqualBinaryPredicate> cstr_to_index_mmap; + DescriptorColl m_descriptors; + mutable cstr_to_index_mmap m_name_to_descriptor_index; +}; + +#endif // SymbolFileDWARF_DWARFDebugPubnamesSet_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp new file mode 100644 index 00000000000..62da22855f7 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp @@ -0,0 +1,275 @@ +//===-- DWARFDebugRanges.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugRanges.h" +#include "SymbolFileDWARF.h" +#include "lldb/Core/Stream.h" +#include <assert.h> + +using namespace lldb_private; +using namespace std; + +DWARFDebugRanges::DWARFDebugRanges() : + m_range_map() +{ +} + +DWARFDebugRanges::~DWARFDebugRanges() +{ +} + +void +DWARFDebugRanges::Extract(SymbolFileDWARF* dwarf2Data) +{ + RangeList range_list; + dw_offset_t offset = 0; + dw_offset_t debug_ranges_offset = offset; + while (range_list.Extract(dwarf2Data, &offset)) + { + m_range_map[debug_ranges_offset] = range_list; + debug_ranges_offset = offset; + } +} + +bool +DWARFDebugRanges::RangeList::AddRange(dw_addr_t lo_addr, dw_addr_t hi_addr) +{ + if (lo_addr <= hi_addr) + { + Range range(lo_addr, hi_addr); + ranges.push_back(range); + return true; + } + return false; +} + +const DWARFDebugRanges::Range* +DWARFDebugRanges::RangeList::Lookup(dw_addr_t offset) const +{ + Range::const_iterator pos = ranges.begin(); + Range::const_iterator end_pos = ranges.end(); + for (pos = ranges.begin(); pos != end_pos; ++pos) + { + if (pos->begin_offset <= offset && offset < pos->end_offset) + { + return &(*pos); + } + } + return NULL; +} + +size_t +DWARFDebugRanges::RangeList::Size() const +{ + return ranges.size(); +} + +void +DWARFDebugRanges::RangeList::AddOffset(dw_addr_t offset) +{ + if (!ranges.empty()) + { + Range::iterator pos = ranges.begin(); + Range::iterator end_pos = ranges.end(); + for (pos = ranges.begin(); pos != end_pos; ++pos) + { + // assert for unsigned overflows + assert (~pos->begin_offset >= offset); + assert (~pos->end_offset >= offset); + pos->begin_offset += offset; + pos->end_offset += offset; + } + } +} + +void +DWARFDebugRanges::RangeList::SubtractOffset(dw_addr_t offset) +{ + if (!ranges.empty()) + { + Range::iterator pos = ranges.begin(); + Range::iterator end_pos = ranges.end(); + for (pos = ranges.begin(); pos != end_pos; ++pos) + { + assert (pos->begin_offset >= offset); + assert (pos->end_offset >= offset); + pos->begin_offset -= offset; + pos->end_offset -= offset; + } + } +} + + +const DWARFDebugRanges::Range* +DWARFDebugRanges::RangeList::RangeAtIndex(size_t i) const +{ + if (i < ranges.size()) + return &ranges[i]; + return NULL; +} + +bool +DWARFDebugRanges::RangeList::Extract(SymbolFileDWARF* dwarf2Data, uint32_t* offset_ptr) +{ + Clear(); + uint32_t range_offset = *offset_ptr; + const DataExtractor& debug_ranges_data = dwarf2Data->get_debug_ranges_data(); + uint32_t addr_size = debug_ranges_data.GetAddressByteSize(); + + while (debug_ranges_data.ValidOffsetForDataOfSize(*offset_ptr, 2 * addr_size)) + { + dw_addr_t begin = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); + dw_addr_t end = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); + if (!begin && !end) + { + // End of range list + break; + } + // Extend 4 byte addresses that consits of 32 bits of 1's to be 64 bits + // of ones + switch (addr_size) + { + case 2: + if (begin == 0xFFFFull) + begin = DW_INVALID_ADDRESS; + break; + + case 4: + if (begin == 0xFFFFFFFFull) + begin = DW_INVALID_ADDRESS; + break; + + case 8: + break; + + default: + assert(!"DWARFDebugRanges::RangeList::Extract() unsupported address size."); + break; + } + + // Filter out empty ranges + if (begin != end) + ranges.push_back(Range(begin, end)); + } + + // Make sure we consumed at least something + return range_offset != *offset_ptr; +} + + +dw_addr_t +DWARFDebugRanges::RangeList::LowestAddress(const dw_addr_t cu_base_addr) const +{ + dw_addr_t addr = DW_INVALID_ADDRESS; + dw_addr_t curr_base_addr = cu_base_addr; + if (!ranges.empty()) + { + Range::const_iterator pos = ranges.begin(); + Range::const_iterator end_pos = ranges.end(); + for (pos = ranges.begin(); pos != end_pos; ++pos) + { + if (pos->begin_offset == DW_INVALID_ADDRESS) + curr_base_addr = pos->end_offset; + else if (curr_base_addr != DW_INVALID_ADDRESS) + { + dw_addr_t curr_addr = curr_base_addr + pos->begin_offset; + if (addr > curr_addr) + addr = curr_addr; + } + } + } + return addr; +} + +dw_addr_t +DWARFDebugRanges::RangeList::HighestAddress(const dw_addr_t cu_base_addr) const +{ + dw_addr_t addr = 0; + dw_addr_t curr_base_addr = cu_base_addr; + if (!ranges.empty()) + { + Range::const_iterator pos = ranges.begin(); + Range::const_iterator end_pos = ranges.end(); + for (pos = ranges.begin(); pos != end_pos; ++pos) + { + if (pos->begin_offset == DW_INVALID_ADDRESS) + curr_base_addr = pos->end_offset; + else if (curr_base_addr != DW_INVALID_ADDRESS) + { + dw_addr_t curr_addr = curr_base_addr + pos->end_offset; + if (addr < curr_addr) + addr = curr_addr; + } + } + } + if (addr != 0) + return addr; + return DW_INVALID_ADDRESS; +} + + +void +DWARFDebugRanges::Dump(Stream *s, const DataExtractor& debug_ranges_data, uint32_t* offset_ptr, dw_addr_t cu_base_addr) +{ + uint32_t addr_size = s->GetAddressByteSize(); + bool verbose = s->GetVerbose(); + + dw_addr_t base_addr = cu_base_addr; + while (debug_ranges_data.ValidOffsetForDataOfSize(*offset_ptr, 2 * addr_size)) + { + dw_addr_t begin = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); + dw_addr_t end = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); + // Extend 4 byte addresses that consits of 32 bits of 1's to be 64 bits + // of ones + if (begin == 0xFFFFFFFFull && addr_size == 4) + begin = DW_INVALID_ADDRESS; + + s->Indent(); + if (verbose) + { + s->AddressRange(begin, end, sizeof (dw_addr_t), " offsets = "); + } + + + if (begin == 0 && end == 0) + { + s->PutCString(" End"); + break; + } + else if (begin == DW_INVALID_ADDRESS) + { + // A base address selection entry + base_addr = end; + s->Address(base_addr, sizeof (dw_addr_t), " Base address = "); + } + else + { + // Convert from offset to an address + dw_addr_t begin_addr = begin + base_addr; + dw_addr_t end_addr = end + base_addr; + + s->AddressRange(begin_addr, end_addr, sizeof (dw_addr_t), verbose ? " ==> addrs = " : NULL); + } + } +} + +bool +DWARFDebugRanges::FindRanges(dw_offset_t debug_ranges_offset, RangeList& range_list) const +{ + range_map_const_iterator pos = m_range_map.find(debug_ranges_offset); + if (pos != m_range_map.end()) + { + range_list = pos->second; + return true; + } + return false; +} + + + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h new file mode 100644 index 00000000000..607c3c24a3e --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h @@ -0,0 +1,89 @@ +//===-- DWARFDebugRanges.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_DWARFDebugRanges_h_ +#define liblldb_DWARFDebugRanges_h_ + +#include "SymbolFileDWARF.h" +#include <map> +#include <vector> + + +class DWARFDebugRanges +{ +public: + + //------------------------------------------------------------------ + // Address range + //------------------------------------------------------------------ + struct Range + { + Range(dw_addr_t begin = DW_INVALID_ADDRESS, dw_addr_t end = DW_INVALID_ADDRESS) : + begin_offset(begin), + end_offset(end) + { + } + + void Clear() + { + begin_offset = DW_INVALID_ADDRESS; + end_offset = DW_INVALID_ADDRESS; + } + + dw_addr_t begin_offset; + dw_addr_t end_offset; + + typedef std::vector<Range> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + }; + + //------------------------------------------------------------------ + // Collection of ranges + //------------------------------------------------------------------ + struct RangeList + { + RangeList() : + ranges() + { + } + + bool Extract(SymbolFileDWARF* dwarf2Data, uint32_t* offset_ptr); + bool AddRange(dw_addr_t lo_addr, dw_addr_t hi_addr); + void Clear() + { + ranges.clear(); + } + + dw_addr_t LowestAddress(const dw_addr_t base_addr) const; + dw_addr_t HighestAddress(const dw_addr_t base_addr) const; + void AddOffset(dw_addr_t offset); + void SubtractOffset(dw_addr_t offset); + size_t Size() const; + const Range* RangeAtIndex(size_t i) const; + const Range* Lookup(dw_addr_t offset) const; + Range::collection ranges; + }; + + DWARFDebugRanges(); + ~DWARFDebugRanges(); + void Extract(SymbolFileDWARF* dwarf2Data); + static void Dump(lldb_private::Stream *s, const lldb_private::DataExtractor& debug_ranges_data, uint32_t* offset_ptr, dw_addr_t cu_base_addr); + bool FindRanges(dw_offset_t debug_ranges_offset, DWARFDebugRanges::RangeList& range_list) const; + +protected: + typedef std::map<dw_offset_t, RangeList> range_map; + typedef range_map::iterator range_map_iterator; + typedef range_map::const_iterator range_map_const_iterator; + range_map m_range_map; +}; + + +#endif // liblldb_DWARFDebugRanges_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.c b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.c new file mode 100644 index 00000000000..fe487f9b792 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.c @@ -0,0 +1,2224 @@ +//===-- DWARFDefines.c ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDefines.h" +#include <stdio.h> + +#define DW_TAG_PREFIX "TAG_" +#define DW_AT_PREFIX " AT_" +#define DW_FORM_PREFIX "FORM_" + +/* [7.5.4] Figure 16 "Tag Encodings" (pp. 125-127) in DWARFv3 draft 8 */ + +const char * +DW_TAG_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0000: return DW_TAG_PREFIX "NULL"; + case 0x0001: return DW_TAG_PREFIX "array_type"; + case 0x0002: return DW_TAG_PREFIX "class_type"; + case 0x0003: return DW_TAG_PREFIX "entry_point"; + case 0x0004: return DW_TAG_PREFIX "enumeration_type"; + case 0x0005: return DW_TAG_PREFIX "formal_parameter"; + case 0x0008: return DW_TAG_PREFIX "imported_declaration"; + case 0x000a: return DW_TAG_PREFIX "label"; + case 0x000b: return DW_TAG_PREFIX "lexical_block"; + case 0x000d: return DW_TAG_PREFIX "member"; + case 0x000f: return DW_TAG_PREFIX "pointer_type"; + case 0x0010: return DW_TAG_PREFIX "reference_type"; + case 0x0011: return DW_TAG_PREFIX "compile_unit"; + case 0x0012: return DW_TAG_PREFIX "string_type"; + case 0x0013: return DW_TAG_PREFIX "structure_type"; + case 0x0015: return DW_TAG_PREFIX "subroutine_type"; + case 0x0016: return DW_TAG_PREFIX "typedef"; + case 0x0017: return DW_TAG_PREFIX "union_type"; + case 0x0018: return DW_TAG_PREFIX "unspecified_parameters"; + case 0x0019: return DW_TAG_PREFIX "variant"; + case 0x001a: return DW_TAG_PREFIX "common_block"; + case 0x001b: return DW_TAG_PREFIX "common_inclusion"; + case 0x001c: return DW_TAG_PREFIX "inheritance"; + case 0x001d: return DW_TAG_PREFIX "inlined_subroutine"; + case 0x001e: return DW_TAG_PREFIX "module"; + case 0x001f: return DW_TAG_PREFIX "ptr_to_member_type"; + case 0x0020: return DW_TAG_PREFIX "set_type"; + case 0x0021: return DW_TAG_PREFIX "subrange_type"; + case 0x0022: return DW_TAG_PREFIX "with_stmt"; + case 0x0023: return DW_TAG_PREFIX "access_declaration"; + case 0x0024: return DW_TAG_PREFIX "base_type"; + case 0x0025: return DW_TAG_PREFIX "catch_block"; + case 0x0026: return DW_TAG_PREFIX "const_type"; + case 0x0027: return DW_TAG_PREFIX "constant"; + case 0x0028: return DW_TAG_PREFIX "enumerator"; + case 0x0029: return DW_TAG_PREFIX "file_type"; + case 0x002a: return DW_TAG_PREFIX "friend"; + case 0x002b: return DW_TAG_PREFIX "namelist"; + case 0x002c: return DW_TAG_PREFIX "namelist_item"; + case 0x002d: return DW_TAG_PREFIX "packed_type"; + case 0x002e: return DW_TAG_PREFIX "subprogram"; + case 0x002f: return DW_TAG_PREFIX "template_type_parameter"; + case 0x0030: return DW_TAG_PREFIX "template_value_parameter"; + case 0x0031: return DW_TAG_PREFIX "thrown_type"; + case 0x0032: return DW_TAG_PREFIX "try_block"; + case 0x0033: return DW_TAG_PREFIX "variant_part"; + case 0x0034: return DW_TAG_PREFIX "variable"; + case 0x0035: return DW_TAG_PREFIX "volatile_type"; + case 0x0036: return DW_TAG_PREFIX "dwarf_procedure"; + case 0x0037: return DW_TAG_PREFIX "restrict_type"; + case 0x0038: return DW_TAG_PREFIX "interface_type"; + case 0x0039: return DW_TAG_PREFIX "namespace"; + case 0x003a: return DW_TAG_PREFIX "imported_module"; + case 0x003b: return DW_TAG_PREFIX "unspecified_type"; + case 0x003c: return DW_TAG_PREFIX "partial_unit"; + case 0x003d: return DW_TAG_PREFIX "imported_unit"; +// case 0x003d: return DW_TAG_PREFIX "condition"; + case 0x0040: return DW_TAG_PREFIX "shared_type"; + case 0x4080: return DW_TAG_PREFIX "lo_user"; + case 0xffff: return DW_TAG_PREFIX "hi_user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_TAG constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_TAG_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0001: return "array type"; + case 0x0002: return "class type"; + case 0x0003: return "entry point"; + case 0x0004: return "enumeration type"; + case 0x0005: return "formal parameter"; + case 0x0008: return "imported declaration"; + case 0x000a: return "label"; + case 0x000b: return "lexical block"; + case 0x000d: return "member"; + case 0x000f: return "pointer type"; + case 0x0010: return "reference type"; + case 0x0011: return "file"; + case 0x0012: return "string type"; + case 0x0013: return "structure type"; + case 0x0015: return "subroutine type"; + case 0x0016: return "typedef"; + case 0x0017: return "union type"; + case 0x0018: return "unspecified parameters"; + case 0x0019: return "variant"; + case 0x001a: return "common block"; + case 0x001b: return "common inclusion"; + case 0x001c: return "inheritance"; + case 0x001d: return "inlined subroutine"; + case 0x001e: return "module"; + case 0x001f: return "ptr to member type"; + case 0x0020: return "set type"; + case 0x0021: return "subrange type"; + case 0x0022: return "with stmt"; + case 0x0023: return "access declaration"; + case 0x0024: return "base type"; + case 0x0025: return "catch block"; + case 0x0026: return "const type"; + case 0x0027: return "constant"; + case 0x0028: return "enumerator"; + case 0x0029: return "file type"; + case 0x002a: return "friend"; + case 0x002b: return "namelist"; + case 0x002c: return "namelist item"; + case 0x002d: return "packed type"; + case 0x002e: return "function"; + case 0x002f: return "template type parameter"; + case 0x0030: return "template value parameter"; + case 0x0031: return "thrown type"; + case 0x0032: return "try block"; + case 0x0033: return "variant part"; + case 0x0034: return "variable"; + case 0x0035: return "volatile type"; + case 0x0036: return "dwarf procedure"; + case 0x0037: return "restrict type"; + case 0x0038: return "interface type"; + case 0x0039: return "namespace"; + case 0x003a: return "imported module"; + case 0x003b: return "unspecified type"; + case 0x003c: return "partial unit"; + case 0x003d: return "imported unit"; +// case 0x003d: return "condition"; + case 0x0040: return "shared type"; + case 0x4080: return "lo user"; + case 0xffff: return "hi user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_TAG constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_TAG_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0001: return 0; + case 0x0002: return 0; + case 0x0003: return 0; + case 0x0004: return 0; + case 0x0005: return 0; + case 0x0008: return 0; + case 0x000a: return 0; + case 0x000b: return 0; + case 0x000d: return 0; + case 0x000f: return 0; + case 0x0010: return 0; + case 0x0011: return 0; + case 0x0012: return 0; + case 0x0013: return 0; + case 0x0015: return 0; + case 0x0016: return 0; + case 0x0017: return 0; + case 0x0018: return 0; + case 0x0019: return 0; + case 0x001a: return 0; + case 0x001b: return 0; + case 0x001c: return 0; + case 0x001d: return 0; + case 0x001e: return 0; + case 0x001f: return 0; + case 0x0020: return 0; + case 0x0021: return 0; + case 0x0022: return 0; + case 0x0023: return 0; + case 0x0024: return 0; + case 0x0025: return 0; + case 0x0026: return 0; + case 0x0027: return 0; + case 0x0028: return 0; + case 0x0029: return 0; + case 0x002a: return 0; + case 0x002b: return 0; + case 0x002c: return 0; + case 0x002d: return 0; + case 0x002e: return 0; + case 0x002f: return 0; + case 0x0030: return 0; + case 0x0031: return 0; + case 0x0032: return 0; + case 0x0033: return 0; + case 0x0034: return 0; + case 0x0035: return 0; + case 0x0036: return DRC_DWARFv3; + case 0x0037: return DRC_DWARFv3; + case 0x0038: return DRC_DWARFv3; + case 0x0039: return DRC_DWARFv3; + case 0x003a: return DRC_DWARFv3; + case 0x003b: return DRC_DWARFv3; + case 0x003c: return DRC_DWARFv3; + case 0x003d: return DRC_DWARFv3; +// case 0x003d: return DRC_DWARFv3; + case 0x0040: return DRC_DWARFv3; + case 0x4080: return 0; + case 0xffff: return 0; + default: return 0; + } +} + +/* [7.5.4] Figure 17 "Child determination encodings" (p. 128) in DWARFv3 draft 8 */ + +const char * +DW_CHILDREN_value_to_name (uint8_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "DW_CHILDREN_no"; + case 0x1: return "DW_CHILDREN_yes"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_CHILDREN constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_CHILDREN_value_to_englishy_name (uint8_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "no"; + case 0x1: return "yes"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_CHILDREN constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_CHILDREN_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0: return 0; + case 0x1: return 0; + default: return 0; + } +} + +/* [7.5.4] Figure 18 "Attribute encodings" (pp. 129-132) in DWARFv3 draft 8 */ + +const char * +DW_AT_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0001: return DW_AT_PREFIX "sibling"; + case 0x0002: return DW_AT_PREFIX "location"; + case 0x0003: return DW_AT_PREFIX "name"; + case 0x0009: return DW_AT_PREFIX "ordering"; + case 0x000b: return DW_AT_PREFIX "byte_size"; + case 0x000c: return DW_AT_PREFIX "bit_offset"; + case 0x000d: return DW_AT_PREFIX "bit_size"; + case 0x0010: return DW_AT_PREFIX "stmt_list"; + case 0x0011: return DW_AT_PREFIX "low_pc"; + case 0x0012: return DW_AT_PREFIX "high_pc"; + case 0x0013: return DW_AT_PREFIX "language"; + case 0x0015: return DW_AT_PREFIX "discr"; + case 0x0016: return DW_AT_PREFIX "discr_value"; + case 0x0017: return DW_AT_PREFIX "visibility"; + case 0x0018: return DW_AT_PREFIX "import"; + case 0x0019: return DW_AT_PREFIX "string_length"; + case 0x001a: return DW_AT_PREFIX "common_reference"; + case 0x001b: return DW_AT_PREFIX "comp_dir"; + case 0x001c: return DW_AT_PREFIX "const_value"; + case 0x001d: return DW_AT_PREFIX "containing_type"; + case 0x001e: return DW_AT_PREFIX "default_value"; + case 0x0020: return DW_AT_PREFIX "inline"; + case 0x0021: return DW_AT_PREFIX "is_optional"; + case 0x0022: return DW_AT_PREFIX "lower_bound"; + case 0x0025: return DW_AT_PREFIX "producer"; + case 0x0027: return DW_AT_PREFIX "prototyped"; + case 0x002a: return DW_AT_PREFIX "return_addr"; + case 0x002c: return DW_AT_PREFIX "start_scope"; + case 0x002e: return DW_AT_PREFIX "bit_stride"; + case 0x002f: return DW_AT_PREFIX "upper_bound"; + case 0x0031: return DW_AT_PREFIX "abstract_origin"; + case 0x0032: return DW_AT_PREFIX "accessibility"; + case 0x0033: return DW_AT_PREFIX "address_class"; + case 0x0034: return DW_AT_PREFIX "artificial"; + case 0x0035: return DW_AT_PREFIX "base_types"; + case 0x0036: return DW_AT_PREFIX "calling_convention"; + case 0x0037: return DW_AT_PREFIX "count"; + case 0x0038: return DW_AT_PREFIX "data_member_location"; + case 0x0039: return DW_AT_PREFIX "decl_column"; + case 0x003a: return DW_AT_PREFIX "decl_file"; + case 0x003b: return DW_AT_PREFIX "decl_line"; + case 0x003c: return DW_AT_PREFIX "declaration"; + case 0x003d: return DW_AT_PREFIX "discr_list"; + case 0x003e: return DW_AT_PREFIX "encoding"; + case 0x003f: return DW_AT_PREFIX "external"; + case 0x0040: return DW_AT_PREFIX "frame_base"; + case 0x0041: return DW_AT_PREFIX "friend"; + case 0x0042: return DW_AT_PREFIX "identifier_case"; + case 0x0043: return DW_AT_PREFIX "macro_info"; + case 0x0044: return DW_AT_PREFIX "namelist_item"; + case 0x0045: return DW_AT_PREFIX "priority"; + case 0x0046: return DW_AT_PREFIX "segment"; + case 0x0047: return DW_AT_PREFIX "specification"; + case 0x0048: return DW_AT_PREFIX "static_link"; + case 0x0049: return DW_AT_PREFIX "type"; + case 0x004a: return DW_AT_PREFIX "use_location"; + case 0x004b: return DW_AT_PREFIX "variable_parameter"; + case 0x004c: return DW_AT_PREFIX "virtuality"; + case 0x004d: return DW_AT_PREFIX "vtable_elem_location"; + case 0x004e: return DW_AT_PREFIX "allocated"; + case 0x004f: return DW_AT_PREFIX "associated"; + case 0x0050: return DW_AT_PREFIX "data_location"; + case 0x0051: return DW_AT_PREFIX "byte_stride"; + case 0x0052: return DW_AT_PREFIX "entry_pc"; + case 0x0053: return DW_AT_PREFIX "use_UTF8"; + case 0x0054: return DW_AT_PREFIX "extension"; + case 0x0055: return DW_AT_PREFIX "ranges"; + case 0x0056: return DW_AT_PREFIX "trampoline"; + case 0x0057: return DW_AT_PREFIX "call_column"; + case 0x0058: return DW_AT_PREFIX "call_file"; + case 0x0059: return DW_AT_PREFIX "call_line"; + case 0x005a: return DW_AT_PREFIX "description"; + case 0x005b: return DW_AT_PREFIX "binary_scale"; + case 0x005c: return DW_AT_PREFIX "decimal_scale"; + case 0x005d: return DW_AT_PREFIX "small"; + case 0x005e: return DW_AT_PREFIX "decimal_sign"; + case 0x005f: return DW_AT_PREFIX "digit_count"; + case 0x0060: return DW_AT_PREFIX "picture_string"; + case 0x0061: return DW_AT_PREFIX "mutable"; + case 0x0062: return DW_AT_PREFIX "threads_scaled"; + case 0x0063: return DW_AT_PREFIX "explicit"; + case 0x0064: return DW_AT_PREFIX "object_pointer"; + case 0x0065: return DW_AT_PREFIX "endianity"; + case 0x0066: return DW_AT_PREFIX "elemental"; + case 0x0067: return DW_AT_PREFIX "pure"; + case 0x0068: return DW_AT_PREFIX "recursive"; + case 0x2000: return DW_AT_PREFIX "lo_user"; + case 0x3fff: return DW_AT_PREFIX "hi_user"; + case 0x2001: return DW_AT_PREFIX "MIPS_fde"; + case 0x2002: return DW_AT_PREFIX "MIPS_loop_begin"; + case 0x2003: return DW_AT_PREFIX "MIPS_tail_loop_begin"; + case 0x2004: return DW_AT_PREFIX "MIPS_epilog_begin"; + case 0x2005: return DW_AT_PREFIX "MIPS_loop_unroll_factor"; + case 0x2006: return DW_AT_PREFIX "MIPS_software_pipeline_depth"; + case 0x2007: return DW_AT_PREFIX "MIPS_linkage_name"; + case 0x2008: return DW_AT_PREFIX "MIPS_stride"; + case 0x2009: return DW_AT_PREFIX "MIPS_abstract_name"; + case 0x200a: return DW_AT_PREFIX "MIPS_clone_origin"; + case 0x200b: return DW_AT_PREFIX "MIPS_has_inlines"; + case 0x2101: return DW_AT_PREFIX "sf_names"; + case 0x2102: return DW_AT_PREFIX "src_info"; + case 0x2103: return DW_AT_PREFIX "mac_info"; + case 0x2104: return DW_AT_PREFIX "src_coords"; + case 0x2105: return DW_AT_PREFIX "body_begin"; + case 0x2106: return DW_AT_PREFIX "body_end"; + case 0x2107: return DW_AT_PREFIX "GNU_vector"; + case 0x2501: return DW_AT_PREFIX "APPLE_repository_file"; + case 0x2502: return DW_AT_PREFIX "APPLE_repository_type"; + case 0x2503: return DW_AT_PREFIX "APPLE_repository_name"; + case 0x2504: return DW_AT_PREFIX "APPLE_repository_specification"; + case 0x2505: return DW_AT_PREFIX "APPLE_repository_import"; + case 0x2506: return DW_AT_PREFIX "APPLE_repository_abstract_origin"; + case DW_AT_APPLE_flags: return DW_AT_PREFIX "APPLE_flags"; + case DW_AT_APPLE_optimized: return DW_AT_PREFIX "APPLE_optimized"; + case DW_AT_APPLE_isa: return DW_AT_PREFIX "APPLE_isa"; + case DW_AT_APPLE_block: return DW_AT_PREFIX "APPLE_block"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_AT constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_AT_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0001: return "sibling"; + case 0x0002: return "location"; + case 0x0003: return "name"; + case 0x0009: return "ordering"; + case 0x000b: return "byte size"; + case 0x000c: return "bit offset"; + case 0x000d: return "bit size"; + case 0x0010: return "stmt list"; + case 0x0011: return "low pc"; + case 0x0012: return "high pc"; + case 0x0013: return "language"; + case 0x0015: return "discr"; + case 0x0016: return "discr value"; + case 0x0017: return "visibility"; + case 0x0018: return "import"; + case 0x0019: return "string length"; + case 0x001a: return "common reference"; + case 0x001b: return "comp dir"; + case 0x001c: return "const value"; + case 0x001d: return "containing type"; + case 0x001e: return "default value"; + case 0x0020: return "inline"; + case 0x0021: return "is optional"; + case 0x0022: return "lower bound"; + case 0x0025: return "producer"; + case 0x0027: return "prototyped"; + case 0x002a: return "return addr"; + case 0x002c: return "start scope"; + case 0x002e: return "bit stride"; + case 0x002f: return "upper bound"; + case 0x0031: return "abstract origin"; + case 0x0032: return "accessibility"; + case 0x0033: return "address class"; + case 0x0034: return "artificial"; + case 0x0035: return "base types"; + case 0x0036: return "calling convention"; + case 0x0037: return "count"; + case 0x0038: return "data member location"; + case 0x0039: return "decl column"; + case 0x003a: return "decl file"; + case 0x003b: return "decl line"; + case 0x003c: return "declaration"; + case 0x003d: return "discr list"; + case 0x003e: return "encoding"; + case 0x003f: return "external"; + case 0x0040: return "frame base"; + case 0x0041: return "friend"; + case 0x0042: return "identifier case"; + case 0x0043: return "macro info"; + case 0x0044: return "namelist item"; + case 0x0045: return "priority"; + case 0x0046: return "segment"; + case 0x0047: return "specification"; + case 0x0048: return "static link"; + case 0x0049: return "type"; + case 0x004a: return "use location"; + case 0x004b: return "variable parameter"; + case 0x004c: return "virtuality"; + case 0x004d: return "vtable elem location"; + case 0x004e: return "allocated"; + case 0x004f: return "associated"; + case 0x0050: return "data location"; + case 0x0051: return "byte stride"; + case 0x0052: return "entry pc"; + case 0x0053: return "use UTF8"; + case 0x0054: return "extension"; + case 0x0055: return "ranges"; + case 0x0056: return "trampoline"; + case 0x0057: return "call column"; + case 0x0058: return "call file"; + case 0x0059: return "call line"; + case 0x005a: return "description"; + case 0x005b: return "binary scale"; + case 0x005c: return "decimal scale"; + case 0x005d: return "small"; + case 0x005e: return "decimal sign"; + case 0x005f: return "digit count"; + case 0x0060: return "picture string"; + case 0x0061: return "mutable"; + case 0x0062: return "threads scaled"; + case 0x0063: return "explicit"; + case 0x0064: return "object pointer"; + case 0x0065: return "endianity"; + case 0x0066: return "elemental"; + case 0x0067: return "pure"; + case 0x0068: return "recursive"; + case 0x2000: return "lo user"; + case 0x3fff: return "hi user"; + case 0x2001: return "MIPS fde"; + case 0x2002: return "MIPS loop begin"; + case 0x2003: return "MIPS tail loop begin"; + case 0x2004: return "MIPS epilog begin"; + case 0x2005: return "MIPS loop unroll factor"; + case 0x2006: return "MIPS software pipeline depth"; + case 0x2007: return "MIPS linkage name"; + case 0x2008: return "MIPS stride"; + case 0x2009: return "MIPS abstract name"; + case 0x200a: return "MIPS clone origin"; + case 0x200b: return "MIPS has inlines"; + case 0x2101: return "source file names"; + case 0x2102: return "source info"; + case 0x2103: return "macro info"; + case 0x2104: return "source coordinates"; + case 0x2105: return "body begin"; + case 0x2106: return "body end"; + case 0x2107: return "GNU vector"; + case 0x2501: return "repository file"; + case 0x2502: return "repository type"; + case 0x2503: return "repository name"; + case 0x2504: return "repository specification"; + case 0x2505: return "repository import"; + case 0x2506: return "repository abstract origin"; + case DW_AT_APPLE_flags: return "Apple gcc compiler flags"; + case DW_AT_APPLE_optimized: return "APPLE optimized"; + case DW_AT_APPLE_isa: return "APPLE instruction set architecture"; + case DW_AT_APPLE_block: return "APPLE block"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_AT constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_AT_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0001: return DRC_REFERENCE; + case 0x0002: return DRC_BLOCK | DRC_LOCEXPR | DRC_LOCLISTPTR; + case 0x0003: return DRC_STRING; + case 0x0009: return DRC_CONSTANT; + case 0x000b: return DRC_BLOCK | DRC_CONSTANT | DRC_REFERENCE; + case 0x000c: return DRC_BLOCK | DRC_CONSTANT | DRC_REFERENCE; + case 0x000d: return DRC_BLOCK | DRC_CONSTANT | DRC_REFERENCE; + case 0x0010: return DRC_LINEPTR; + case 0x0011: return DRC_ADDRESS; + case 0x0012: return DRC_ADDRESS; + case 0x0013: return DRC_CONSTANT; + case 0x0015: return DRC_REFERENCE; + case 0x0016: return DRC_CONSTANT; + case 0x0017: return DRC_CONSTANT; + case 0x0018: return DRC_REFERENCE; + case 0x0019: return DRC_BLOCK | DRC_LOCEXPR | DRC_LOCLISTPTR; + case 0x001a: return DRC_REFERENCE; + case 0x001b: return DRC_STRING; + case 0x001c: return DRC_BLOCK | DRC_CONSTANT | DRC_STRING; + case 0x001d: return DRC_REFERENCE; + case 0x001e: return DRC_REFERENCE; + case 0x0020: return DRC_CONSTANT; + case 0x0021: return DRC_FLAG; + case 0x0022: return DRC_BLOCK | DRC_CONSTANT | DRC_REFERENCE; + case 0x0025: return DRC_STRING; + case 0x0027: return DRC_FLAG; + case 0x002a: return DRC_BLOCK | DRC_LOCEXPR | DRC_LOCLISTPTR; + case 0x002c: return DRC_CONSTANT; + case 0x002e: return DRC_CONSTANT; + case 0x002f: return DRC_BLOCK | DRC_CONSTANT | DRC_REFERENCE; + case 0x0031: return DRC_REFERENCE; + case 0x0032: return DRC_CONSTANT; + case 0x0033: return DRC_CONSTANT; + case 0x0034: return DRC_FLAG; + case 0x0035: return DRC_REFERENCE; + case 0x0036: return DRC_CONSTANT; + case 0x0037: return DRC_BLOCK | DRC_CONSTANT | DRC_REFERENCE; + case 0x0038: return DRC_BLOCK | DRC_CONSTANT | DRC_LOCEXPR | DRC_LOCLISTPTR; + case 0x0039: return DRC_CONSTANT; + case 0x003a: return DRC_CONSTANT; + case 0x003b: return DRC_CONSTANT; + case 0x003c: return DRC_FLAG; + case 0x003d: return DRC_BLOCK; + case 0x003e: return DRC_CONSTANT; + case 0x003f: return DRC_FLAG; + case 0x0040: return DRC_BLOCK | DRC_LOCEXPR | DRC_LOCLISTPTR; + case 0x0041: return DRC_REFERENCE; + case 0x0042: return DRC_CONSTANT; + case 0x0043: return DRC_MACPTR; + case 0x0044: return DRC_BLOCK; + case 0x0045: return DRC_REFERENCE; + case 0x0046: return DRC_BLOCK | DRC_CONSTANT; + case 0x0047: return DRC_REFERENCE; + case 0x0048: return DRC_BLOCK | DRC_LOCEXPR | DRC_LOCLISTPTR; + case 0x0049: return DRC_REFERENCE; + case 0x004a: return DRC_BLOCK | DRC_LOCEXPR | DRC_LOCLISTPTR; + case 0x004b: return DRC_FLAG; + case 0x004c: return DRC_CONSTANT; + case 0x004d: return DRC_BLOCK | DRC_LOCEXPR | DRC_LOCLISTPTR; + case 0x004e: return DRC_BLOCK | DRC_CONSTANT | DRC_DWARFv3 | DRC_REFERENCE; + case 0x004f: return DRC_BLOCK | DRC_CONSTANT | DRC_DWARFv3 | DRC_REFERENCE; + case 0x0050: return DRC_BLOCK | DRC_DWARFv3; + case 0x0051: return DRC_BLOCK | DRC_CONSTANT | DRC_DWARFv3 | DRC_REFERENCE; + case 0x0052: return DRC_ADDRESS | DRC_DWARFv3; + case 0x0053: return DRC_DWARFv3 | DRC_FLAG; + case 0x0054: return DRC_DWARFv3 | DRC_REFERENCE; + case 0x0055: return DRC_DWARFv3 | DRC_RANGELISTPTR; + case 0x0056: return DRC_ADDRESS | DRC_DWARFv3 | DRC_FLAG | DRC_REFERENCE | DRC_STRING; + case 0x0057: return DRC_CONSTANT | DRC_DWARFv3; + case 0x0058: return DRC_CONSTANT | DRC_DWARFv3; + case 0x0059: return DRC_CONSTANT | DRC_DWARFv3; + case 0x005a: return DRC_DWARFv3 | DRC_STRING; + case 0x005b: return DRC_CONSTANT | DRC_DWARFv3; + case 0x005c: return DRC_CONSTANT | DRC_DWARFv3; + case 0x005d: return DRC_DWARFv3 | DRC_REFERENCE; + case 0x005e: return DRC_CONSTANT | DRC_DWARFv3; + case 0x005f: return DRC_CONSTANT | DRC_DWARFv3; + case 0x0060: return DRC_DWARFv3 | DRC_STRING; + case 0x0061: return DRC_DWARFv3 | DRC_FLAG; + case 0x0062: return DRC_DWARFv3 | DRC_FLAG; + case 0x0063: return DRC_DWARFv3 | DRC_FLAG; + case 0x0064: return DRC_DWARFv3 | DRC_REFERENCE; + case 0x0065: return DRC_0x65 | DRC_CONSTANT | DRC_DWARFv3; + case 0x0066: return DRC_DWARFv3 | DRC_FLAG; + case 0x0067: return DRC_DWARFv3 | DRC_FLAG; + case 0x0068: return DRC_DWARFv3 | DRC_FLAG; + case 0x2000: return 0; + case 0x3fff: return 0; + case 0x2001: return DRC_VENDOR_MIPS; + case 0x2002: return DRC_VENDOR_MIPS; + case 0x2003: return DRC_VENDOR_MIPS; + case 0x2004: return DRC_VENDOR_MIPS; + case 0x2005: return DRC_VENDOR_MIPS; + case 0x2006: return DRC_VENDOR_MIPS; + case 0x2007: return DRC_STRING | DRC_VENDOR_MIPS; + case 0x2008: return DRC_VENDOR_MIPS; + case 0x2009: return DRC_VENDOR_MIPS; + case 0x200a: return DRC_VENDOR_MIPS; + case 0x200b: return DRC_VENDOR_MIPS; + default: return 0; + } +} + +/* [7.5.4] Figure 19 "Attribute form encodings" (pp. 133-134) in DWARFv3 draft 8 */ + +const char * +DW_FORM_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return DW_FORM_PREFIX "addr"; + case 0x03: return DW_FORM_PREFIX "block2"; + case 0x04: return DW_FORM_PREFIX "block4"; + case 0x05: return DW_FORM_PREFIX "data2"; + case 0x06: return DW_FORM_PREFIX "data4"; + case 0x07: return DW_FORM_PREFIX "data8"; + case 0x08: return DW_FORM_PREFIX "string"; + case 0x09: return DW_FORM_PREFIX "block"; + case 0x0a: return DW_FORM_PREFIX "block1"; + case 0x0b: return DW_FORM_PREFIX "data1"; + case 0x0c: return DW_FORM_PREFIX "flag"; + case 0x0d: return DW_FORM_PREFIX "sdata"; + case 0x0e: return DW_FORM_PREFIX "strp"; + case 0x0f: return DW_FORM_PREFIX "udata"; + case 0x10: return DW_FORM_PREFIX "ref_addr"; + case 0x11: return DW_FORM_PREFIX "ref1"; + case 0x12: return DW_FORM_PREFIX "ref2"; + case 0x13: return DW_FORM_PREFIX "ref4"; + case 0x14: return DW_FORM_PREFIX "ref8"; + case 0x15: return DW_FORM_PREFIX "ref_udata"; + case 0x16: return DW_FORM_PREFIX "indirect"; +// case DW_FORM_APPLE_db_str: return DW_FORM_PREFIX "APPLE_db_str"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_FORM constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_FORM_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "addr"; + case 0x03: return "block2"; + case 0x04: return "block4"; + case 0x05: return "data2"; + case 0x06: return "data4"; + case 0x07: return "data8"; + case 0x08: return "string"; + case 0x09: return "block"; + case 0x0a: return "block1"; + case 0x0b: return "data1"; + case 0x0c: return "flag"; + case 0x0d: return "sdata"; + case 0x0e: return "strp"; + case 0x0f: return "udata"; + case 0x10: return "ref addr"; + case 0x11: return "ref1"; + case 0x12: return "ref2"; + case 0x13: return "ref4"; + case 0x14: return "ref8"; + case 0x15: return "ref udata"; + case 0x16: return "indirect"; +// case DW_FORM_APPLE_db_str: return "repository str"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_FORM constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_FORM_value_to_class (uint32_t val) +{ + switch (val) { + case 0x01: return DRC_ADDRESS; + case 0x03: return DRC_BLOCK | DRC_LOCEXPR; + case 0x04: return DRC_BLOCK | DRC_LOCEXPR; + case 0x05: return DRC_CONSTANT; + case 0x06: return DRC_CONSTANT | DRC_LINEPTR | DRC_LOCLISTPTR | DRC_MACPTR | DRC_RANGELISTPTR; + case 0x07: return DRC_CONSTANT | DRC_LINEPTR | DRC_LOCLISTPTR | DRC_MACPTR | DRC_RANGELISTPTR; + case 0x08: return DRC_STRING; + case 0x09: return DRC_BLOCK | DRC_LOCEXPR; + case 0x0a: return DRC_BLOCK | DRC_LOCEXPR; + case 0x0b: return DRC_CONSTANT; + case 0x0c: return DRC_FLAG; + case 0x0d: return DRC_CONSTANT; + case 0x0e: return DRC_STRING; + case 0x0f: return DRC_CONSTANT; + case 0x10: return DRC_REFERENCE; + case 0x11: return DRC_REFERENCE; + case 0x12: return DRC_REFERENCE; + case 0x13: return DRC_REFERENCE; + case 0x14: return DRC_REFERENCE; + case 0x15: return DRC_REFERENCE; + case 0x16: return DRC_INDIRECT_SPECIAL; + default: return 0; + } +} + +/* [7.7.1] Figure 22 "DWARF operation encodings" (pp. 136-139) in DWARFv3 draft 8 */ + +const char * +DW_OP_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x03: return "DW_OP_addr"; + case 0x06: return "DW_OP_deref"; + case 0x08: return "DW_OP_const1u"; + case 0x09: return "DW_OP_const1s"; + case 0x0a: return "DW_OP_const2u"; + case 0x0b: return "DW_OP_const2s"; + case 0x0c: return "DW_OP_const4u"; + case 0x0d: return "DW_OP_const4s"; + case 0x0e: return "DW_OP_const8u"; + case 0x0f: return "DW_OP_const8s"; + case 0x10: return "DW_OP_constu"; + case 0x11: return "DW_OP_consts"; + case 0x12: return "DW_OP_dup"; + case 0x13: return "DW_OP_drop"; + case 0x14: return "DW_OP_over"; + case 0x15: return "DW_OP_pick"; + case 0x16: return "DW_OP_swap"; + case 0x17: return "DW_OP_rot"; + case 0x18: return "DW_OP_xderef"; + case 0x19: return "DW_OP_abs"; + case 0x1a: return "DW_OP_and"; + case 0x1b: return "DW_OP_div"; + case 0x1c: return "DW_OP_minus"; + case 0x1d: return "DW_OP_mod"; + case 0x1e: return "DW_OP_mul"; + case 0x1f: return "DW_OP_neg"; + case 0x20: return "DW_OP_not"; + case 0x21: return "DW_OP_or"; + case 0x22: return "DW_OP_plus"; + case 0x23: return "DW_OP_plus_uconst"; + case 0x24: return "DW_OP_shl"; + case 0x25: return "DW_OP_shr"; + case 0x26: return "DW_OP_shra"; + case 0x27: return "DW_OP_xor"; + case 0x2f: return "DW_OP_skip"; + case 0x28: return "DW_OP_bra"; + case 0x29: return "DW_OP_eq"; + case 0x2a: return "DW_OP_ge"; + case 0x2b: return "DW_OP_gt"; + case 0x2c: return "DW_OP_le"; + case 0x2d: return "DW_OP_lt"; + case 0x2e: return "DW_OP_ne"; + case 0x30: return "DW_OP_lit0"; + case 0x31: return "DW_OP_lit1"; + case 0x32: return "DW_OP_lit2"; + case 0x33: return "DW_OP_lit3"; + case 0x34: return "DW_OP_lit4"; + case 0x35: return "DW_OP_lit5"; + case 0x36: return "DW_OP_lit6"; + case 0x37: return "DW_OP_lit7"; + case 0x38: return "DW_OP_lit8"; + case 0x39: return "DW_OP_lit9"; + case 0x3a: return "DW_OP_lit10"; + case 0x3b: return "DW_OP_lit11"; + case 0x3c: return "DW_OP_lit12"; + case 0x3d: return "DW_OP_lit13"; + case 0x3e: return "DW_OP_lit14"; + case 0x3f: return "DW_OP_lit15"; + case 0x40: return "DW_OP_lit16"; + case 0x41: return "DW_OP_lit17"; + case 0x42: return "DW_OP_lit18"; + case 0x43: return "DW_OP_lit19"; + case 0x44: return "DW_OP_lit20"; + case 0x45: return "DW_OP_lit21"; + case 0x46: return "DW_OP_lit22"; + case 0x47: return "DW_OP_lit23"; + case 0x48: return "DW_OP_lit24"; + case 0x49: return "DW_OP_lit25"; + case 0x4a: return "DW_OP_lit26"; + case 0x4b: return "DW_OP_lit27"; + case 0x4c: return "DW_OP_lit28"; + case 0x4d: return "DW_OP_lit29"; + case 0x4e: return "DW_OP_lit30"; + case 0x4f: return "DW_OP_lit31"; + case 0x50: return "DW_OP_reg0"; + case 0x51: return "DW_OP_reg1"; + case 0x52: return "DW_OP_reg2"; + case 0x53: return "DW_OP_reg3"; + case 0x54: return "DW_OP_reg4"; + case 0x55: return "DW_OP_reg5"; + case 0x56: return "DW_OP_reg6"; + case 0x57: return "DW_OP_reg7"; + case 0x58: return "DW_OP_reg8"; + case 0x59: return "DW_OP_reg9"; + case 0x5a: return "DW_OP_reg10"; + case 0x5b: return "DW_OP_reg11"; + case 0x5c: return "DW_OP_reg12"; + case 0x5d: return "DW_OP_reg13"; + case 0x5e: return "DW_OP_reg14"; + case 0x5f: return "DW_OP_reg15"; + case 0x60: return "DW_OP_reg16"; + case 0x61: return "DW_OP_reg17"; + case 0x62: return "DW_OP_reg18"; + case 0x63: return "DW_OP_reg19"; + case 0x64: return "DW_OP_reg20"; + case 0x65: return "DW_OP_reg21"; + case 0x66: return "DW_OP_reg22"; + case 0x67: return "DW_OP_reg23"; + case 0x68: return "DW_OP_reg24"; + case 0x69: return "DW_OP_reg25"; + case 0x6a: return "DW_OP_reg26"; + case 0x6b: return "DW_OP_reg27"; + case 0x6c: return "DW_OP_reg28"; + case 0x6d: return "DW_OP_reg29"; + case 0x6e: return "DW_OP_reg30"; + case 0x6f: return "DW_OP_reg31"; + case 0x70: return "DW_OP_breg0"; + case 0x71: return "DW_OP_breg1"; + case 0x72: return "DW_OP_breg2"; + case 0x73: return "DW_OP_breg3"; + case 0x74: return "DW_OP_breg4"; + case 0x75: return "DW_OP_breg5"; + case 0x76: return "DW_OP_breg6"; + case 0x77: return "DW_OP_breg7"; + case 0x78: return "DW_OP_breg8"; + case 0x79: return "DW_OP_breg9"; + case 0x7a: return "DW_OP_breg10"; + case 0x7b: return "DW_OP_breg11"; + case 0x7c: return "DW_OP_breg12"; + case 0x7d: return "DW_OP_breg13"; + case 0x7e: return "DW_OP_breg14"; + case 0x7f: return "DW_OP_breg15"; + case 0x80: return "DW_OP_breg16"; + case 0x81: return "DW_OP_breg17"; + case 0x82: return "DW_OP_breg18"; + case 0x83: return "DW_OP_breg19"; + case 0x84: return "DW_OP_breg20"; + case 0x85: return "DW_OP_breg21"; + case 0x86: return "DW_OP_breg22"; + case 0x87: return "DW_OP_breg23"; + case 0x88: return "DW_OP_breg24"; + case 0x89: return "DW_OP_breg25"; + case 0x8a: return "DW_OP_breg26"; + case 0x8b: return "DW_OP_breg27"; + case 0x8c: return "DW_OP_breg28"; + case 0x8d: return "DW_OP_breg29"; + case 0x8e: return "DW_OP_breg30"; + case 0x8f: return "DW_OP_breg31"; + case 0x90: return "DW_OP_regx"; + case 0x91: return "DW_OP_fbreg"; + case 0x92: return "DW_OP_bregx"; + case 0x93: return "DW_OP_piece"; + case 0x94: return "DW_OP_deref_size"; + case 0x95: return "DW_OP_xderef_size"; + case 0x96: return "DW_OP_nop"; + case 0x97: return "DW_OP_push_object_address"; + case 0x98: return "DW_OP_call2"; + case 0x99: return "DW_OP_call4"; + case 0x9a: return "DW_OP_call_ref"; + case 0xf0: return "DW_OP_APPLE_uninit"; + case 0xe0: return "DW_OP_lo_user"; + case 0xff: return "DW_OP_hi_user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_OP constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_OP_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x03: return "addr"; + case 0x06: return "deref"; + case 0x08: return "const1u"; + case 0x09: return "const1s"; + case 0x0a: return "const2u"; + case 0x0b: return "const2s"; + case 0x0c: return "const4u"; + case 0x0d: return "const4s"; + case 0x0e: return "const8u"; + case 0x0f: return "const8s"; + case 0x10: return "constu"; + case 0x11: return "consts"; + case 0x12: return "dup"; + case 0x13: return "drop"; + case 0x14: return "over"; + case 0x15: return "pick"; + case 0x16: return "swap"; + case 0x17: return "rot"; + case 0x18: return "xderef"; + case 0x19: return "abs"; + case 0x1a: return "and"; + case 0x1b: return "div"; + case 0x1c: return "minus"; + case 0x1d: return "mod"; + case 0x1e: return "mul"; + case 0x1f: return "neg"; + case 0x20: return "not"; + case 0x21: return "or"; + case 0x22: return "plus"; + case 0x23: return "plus uconst"; + case 0x24: return "shl"; + case 0x25: return "shr"; + case 0x26: return "shra"; + case 0x27: return "xor"; + case 0x2f: return "skip"; + case 0x28: return "bra"; + case 0x29: return "eq"; + case 0x2a: return "ge"; + case 0x2b: return "gt"; + case 0x2c: return "le"; + case 0x2d: return "lt"; + case 0x2e: return "ne"; + case 0x30: return "lit0"; + case 0x31: return "lit1"; + case 0x32: return "lit2"; + case 0x33: return "lit3"; + case 0x34: return "lit4"; + case 0x35: return "lit5"; + case 0x36: return "lit6"; + case 0x37: return "lit7"; + case 0x38: return "lit8"; + case 0x39: return "lit9"; + case 0x3a: return "lit10"; + case 0x3b: return "lit11"; + case 0x3c: return "lit12"; + case 0x3d: return "lit13"; + case 0x3e: return "lit14"; + case 0x3f: return "lit15"; + case 0x40: return "lit16"; + case 0x41: return "lit17"; + case 0x42: return "lit18"; + case 0x43: return "lit19"; + case 0x44: return "lit20"; + case 0x45: return "lit21"; + case 0x46: return "lit22"; + case 0x47: return "lit23"; + case 0x48: return "lit24"; + case 0x49: return "lit25"; + case 0x4a: return "lit26"; + case 0x4b: return "lit27"; + case 0x4c: return "lit28"; + case 0x4d: return "lit29"; + case 0x4e: return "lit30"; + case 0x4f: return "lit31"; + case 0x50: return "reg0"; + case 0x51: return "reg1"; + case 0x52: return "reg2"; + case 0x53: return "reg3"; + case 0x54: return "reg4"; + case 0x55: return "reg5"; + case 0x56: return "reg6"; + case 0x57: return "reg7"; + case 0x58: return "reg8"; + case 0x59: return "reg9"; + case 0x5a: return "reg10"; + case 0x5b: return "reg11"; + case 0x5c: return "reg12"; + case 0x5d: return "reg13"; + case 0x5e: return "reg14"; + case 0x5f: return "reg15"; + case 0x60: return "reg16"; + case 0x61: return "reg17"; + case 0x62: return "reg18"; + case 0x63: return "reg19"; + case 0x64: return "reg20"; + case 0x65: return "reg21"; + case 0x66: return "reg22"; + case 0x67: return "reg23"; + case 0x68: return "reg24"; + case 0x69: return "reg25"; + case 0x6a: return "reg26"; + case 0x6b: return "reg27"; + case 0x6c: return "reg28"; + case 0x6d: return "reg29"; + case 0x6e: return "reg30"; + case 0x6f: return "reg31"; + case 0x70: return "breg0"; + case 0x71: return "breg1"; + case 0x72: return "breg2"; + case 0x73: return "breg3"; + case 0x74: return "breg4"; + case 0x75: return "breg5"; + case 0x76: return "breg6"; + case 0x77: return "breg7"; + case 0x78: return "breg8"; + case 0x79: return "breg9"; + case 0x7a: return "breg10"; + case 0x7b: return "breg11"; + case 0x7c: return "breg12"; + case 0x7d: return "breg13"; + case 0x7e: return "breg14"; + case 0x7f: return "breg15"; + case 0x80: return "breg16"; + case 0x81: return "breg17"; + case 0x82: return "breg18"; + case 0x83: return "breg19"; + case 0x84: return "breg20"; + case 0x85: return "breg21"; + case 0x86: return "breg22"; + case 0x87: return "breg23"; + case 0x88: return "breg24"; + case 0x89: return "breg25"; + case 0x8a: return "breg26"; + case 0x8b: return "breg27"; + case 0x8c: return "breg28"; + case 0x8d: return "breg29"; + case 0x8e: return "breg30"; + case 0x8f: return "breg31"; + case 0x90: return "regx"; + case 0x91: return "fbreg"; + case 0x92: return "bregx"; + case 0x93: return "piece"; + case 0x94: return "deref size"; + case 0x95: return "xderef size"; + case 0x96: return "nop"; + case 0x97: return "push object address"; + case 0x98: return "call2"; + case 0x99: return "call4"; + case 0x9a: return "call ref"; + case 0xf0: return "uninitialized"; + case 0xe0: return "lo user"; + case 0xff: return "hi user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_OP constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_OP_value_to_class (uint32_t val) +{ + switch (val) { + case 0x03: return DRC_ONEOPERAND; + case 0x06: return DRC_ZEROOPERANDS; + case 0x08: return DRC_ONEOPERAND; + case 0x09: return DRC_ONEOPERAND; + case 0x0a: return DRC_ONEOPERAND; + case 0x0b: return DRC_ONEOPERAND; + case 0x0c: return DRC_ONEOPERAND; + case 0x0d: return DRC_ONEOPERAND; + case 0x0e: return DRC_ONEOPERAND; + case 0x0f: return DRC_ONEOPERAND; + case 0x10: return DRC_ONEOPERAND; + case 0x11: return DRC_ONEOPERAND; + case 0x12: return DRC_ZEROOPERANDS; + case 0x13: return DRC_ZEROOPERANDS; + case 0x14: return DRC_ZEROOPERANDS; + case 0x15: return DRC_ONEOPERAND; + case 0x16: return DRC_ZEROOPERANDS; + case 0x17: return DRC_ZEROOPERANDS; + case 0x18: return DRC_ZEROOPERANDS; + case 0x19: return DRC_ZEROOPERANDS; + case 0x1a: return DRC_ZEROOPERANDS; + case 0x1b: return DRC_ZEROOPERANDS; + case 0x1c: return DRC_ZEROOPERANDS; + case 0x1d: return DRC_ZEROOPERANDS; + case 0x1e: return DRC_ZEROOPERANDS; + case 0x1f: return DRC_ZEROOPERANDS; + case 0x20: return DRC_ZEROOPERANDS; + case 0x21: return DRC_ZEROOPERANDS; + case 0x22: return DRC_ZEROOPERANDS; + case 0x23: return DRC_ONEOPERAND; + case 0x24: return DRC_ZEROOPERANDS; + case 0x25: return DRC_ZEROOPERANDS; + case 0x26: return DRC_ZEROOPERANDS; + case 0x27: return DRC_ZEROOPERANDS; + case 0x2f: return DRC_ONEOPERAND; + case 0x28: return DRC_ONEOPERAND; + case 0x29: return DRC_ZEROOPERANDS; + case 0x2a: return DRC_ZEROOPERANDS; + case 0x2b: return DRC_ZEROOPERANDS; + case 0x2c: return DRC_ZEROOPERANDS; + case 0x2d: return DRC_ZEROOPERANDS; + case 0x2e: return DRC_ZEROOPERANDS; + case 0x30: return DRC_ZEROOPERANDS; + case 0x31: return DRC_ZEROOPERANDS; + case 0x32: return DRC_ZEROOPERANDS; + case 0x33: return DRC_ZEROOPERANDS; + case 0x34: return DRC_ZEROOPERANDS; + case 0x35: return DRC_ZEROOPERANDS; + case 0x36: return DRC_ZEROOPERANDS; + case 0x37: return DRC_ZEROOPERANDS; + case 0x38: return DRC_ZEROOPERANDS; + case 0x39: return DRC_ZEROOPERANDS; + case 0x3a: return DRC_ZEROOPERANDS; + case 0x3b: return DRC_ZEROOPERANDS; + case 0x3c: return DRC_ZEROOPERANDS; + case 0x3d: return DRC_ZEROOPERANDS; + case 0x3e: return DRC_ZEROOPERANDS; + case 0x3f: return DRC_ZEROOPERANDS; + case 0x40: return DRC_ZEROOPERANDS; + case 0x41: return DRC_ZEROOPERANDS; + case 0x42: return DRC_ZEROOPERANDS; + case 0x43: return DRC_ZEROOPERANDS; + case 0x44: return DRC_ZEROOPERANDS; + case 0x45: return DRC_ZEROOPERANDS; + case 0x46: return DRC_ZEROOPERANDS; + case 0x47: return DRC_ZEROOPERANDS; + case 0x48: return DRC_ZEROOPERANDS; + case 0x49: return DRC_ZEROOPERANDS; + case 0x4a: return DRC_ZEROOPERANDS; + case 0x4b: return DRC_ZEROOPERANDS; + case 0x4c: return DRC_ZEROOPERANDS; + case 0x4d: return DRC_ZEROOPERANDS; + case 0x4e: return DRC_ZEROOPERANDS; + case 0x4f: return DRC_ZEROOPERANDS; + case 0x50: return DRC_ZEROOPERANDS; + case 0x51: return DRC_ZEROOPERANDS; + case 0x52: return DRC_ZEROOPERANDS; + case 0x53: return DRC_ZEROOPERANDS; + case 0x54: return DRC_ZEROOPERANDS; + case 0x55: return DRC_ZEROOPERANDS; + case 0x56: return DRC_ZEROOPERANDS; + case 0x57: return DRC_ZEROOPERANDS; + case 0x58: return DRC_ZEROOPERANDS; + case 0x59: return DRC_ZEROOPERANDS; + case 0x5a: return DRC_ZEROOPERANDS; + case 0x5b: return DRC_ZEROOPERANDS; + case 0x5c: return DRC_ZEROOPERANDS; + case 0x5d: return DRC_ZEROOPERANDS; + case 0x5e: return DRC_ZEROOPERANDS; + case 0x5f: return DRC_ZEROOPERANDS; + case 0x60: return DRC_ZEROOPERANDS; + case 0x61: return DRC_ZEROOPERANDS; + case 0x62: return DRC_ZEROOPERANDS; + case 0x63: return DRC_ZEROOPERANDS; + case 0x64: return DRC_ZEROOPERANDS; + case 0x65: return DRC_ZEROOPERANDS; + case 0x66: return DRC_ZEROOPERANDS; + case 0x67: return DRC_ZEROOPERANDS; + case 0x68: return DRC_ZEROOPERANDS; + case 0x69: return DRC_ZEROOPERANDS; + case 0x6a: return DRC_ZEROOPERANDS; + case 0x6b: return DRC_ZEROOPERANDS; + case 0x6c: return DRC_ZEROOPERANDS; + case 0x6d: return DRC_ZEROOPERANDS; + case 0x6e: return DRC_ZEROOPERANDS; + case 0x6f: return DRC_ZEROOPERANDS; + case 0x70: return DRC_ONEOPERAND; + case 0x71: return DRC_ONEOPERAND; + case 0x72: return DRC_ONEOPERAND; + case 0x73: return DRC_ONEOPERAND; + case 0x74: return DRC_ONEOPERAND; + case 0x75: return DRC_ONEOPERAND; + case 0x76: return DRC_ONEOPERAND; + case 0x77: return DRC_ONEOPERAND; + case 0x78: return DRC_ONEOPERAND; + case 0x79: return DRC_ONEOPERAND; + case 0x7a: return DRC_ONEOPERAND; + case 0x7b: return DRC_ONEOPERAND; + case 0x7c: return DRC_ONEOPERAND; + case 0x7d: return DRC_ONEOPERAND; + case 0x7e: return DRC_ONEOPERAND; + case 0x7f: return DRC_ONEOPERAND; + case 0x80: return DRC_ONEOPERAND; + case 0x81: return DRC_ONEOPERAND; + case 0x82: return DRC_ONEOPERAND; + case 0x83: return DRC_ONEOPERAND; + case 0x84: return DRC_ONEOPERAND; + case 0x85: return DRC_ONEOPERAND; + case 0x86: return DRC_ONEOPERAND; + case 0x87: return DRC_ONEOPERAND; + case 0x88: return DRC_ONEOPERAND; + case 0x89: return DRC_ONEOPERAND; + case 0x8a: return DRC_ONEOPERAND; + case 0x8b: return DRC_ONEOPERAND; + case 0x8c: return DRC_ONEOPERAND; + case 0x8d: return DRC_ONEOPERAND; + case 0x8e: return DRC_ONEOPERAND; + case 0x8f: return DRC_ONEOPERAND; + case 0x90: return DRC_ONEOPERAND; + case 0x91: return DRC_ONEOPERAND; + case 0x92: return DRC_TWOOPERANDS; + case 0x93: return DRC_ONEOPERAND; + case 0x94: return DRC_ONEOPERAND; + case 0x95: return DRC_ONEOPERAND; + case 0x96: return DRC_ZEROOPERANDS; + case 0x97: return DRC_DWARFv3 | DRC_ZEROOPERANDS; + case 0x98: return DRC_DWARFv3 | DRC_ONEOPERAND; + case 0x99: return DRC_DWARFv3 | DRC_ONEOPERAND; + case 0x9a: return DRC_DWARFv3 | DRC_ONEOPERAND; + case 0xf0: return DRC_ZEROOPERANDS; /* DW_OP_APPLE_uninit */ + case 0xe0: return 0; + case 0xff: return 0; + default: return 0; + } +} + +/* [7.8] Figure 23 "Base type encoding values" (pp. 140-141) in DWARFv3 draft 8 */ + +const char * +DW_ATE_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "DW_ATE_address"; + case 0x02: return "DW_ATE_boolean"; + case 0x03: return "DW_ATE_complex_float"; + case 0x04: return "DW_ATE_float"; + case 0x05: return "DW_ATE_signed"; + case 0x06: return "DW_ATE_signed_char"; + case 0x07: return "DW_ATE_unsigned"; + case 0x08: return "DW_ATE_unsigned_char"; + case 0x09: return "DW_ATE_imaginary_float"; + case 0x80: return "DW_ATE_lo_user"; + case 0xff: return "DW_ATE_hi_user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ATE constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_ATE_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "address"; + case 0x02: return "boolean"; + case 0x03: return "complex float"; + case 0x04: return "float"; + case 0x05: return "signed"; + case 0x06: return "signed char"; + case 0x07: return "unsigned"; + case 0x08: return "unsigned char"; + case 0x09: return "imaginary float"; + case 0x80: return "lo user"; + case 0xff: return "hi user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ATE constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_ATE_value_to_class (uint32_t val) +{ + switch (val) { + case 0x01: return 0; + case 0x02: return 0; + case 0x03: return 0; + case 0x04: return 0; + case 0x05: return 0; + case 0x06: return 0; + case 0x07: return 0; + case 0x08: return 0; + case 0x09: return DRC_DWARFv3; + case 0x80: return 0; + case 0xff: return 0; + default: return 0; + } +} + +/* [7.9] Figure 24 "Accessibility encodings" (p. 141) in DWARFv3 draft 8 */ + +const char * +DW_ACCESS_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x1: return "DW_ACCESS_public"; + case 0x2: return "DW_ACCESS_protected"; + case 0x3: return "DW_ACCESS_private"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ACCESS constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_ACCESS_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x1: return "public"; + case 0x2: return "protected"; + case 0x3: return "private"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ACCESS constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_ACCESS_value_to_class (uint32_t val) +{ + switch (val) { + case 0x1: return 0; + case 0x2: return 0; + case 0x3: return 0; + default: return 0; + } +} + +/* [7.10] Figure 25 "Visibility encodings" (p. 142) in DWARFv3 draft 8 */ + +const char * +DW_VIS_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x1: return "DW_VIS_local"; + case 0x2: return "DW_VIS_exported"; + case 0x3: return "DW_VIS_qualified"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_VIS constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_VIS_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x1: return "local"; + case 0x2: return "exported"; + case 0x3: return "qualified"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_VIS constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_VIS_value_to_class (uint32_t val) +{ + switch (val) { + case 0x1: return 0; + case 0x2: return 0; + case 0x3: return 0; + default: return 0; + } +} + +/* [7.11] Figure 26 "Virtuality encodings" (p. 142) in DWARFv3 draft 8 */ + +const char * +DW_VIRTUALITY_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "DW_VIRTUALITY_none"; + case 0x1: return "DW_VIRTUALITY_virtual"; + case 0x2: return "DW_VIRTUALITY_pure_virtual"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_VIRTUALITY constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_VIRTUALITY_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "none"; + case 0x1: return "virtual"; + case 0x2: return "pure virtual"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_VIRTUALITY constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_VIRTUALITY_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0: return 0; + case 0x1: return 0; + case 0x2: return 0; + default: return 0; + } +} + +/* [7.12] Figure 27 "Language encodings" (p. 143) in DWARFv3 draft 8 */ + +const char * +DW_LANG_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0001: return "DW_LANG_C89"; + case 0x0002: return "DW_LANG_C"; + case 0x0003: return "DW_LANG_Ada83"; + case 0x0004: return "DW_LANG_C_plus_plus"; + case 0x0005: return "DW_LANG_Cobol74"; + case 0x0006: return "DW_LANG_Cobol85"; + case 0x0007: return "DW_LANG_Fortran77"; + case 0x0008: return "DW_LANG_Fortran90"; + case 0x0009: return "DW_LANG_Pascal83"; + case 0x000a: return "DW_LANG_Modula2"; + case 0x000b: return "DW_LANG_Java"; + case 0x000c: return "DW_LANG_C99"; + case 0x000d: return "DW_LANG_Ada95"; + case 0x000e: return "DW_LANG_Fortran95"; + case 0x000f: return "DW_LANG_PLI"; + case 0x0010: return "DW_LANG_ObjC"; + case 0x0011: return "DW_LANG_ObjC_plus_plus"; + case 0x0012: return "DW_LANG_UPC"; + case 0x0013: return "DW_LANG_D"; + case 0x8000: return "DW_LANG_lo_user"; + case 0x8001: return "DW_LANG_Mips_Assembler"; + case 0x8765: return "DW_LANG_Upc"; + case 0xffff: return "DW_LANG_hi_user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_LANG constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_LANG_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0001: return "C89"; + case 0x0002: return "C"; + case 0x0003: return "Ada83"; + case 0x0004: return "C++"; + case 0x0005: return "Cobol74"; + case 0x0006: return "Cobol85"; + case 0x0007: return "Fortran77"; + case 0x0008: return "Fortran90"; + case 0x0009: return "Pascal83"; + case 0x000a: return "Modula2"; + case 0x000b: return "Java"; + case 0x000c: return "C99"; + case 0x000d: return "Ada95"; + case 0x000e: return "Fortran95"; + case 0x000f: return "PLI"; + case 0x0010: return "Objective C"; + case 0x0011: return "Objective C++"; + case 0x0012: return "UPC"; + case 0x0013: return "D"; + case 0x8000: return "lo user"; + case 0x8001: return "MIPS Assembler"; + case 0x8765: return "UPC"; + case 0xffff: return "hi user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_LANG constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_LANG_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0001: return 0; + case 0x0002: return 0; + case 0x0003: return 0; + case 0x0004: return 0; + case 0x0005: return 0; + case 0x0006: return 0; + case 0x0007: return 0; + case 0x0008: return 0; + case 0x0009: return 0; + case 0x000a: return 0; + case 0x000b: return DRC_DWARFv3; + case 0x000c: return DRC_DWARFv3; + case 0x000d: return DRC_DWARFv3; + case 0x000e: return DRC_DWARFv3; + case 0x000f: return DRC_DWARFv3; + case 0x0010: return DRC_DWARFv3; + case 0x0011: return DRC_DWARFv3; + case 0x0012: return DRC_DWARFv3; + case 0x0013: return DRC_DWARFv3; + case 0x8000: return 0; + case 0x8001: return 0; + case 0x8765: return 0; + case 0xffff: return 0; + default: return 0; + } +} + +/* [7.13], "Address Class Encodings" (p. 144) in DWARFv3 draft 8 */ + +const char * +DW_ADDR_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "DW_ADDR_none"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ADDR constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_ADDR_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "none"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ADDR constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_ADDR_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0: return 0; + default: return 0; + } +} + +/* [7.14] Figure 28 "Identifier case encodings" (p. 144) in DWARFv3 draft 8 */ + +const char * +DW_ID_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "DW_ID_case_sensitive"; + case 0x1: return "DW_ID_up_case"; + case 0x2: return "DW_ID_down_case"; + case 0x3: return "DW_ID_case_insensitive"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ID constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_ID_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "case sensitive"; + case 0x1: return "up case"; + case 0x2: return "down case"; + case 0x3: return "case insensitive"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ID constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_ID_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0: return 0; + case 0x1: return 0; + case 0x2: return 0; + case 0x3: return 0; + default: return 0; + } +} + +/* [7.15] Figure 29 "Calling convention encodings" (p. 144) in DWARFv3 draft 8 */ + +const char * +DW_CC_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "DW_CC_normal"; + case 0x02: return "DW_CC_program"; + case 0x03: return "DW_CC_nocall"; + case 0x40: return "DW_CC_lo_user"; + case 0xff: return "DW_CC_hi_user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_CC constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_CC_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "normal"; + case 0x02: return "program"; + case 0x03: return "nocall"; + case 0x40: return "lo user"; + case 0xff: return "hi user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_CC constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_CC_value_to_class (uint32_t val) +{ + switch (val) { + case 0x01: return 0; + case 0x02: return 0; + case 0x03: return 0; + case 0x40: return 0; + case 0xff: return 0; + default: return 0; + } +} + +/* [7.16] Figure 30 "Inline encodings" (p. 145) in DWARFv3 draft 8 */ + +const char * +DW_INL_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "DW_INL_not_inlined"; + case 0x1: return "DW_INL_inlined"; + case 0x2: return "DW_INL_declared_not_inlined"; + case 0x3: return "DW_INL_declared_inlined"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_INL constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_INL_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "not inlined"; + case 0x1: return "inlined"; + case 0x2: return "declared not inlined"; + case 0x3: return "declared inlined"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_INL constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_INL_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0: return 0; + case 0x1: return 0; + case 0x2: return 0; + case 0x3: return 0; + default: return 0; + } +} + +/* [7.17] Figure 31 "Ordering encodings" (p. 145) in DWARFv3 draft 8 */ + +const char * +DW_ORD_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "DW_ORD_row_major"; + case 0x1: return "DW_ORD_col_major"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ORD constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_ORD_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "row major"; + case 0x1: return "col major"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_ORD constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_ORD_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0: return 0; + case 0x1: return 0; + default: return 0; + } +} + +/* [7.18] Figure 32 "Discriminant descriptor encodings" (p. 146) in DWARFv3 draft 8 */ + +const char * +DW_DSC_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "DW_DSC_label"; + case 0x1: return "DW_DSC_range"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_DSC constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_DSC_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x0: return "label"; + case 0x1: return "range"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_DSC constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_DSC_value_to_class (uint32_t val) +{ + switch (val) { + case 0x0: return 0; + case 0x1: return 0; + default: return 0; + } +} + +/* [7.21] Figure 33 "Line Number Standard Opcode Encodings" (pp. 148-149) in DWARFv3 draft 8 */ + +const char * +DW_LNS_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x1: return "DW_LNS_copy"; + case 0x2: return "DW_LNS_advance_pc"; + case 0x3: return "DW_LNS_advance_line"; + case 0x4: return "DW_LNS_set_file"; + case 0x5: return "DW_LNS_set_column"; + case 0x6: return "DW_LNS_negate_stmt"; + case 0x7: return "DW_LNS_set_basic_block"; + case 0x8: return "DW_LNS_const_add_pc"; + case 0x9: return "DW_LNS_fixed_advance_pc"; + case 0xa: return "DW_LNS_set_prologue_end"; + case 0xb: return "DW_LNS_set_epilogue_begin"; + case 0xc: return "DW_LNS_set_isa"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_LNS constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_LNS_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x1: return "copy"; + case 0x2: return "advance pc"; + case 0x3: return "advance line"; + case 0x4: return "set file"; + case 0x5: return "set column"; + case 0x6: return "negate stmt"; + case 0x7: return "set basic block"; + case 0x8: return "const add pc"; + case 0x9: return "fixed advance pc"; + case 0xa: return "set prologue end"; + case 0xb: return "set epilogue begin"; + case 0xc: return "set isa"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_LNS constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_LNS_value_to_class (uint32_t val) +{ + switch (val) { + case 0x1: return 0; + case 0x2: return 0; + case 0x3: return 0; + case 0x4: return 0; + case 0x5: return 0; + case 0x6: return 0; + case 0x7: return 0; + case 0x8: return 0; + case 0x9: return 0; + case 0xa: return DRC_DWARFv3; + case 0xb: return DRC_DWARFv3; + case 0xc: return DRC_DWARFv3; + default: return 0; + } +} + +/* [7.21] Figure 34 "Line Number Extended Opcode Encodings" (p. 149) in DWARFv3 draft 8 */ + +const char * +DW_LNE_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "DW_LNE_end_sequence"; + case 0x02: return "DW_LNE_set_address"; + case 0x03: return "DW_LNE_define_file"; + case 0x80: return "DW_LNE_lo_user"; + case 0xff: return "DW_LNE_hi_user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_LNE constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_LNE_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "end sequence"; + case 0x02: return "set address"; + case 0x03: return "define file"; + case 0x80: return "lo user"; + case 0xff: return "hi user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_LNE constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_LNE_value_to_class (uint32_t val) +{ + switch (val) { + case 0x01: return 0; + case 0x02: return 0; + case 0x03: return 0; + case 0x80: return DRC_DWARFv3; + case 0xff: return DRC_DWARFv3; + default: return 0; + } +} + +/* [7.22] Figure 35 "Macinfo Type Encodings" (p. 150) in DWARFv3 draft 8 */ + +const char * +DW_MACINFO_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "DW_MACINFO_define"; + case 0x02: return "DW_MACINFO_undef"; + case 0x03: return "DW_MACINFO_start_file"; + case 0x04: return "DW_MACINFO_end_file"; + case 0xff: return "DW_MACINFO_vendor_ext"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_MACINFO constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_MACINFO_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x01: return "define"; + case 0x02: return "undef"; + case 0x03: return "start file"; + case 0x04: return "end file"; + case 0xff: return "vendor ext"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_MACINFO constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_MACINFO_value_to_class (uint32_t val) +{ + switch (val) { + case 0x01: return 0; + case 0x02: return 0; + case 0x03: return 0; + case 0x04: return 0; + case 0xff: return 0; + default: return 0; + } +} + +/* [7.23] Figure 36 "Call frame instruction encodings" (pp. 151-152) in DWARFv3 draft 8 */ + +const char * +DW_CFA_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x40: return "DW_CFA_advance_loc"; + case 0x80: return "DW_CFA_offset"; + case 0xc0: return "DW_CFA_restore"; + case 0x00: return "DW_CFA_nop"; + case 0x01: return "DW_CFA_set_loc"; + case 0x02: return "DW_CFA_advance_loc1"; + case 0x03: return "DW_CFA_advance_loc2"; + case 0x04: return "DW_CFA_advance_loc4"; + case 0x05: return "DW_CFA_offset_extended"; + case 0x06: return "DW_CFA_restore_extended"; + case 0x07: return "DW_CFA_undefined"; + case 0x08: return "DW_CFA_same_value"; + case 0x09: return "DW_CFA_register"; + case 0x0a: return "DW_CFA_remember_state"; + case 0x0b: return "DW_CFA_restore_state"; + case 0x0c: return "DW_CFA_def_cfa"; + case 0x0d: return "DW_CFA_def_cfa_register"; + case 0x0e: return "DW_CFA_def_cfa_offset"; + case 0x0f: return "DW_CFA_def_cfa_expression"; + case 0x10: return "DW_CFA_expression"; + case 0x11: return "DW_CFA_offset_extended_sf"; + case 0x12: return "DW_CFA_def_cfa_sf"; + case 0x13: return "DW_CFA_def_cfa_offset_sf"; + case 0x1c: return "DW_CFA_lo_user"; + case 0x3f: return "DW_CFA_hi_user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_CFA constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_CFA_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x40: return "advance loc"; + case 0x80: return "offset"; + case 0xc0: return "restore"; + case 0x00: return "nop"; + case 0x01: return "set loc"; + case 0x02: return "advance loc1"; + case 0x03: return "advance loc2"; + case 0x04: return "advance loc4"; + case 0x05: return "offset extended"; + case 0x06: return "restore extended"; + case 0x07: return "undefined"; + case 0x08: return "same value"; + case 0x09: return "register"; + case 0x0a: return "remember state"; + case 0x0b: return "restore state"; + case 0x0c: return "def cfa"; + case 0x0d: return "def cfa register"; + case 0x0e: return "def cfa offset"; + case 0x0f: return "def cfa expression"; + case 0x10: return "expression"; + case 0x11: return "offset extended sf"; + case 0x12: return "def cfa sf"; + case 0x13: return "def cfa offset sf"; + case 0x1c: return "lo user"; + case 0x3f: return "hi user"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_CFA constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_CFA_value_to_class (uint32_t val) +{ + switch (val) { + case 0x40: return DRC_ZEROOPERANDS; + case 0x80: return DRC_ONEOPERAND | DRC_OPERANDONE_ULEB128_OFFSET; + case 0xc0: return DRC_ZEROOPERANDS; + case 0x00: return DRC_ZEROOPERANDS; + case 0x01: return DRC_ONEOPERAND | DRC_OPERANDONE_ADDRESS; + case 0x02: return DRC_ONEOPERAND | DRC_OPERANDONE_1BYTE_DELTA; + case 0x03: return DRC_ONEOPERAND | DRC_OPERANDONE_2BYTE_DELTA; + case 0x04: return DRC_ONEOPERAND | DRC_OPERANDONE_4BYTE_DELTA; + case 0x05: return DRC_OPERANDTWO_ULEB128_OFFSET | DRC_OPERNADONE_ULEB128_REGISTER | DRC_TWOOPERANDS; + case 0x06: return DRC_ONEOPERAND | DRC_OPERANDONE_ULEB128_REGISTER; + case 0x07: return DRC_ONEOPERAND | DRC_OPERANDONE_ULEB128_REGISTER; + case 0x08: return DRC_ONEOPERAND | DRC_OPERANDONE_ULEB128_REGISTER; + case 0x09: return DRC_OPERANDONE_ULEB128_REGISTER | DRC_OPERANDTWO_ULEB128_REGISTER | DRC_TWOOPERANDS; + case 0x0a: return DRC_ZEROOPERANDS; + case 0x0b: return DRC_ZEROOPERANDS; + case 0x0c: return DRC_OPERANDONE_ULEB128_REGISTER | DRC_OPERANDTWO_ULEB128_OFFSET | DRC_TWOOPERANDS; + case 0x0d: return DRC_ONEOPERAND | DRC_OPERANDONE_ULEB128_REGISTER; + case 0x0e: return DRC_ONEOPERAND | DRC_OPERANDONE_ULEB128_OFFSET; + case 0x0f: return DRC_DWARFv3 | DRC_ONEOPERAND | DRC_OPERANDONE_BLOCK; + case 0x10: return DRC_DWARFv3 | DRC_OPERANDONE_ULEB128_REGISTER | DRC_OPERANDTWO_BLOCK | DRC_TWOOPERANDS; + case 0x11: return DRC_DWARFv3 | DRC_OPERANDONE_ULEB128_REGISTER | DRC_OPERANDTWO_SLEB128_OFFSET | DRC_TWOOPERANDS; + case 0x12: return DRC_DWARFv3 | DRC_OPERANDONE_ULEB128_REGISTER | DRC_OPERANDTWO_SLEB128_OFFSET | DRC_TWOOPERANDS; + case 0x13: return DRC_DWARFv3 | DRC_ONEOPERAND | DRC_OPERANDONE_SLEB128_OFFSET; + case 0x1c: return 0; + case 0x3f: return 0; + default: return 0; + } +} + +/* FSF exception handling Pointer-Encoding constants (CFI augmentation) -- "DW_EH_PE_..." in the FSF sources */ + +const char * +DW_GNU_EH_PE_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x00: return "DW_GNU_EH_PE_absptr"; + case 0x01: return "DW_GNU_EH_PE_uleb128"; + case 0x02: return "DW_GNU_EH_PE_udata2"; + case 0x03: return "DW_GNU_EH_PE_udata4"; + case 0x04: return "DW_GNU_EH_PE_udata8"; + case 0x09: return "DW_GNU_EH_PE_sleb128"; + case 0x0a: return "DW_GNU_EH_PE_sdata2"; + case 0x0b: return "DW_GNU_EH_PE_sdata4"; + case 0x0c: return "DW_GNU_EH_PE_sdata8"; + case 0x08: return "DW_GNU_EH_PE_signed"; + case 0x10: return "DW_GNU_EH_PE_pcrel"; + case 0x20: return "DW_GNU_EH_PE_textrel"; + case 0x30: return "DW_GNU_EH_PE_datarel"; + case 0x40: return "DW_GNU_EH_PE_funcrel"; + case 0x50: return "DW_GNU_EH_PE_aligned"; + case 0x80: return "DW_GNU_EH_PE_indirect"; + case 0xff: return "DW_GNU_EH_PE_omit"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_GNU_EH_PE constant: 0x%x", val); + return invalid; + } +} + +const char * +DW_GNU_EH_PE_value_to_englishy_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x00: return "absptr"; + case 0x01: return "uleb128"; + case 0x02: return "udata2"; + case 0x03: return "udata4"; + case 0x04: return "udata8"; + case 0x09: return "sleb128"; + case 0x0a: return "sdata2"; + case 0x0b: return "sdata4"; + case 0x0c: return "sdata8"; + case 0x08: return "signed"; + case 0x10: return "pcrel"; + case 0x20: return "textrel"; + case 0x30: return "datarel"; + case 0x40: return "funcrel"; + case 0x50: return "aligned"; + case 0x80: return "indirect"; + case 0xff: return "omit"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_GNU_EH_PE constant: 0x%x", val); + return invalid; + } +} + +DRC_class +DW_GNU_EH_PE_value_to_class (uint32_t val) +{ + switch (val) { + case 0x00: return DRC_VENDOR_GNU; + case 0x01: return DRC_VENDOR_GNU; + case 0x02: return DRC_VENDOR_GNU; + case 0x03: return DRC_VENDOR_GNU; + case 0x04: return DRC_VENDOR_GNU; + case 0x09: return DRC_VENDOR_GNU; + case 0x0a: return DRC_VENDOR_GNU; + case 0x0b: return DRC_VENDOR_GNU; + case 0x0c: return DRC_VENDOR_GNU; + case 0x08: return DRC_VENDOR_GNU; + case 0x10: return DRC_VENDOR_GNU; + case 0x20: return DRC_VENDOR_GNU; + case 0x30: return DRC_VENDOR_GNU; + case 0x40: return DRC_VENDOR_GNU; + case 0x50: return DRC_VENDOR_GNU; + case 0x80: return DRC_VENDOR_GNU; + case 0xff: return DRC_VENDOR_GNU; + default: return 0; + } +} + +bool +is_type_tag (uint16_t tag) +{ + switch (tag) + { + case DW_TAG_array_type: + case DW_TAG_base_type: + case DW_TAG_class_type: + case DW_TAG_const_type: + case DW_TAG_enumeration_type: + case DW_TAG_file_type: + case DW_TAG_interface_type: + case DW_TAG_packed_type: + case DW_TAG_pointer_type: + case DW_TAG_ptr_to_member_type: + case DW_TAG_reference_type: + case DW_TAG_restrict_type: + case DW_TAG_set_type: + case DW_TAG_shared_type: + case DW_TAG_string_type: + case DW_TAG_structure_type: + case DW_TAG_subrange_type: + case DW_TAG_subroutine_type: + case DW_TAG_thrown_type: + case DW_TAG_union_type: + case DW_TAG_unspecified_type: + case DW_TAG_volatile_type: + return true; + default: + return false; + } +} + +bool +is_pubtype_tag (uint16_t tag) +{ + switch (tag) + { + case DW_TAG_array_type: + case DW_TAG_class_type: + case DW_TAG_enumeration_type: + case DW_TAG_file_type: + case DW_TAG_interface_type: + case DW_TAG_set_type: + case DW_TAG_string_type: + case DW_TAG_structure_type: + case DW_TAG_subrange_type: + case DW_TAG_subroutine_type: + case DW_TAG_thrown_type: + case DW_TAG_typedef: + case DW_TAG_union_type: + case DW_TAG_unspecified_type: + return true; + default: + break; + } + return false; +} + +DW_TAG_CategoryEnum +get_tag_category (uint16_t tag) +{ + switch (tag) + { + case DW_TAG_array_type : return TagCategoryType; + case DW_TAG_class_type : return TagCategoryType; + case DW_TAG_entry_point : return TagCategoryProgram; + case DW_TAG_enumeration_type : return TagCategoryType; + case DW_TAG_formal_parameter : return TagCategoryVariable; + case DW_TAG_imported_declaration : return TagCategoryProgram; + case DW_TAG_label : return TagCategoryProgram; + case DW_TAG_lexical_block : return TagCategoryProgram; + case DW_TAG_member : return TagCategoryType; + case DW_TAG_pointer_type : return TagCategoryType; + case DW_TAG_reference_type : return TagCategoryType; + case DW_TAG_compile_unit : return TagCategoryProgram; + case DW_TAG_string_type : return TagCategoryType; + case DW_TAG_structure_type : return TagCategoryType; + case DW_TAG_subroutine_type : return TagCategoryType; + case DW_TAG_typedef : return TagCategoryType; + case DW_TAG_union_type : return TagCategoryType; + case DW_TAG_unspecified_parameters : return TagCategoryVariable; + case DW_TAG_variant : return TagCategoryType; + case DW_TAG_common_block : return TagCategoryProgram; + case DW_TAG_common_inclusion : return TagCategoryProgram; + case DW_TAG_inheritance : return TagCategoryType; + case DW_TAG_inlined_subroutine : return TagCategoryProgram; + case DW_TAG_module : return TagCategoryProgram; + case DW_TAG_ptr_to_member_type : return TagCategoryType; + case DW_TAG_set_type : return TagCategoryType; + case DW_TAG_subrange_type : return TagCategoryType; + case DW_TAG_with_stmt : return TagCategoryProgram; + case DW_TAG_access_declaration : return TagCategoryProgram; + case DW_TAG_base_type : return TagCategoryType; + case DW_TAG_catch_block : return TagCategoryProgram; + case DW_TAG_const_type : return TagCategoryType; + case DW_TAG_constant : return TagCategoryVariable; + case DW_TAG_enumerator : return TagCategoryType; + case DW_TAG_file_type : return TagCategoryType; + case DW_TAG_friend : return TagCategoryType; + case DW_TAG_namelist : return TagCategoryVariable; + case DW_TAG_namelist_item : return TagCategoryVariable; + case DW_TAG_packed_type : return TagCategoryType; + case DW_TAG_subprogram : return TagCategoryProgram; + case DW_TAG_template_type_parameter : return TagCategoryType; + case DW_TAG_template_value_parameter : return TagCategoryType; + case DW_TAG_thrown_type : return TagCategoryType; + case DW_TAG_try_block : return TagCategoryProgram; + case DW_TAG_variant_part : return TagCategoryType; + case DW_TAG_variable : return TagCategoryVariable; + case DW_TAG_volatile_type : return TagCategoryType; + case DW_TAG_dwarf_procedure : return TagCategoryProgram; + case DW_TAG_restrict_type : return TagCategoryType; + case DW_TAG_interface_type : return TagCategoryType; + case DW_TAG_namespace : return TagCategoryProgram; + case DW_TAG_imported_module : return TagCategoryProgram; + case DW_TAG_unspecified_type : return TagCategoryType; + case DW_TAG_partial_unit : return TagCategoryProgram; + case DW_TAG_imported_unit : return TagCategoryProgram; + case DW_TAG_shared_type : return TagCategoryType; + default: break; + } + return TagCategoryProgram; +} + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h new file mode 100644 index 00000000000..dafe8a7c8b4 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h @@ -0,0 +1,252 @@ +//===-- DWARFDefines.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_DWARFDefines_h_ +#define liblldb_DWARFDefines_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <stdbool.h> +#include "lldb/Core/dwarf.h" + +/* DWARF constants generated on Wed Sep 7 16:41:50 2005 */ + +typedef uint32_t DRC_class; // Holds DRC_* class bitfields + +/* [7.5.4] Figure 16 "Tag Encodings" (pp. 125-127) in DWARFv3 draft 8 */ + + +enum DW_TAG_Category +{ + TagCategoryVariable, + TagCategoryType, + TagCategoryProgram, + kNumTagCategories +}; + +typedef enum DW_TAG_Category DW_TAG_CategoryEnum; +const char *DW_TAG_value_to_name (uint32_t val); +const char *DW_TAG_value_to_englishy_name (uint32_t val); +DRC_class DW_TAG_value_to_class (uint32_t val); +DW_TAG_CategoryEnum get_tag_category (uint16_t tag); +#define DW_TAG_MAX_NAME_LENGTH 31 + + +/* [7.5.4] Figure 17 "Child determination encodings" (p. 128) in DWARFv3 draft 8 */ + +const char *DW_CHILDREN_value_to_name (uint8_t val); +const char *DW_CHILDREN_value_to_englishy_name (uint8_t val); +DRC_class DW_CHILDREN_value_to_class (uint32_t val); +#define DW_CHILDREN_MAX_NAME_LENGTH 15 + + +/* [7.5.4] Figure 18 "Attribute encodings" (pp. 129-132) in DWARFv3 draft 8 */ + + +const char *DW_AT_value_to_name (uint32_t val); +const char *DW_AT_value_to_englishy_name (uint32_t val); +DRC_class DW_AT_value_to_class (uint32_t val); +#define DW_AT_MAX_NAME_LENGTH 34 + + +/* [7.5.4] Figure 19 "Attribute form encodings" (pp. 133-134) in DWARFv3 draft 8 */ + +const char *DW_FORM_value_to_name (uint32_t val); +const char *DW_FORM_value_to_englishy_name (uint32_t val); +DRC_class DW_FORM_value_to_class (uint32_t val); +#define DW_FORM_MAX_NAME_LENGTH 17 + + +/* [7.7.1] Figure 22 "DWARF operation encodings" (pp. 136-139) in DWARFv3 draft 8 */ + +const char *DW_OP_value_to_name (uint32_t val); +const char *DW_OP_value_to_englishy_name (uint32_t val); +DRC_class DW_OP_value_to_class (uint32_t val); +#define DW_OP_MAX_NAME_LENGTH 25 + + +/* [7.8] Figure 23 "Base type encoding values" (pp. 140-141) in DWARFv3 draft 8 */ + +const char *DW_ATE_value_to_name (uint32_t val); +const char *DW_ATE_value_to_englishy_name (uint32_t val); +DRC_class DW_ATE_value_to_class (uint32_t val); +#define DW_ATE_MAX_NAME_LENGTH 22 + + +/* [7.9] Figure 24 "Accessibility encodings" (p. 141) in DWARFv3 draft 8 */ + +const char *DW_ACCESS_value_to_name (uint32_t val); +const char *DW_ACCESS_value_to_englishy_name (uint32_t val); +DRC_class DW_ACCESS_value_to_class (uint32_t val); +#define DW_ACCESS_MAX_NAME_LENGTH 19 + + +/* [7.10] Figure 25 "Visibility encodings" (p. 142) in DWARFv3 draft 8 */ + +const char *DW_VIS_value_to_name (uint32_t val); +const char *DW_VIS_value_to_englishy_name (uint32_t val); +DRC_class DW_VIS_value_to_class (uint32_t val); +#define DW_VIS_MAX_NAME_LENGTH 16 + + +/* [7.11] Figure 26 "Virtuality encodings" (p. 142) in DWARFv3 draft 8 */ + +const char *DW_VIRTUALITY_value_to_name (uint32_t val); +const char *DW_VIRTUALITY_value_to_englishy_name (uint32_t val); +DRC_class DW_VIRTUALITY_value_to_class (uint32_t val); +#define DW_VIRTUALITY_MAX_NAME_LENGTH 26 + + +/* [7.12] Figure 27 "Language encodings" (p. 143) in DWARFv3 draft 8 */ + +const char *DW_LANG_value_to_name (uint32_t val); +const char *DW_LANG_value_to_englishy_name (uint32_t val); +DRC_class DW_LANG_value_to_class (uint32_t val); +#define DW_LANG_MAX_NAME_LENGTH 19 + + +/* [7.13], "Address Class Encodings" (p. 144) in DWARFv3 draft 8 */ + +const char *DW_ADDR_value_to_name (uint32_t val); +const char *DW_ADDR_value_to_englishy_name (uint32_t val); +DRC_class DW_ADDR_value_to_class (uint32_t val); +#define DW_ADDR_MAX_NAME_LENGTH 12 + + +/* [7.14] Figure 28 "Identifier case encodings" (p. 144) in DWARFv3 draft 8 */ + +const char *DW_ID_value_to_name (uint32_t val); +const char *DW_ID_value_to_englishy_name (uint32_t val); +DRC_class DW_ID_value_to_class (uint32_t val); +#define DW_ID_MAX_NAME_LENGTH 22 + + +/* [7.15] Figure 29 "Calling convention encodings" (p. 144) in DWARFv3 draft 8 */ + +const char *DW_CC_value_to_name (uint32_t val); +const char *DW_CC_value_to_englishy_name (uint32_t val); +DRC_class DW_CC_value_to_class (uint32_t val); +#define DW_CC_MAX_NAME_LENGTH 13 + + +/* [7.16] Figure 30 "Inline encodings" (p. 145) in DWARFv3 draft 8 */ + +const char *DW_INL_value_to_name (uint32_t val); +const char *DW_INL_value_to_englishy_name (uint32_t val); +DRC_class DW_INL_value_to_class (uint32_t val); +#define DW_INL_MAX_NAME_LENGTH 27 + + +/* [7.17] Figure 31 "Ordering encodings" (p. 145) in DWARFv3 draft 8 */ + +const char *DW_ORD_value_to_name (uint32_t val); +const char *DW_ORD_value_to_englishy_name (uint32_t val); +DRC_class DW_ORD_value_to_class (uint32_t val); +#define DW_ORD_MAX_NAME_LENGTH 16 + + +/* [7.18] Figure 32 "Discriminant descriptor encodings" (p. 146) in DWARFv3 draft 8 */ + +const char *DW_DSC_value_to_name (uint32_t val); +const char *DW_DSC_value_to_englishy_name (uint32_t val); +DRC_class DW_DSC_value_to_class (uint32_t val); +#define DW_DSC_MAX_NAME_LENGTH 12 + + +/* [7.21] Figure 33 "Line Number Standard Opcode Encodings" (pp. 148-149) in DWARFv3 draft 8 */ + +const char *DW_LNS_value_to_name (uint32_t val); +const char *DW_LNS_value_to_englishy_name (uint32_t val); +DRC_class DW_LNS_value_to_class (uint32_t val); +#define DW_LNS_MAX_NAME_LENGTH 25 + + +/* [7.21] Figure 34 "Line Number Extended Opcode Encodings" (p. 149) in DWARFv3 draft 8 */ + +const char *DW_LNE_value_to_name (uint32_t val); +const char *DW_LNE_value_to_englishy_name (uint32_t val); +DRC_class DW_LNE_value_to_class (uint32_t val); +#define DW_LNE_MAX_NAME_LENGTH 19 + + +/* [7.22] Figure 35 "Macinfo Type Encodings" (p. 150) in DWARFv3 draft 8 */ + +const char *DW_MACINFO_value_to_name (uint32_t val); +const char *DW_MACINFO_value_to_englishy_name (uint32_t val); +DRC_class DW_MACINFO_value_to_class (uint32_t val); +#define DW_MACINFO_MAX_NAME_LENGTH 21 + + +/* [7.23] Figure 36 "Call frame instruction encodings" (pp. 151-152) in DWARFv3 draft 8 */ + +const char *DW_CFA_value_to_name (uint32_t val); +const char *DW_CFA_value_to_englishy_name (uint32_t val); +DRC_class DW_CFA_value_to_class (uint32_t val); +#define DW_CFA_MAX_NAME_LENGTH 25 + + +/* FSF exception handling Pointer-Encoding constants (CFI augmentation) -- "DW_EH_PE_..." in the FSF sources */ + +const char *DW_GNU_EH_PE_value_to_name (uint32_t val); +const char *DW_GNU_EH_PE_value_to_englishy_name (uint32_t val); +DRC_class DW_GNU_EH_PE_value_to_class (uint32_t val); +#define DW_GNU_EH_PE_MAX_NAME_LENGTH 21 + + +/* These DRC are entirely our own construction, + although they are derived from various comments in the DWARF standard. + Most of these are not useful to the parser, but the DW_AT and DW_FORM + classes should prove to be usable in some fashion. */ + +#define DRC_0x65 0x1 +#define DRC_ADDRESS 0x2 +#define DRC_BLOCK 0x4 +#define DRC_CONSTANT 0x8 +#define DRC_DWARFv3 0x10 +#define DRC_FLAG 0x20 +#define DRC_INDIRECT_SPECIAL 0x40 +#define DRC_LINEPTR 0x80 +#define DRC_LOCEXPR 0x100 +#define DRC_LOCLISTPTR 0x200 +#define DRC_MACPTR 0x400 +#define DRC_ONEOPERAND 0x800 +#define DRC_OPERANDONE_1BYTE_DELTA 0x1000 +#define DRC_OPERANDONE_2BYTE_DELTA 0x2000 +#define DRC_OPERANDONE_4BYTE_DELTA 0x4000 +#define DRC_OPERANDONE_ADDRESS 0x8000 +#define DRC_OPERANDONE_BLOCK 0x10000 +#define DRC_OPERANDONE_SLEB128_OFFSET 0x20000 +#define DRC_OPERANDONE_ULEB128_OFFSET 0x40000 +#define DRC_OPERANDONE_ULEB128_REGISTER 0x80000 +#define DRC_OPERANDTWO_BLOCK 0x100000 +#define DRC_OPERANDTWO_SLEB128_OFFSET 0x200000 +#define DRC_OPERANDTWO_ULEB128_OFFSET 0x400000 +#define DRC_OPERANDTWO_ULEB128_REGISTER 0x800000 +#define DRC_OPERNADONE_ULEB128_REGISTER 0x1000000 +#define DRC_RANGELISTPTR 0x2000000 +#define DRC_REFERENCE 0x4000000 +#define DRC_STRING 0x8000000 +#define DRC_TWOOPERANDS 0x10000000 +#define DRC_VENDOR_GNU 0x20000000 +#define DRC_VENDOR_MIPS 0x40000000 +#define DRC_ZEROOPERANDS 0x80000000 + +bool is_type_tag (uint16_t tag); +bool is_pubtype_tag (uint16_t tag); + + +#ifdef __cplusplus +} +#endif + + +#endif // liblldb_DWARFDefines_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp new file mode 100644 index 00000000000..d2c137bd7df --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp @@ -0,0 +1,571 @@ +//===-- DWARFFormValue.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <assert.h> + +#include "lldb/Core/dwarf.h" +#include "lldb/Core/Stream.h" + +#include "DWARFFormValue.h" +#include "DWARFCompileUnit.h" + +class DWARFCompileUnit; + +using namespace lldb_private; + +DWARFFormValue::DWARFFormValue(dw_form_t form) : + m_form(form), + m_value() +{ +} + +bool +DWARFFormValue::ExtractValue(const DataExtractor& data, uint32_t* offset_ptr, const DWARFCompileUnit* cu) +{ + bool indirect = false; + bool is_block = false; + m_value.data = NULL; + // Read the value for the form into value and follow and DW_FORM_indirect instances we run into + do + { + indirect = false; + switch (m_form) + { + case DW_FORM_addr: m_value.value.uval = data.GetMaxU64(offset_ptr, DWARFCompileUnit::GetAddressByteSize(cu)); break; + case DW_FORM_block2: m_value.value.uval = data.GetU16(offset_ptr); is_block = true; break; + case DW_FORM_block4: m_value.value.uval = data.GetU32(offset_ptr); is_block = true; break; + case DW_FORM_data2: m_value.value.uval = data.GetU16(offset_ptr); break; + case DW_FORM_data4: m_value.value.uval = data.GetU32(offset_ptr); break; + case DW_FORM_data8: m_value.value.uval = data.GetU64(offset_ptr); break; + case DW_FORM_string: m_value.value.cstr = data.GetCStr(offset_ptr); + // Set the string value to also be the data for inlined cstr form values only + // so we can tell the differnence between DW_FORM_string and DW_FORM_strp form + // values; + m_value.data = (uint8_t*)m_value.value.cstr; break; + case DW_FORM_block: m_value.value.uval = data.GetULEB128(offset_ptr); is_block = true; break; + case DW_FORM_block1: m_value.value.uval = data.GetU8(offset_ptr); is_block = true; break; + case DW_FORM_data1: m_value.value.uval = data.GetU8(offset_ptr); break; + case DW_FORM_flag: m_value.value.uval = data.GetU8(offset_ptr); break; + case DW_FORM_sdata: m_value.value.sval = data.GetSLEB128(offset_ptr); break; + case DW_FORM_strp: m_value.value.uval = data.GetU32(offset_ptr); break; + // case DW_FORM_APPLE_db_str: + case DW_FORM_udata: m_value.value.uval = data.GetULEB128(offset_ptr); break; + case DW_FORM_ref_addr: m_value.value.uval = data.GetMaxU64(offset_ptr, DWARFCompileUnit::GetAddressByteSize(cu)); break; + case DW_FORM_ref1: m_value.value.uval = data.GetU8(offset_ptr); break; + case DW_FORM_ref2: m_value.value.uval = data.GetU16(offset_ptr); break; + case DW_FORM_ref4: m_value.value.uval = data.GetU32(offset_ptr); break; + case DW_FORM_ref8: m_value.value.uval = data.GetU64(offset_ptr); break; + case DW_FORM_ref_udata: m_value.value.uval = data.GetULEB128(offset_ptr); break; + case DW_FORM_indirect: + m_form = data.GetULEB128(offset_ptr); + indirect = true; + break; + + default: + return false; + break; + } + } while (indirect); + + if (is_block) + { + m_value.data = data.PeekData(*offset_ptr, m_value.value.uval); + if (m_value.data != NULL) + { + *offset_ptr += m_value.value.uval; + } + } + + return true; +} + +bool +DWARFFormValue::SkipValue(const DataExtractor& debug_info_data, uint32_t* offset_ptr, const DWARFCompileUnit* cu) const +{ + return DWARFFormValue::SkipValue(m_form, debug_info_data, offset_ptr, cu); +} + +bool +DWARFFormValue::SkipValue(dw_form_t form, const DataExtractor& debug_info_data, uint32_t* offset_ptr, const DWARFCompileUnit* cu) +{ + bool indirect = false; + do + { + indirect = false; + switch (form) + { + // Blocks if inlined data that have a length field and the data bytes + // inlined in the .debug_info + case DW_FORM_block : { dw_uleb128_t size = debug_info_data.GetULEB128(offset_ptr); *offset_ptr += size; } return true; + case DW_FORM_block1 : { dw_uleb128_t size = debug_info_data.GetU8(offset_ptr); *offset_ptr += size; } return true; + case DW_FORM_block2 : { dw_uleb128_t size = debug_info_data.GetU16(offset_ptr); *offset_ptr += size; } return true; + case DW_FORM_block4 : { dw_uleb128_t size = debug_info_data.GetU32(offset_ptr); *offset_ptr += size; } return true; + + // Inlined NULL terminated C-strings + case DW_FORM_string : + debug_info_data.GetCStr(offset_ptr); + return true; + + // Compile unit address sized values + case DW_FORM_addr : + case DW_FORM_ref_addr : + *offset_ptr += DWARFCompileUnit::GetAddressByteSize(cu); + return true; + + // 1 byte values + case DW_FORM_data1 : + case DW_FORM_flag : + case DW_FORM_ref1 : + *offset_ptr += 1; + return true; + + // 2 byte values + case DW_FORM_data2 : + case DW_FORM_ref2 : + *offset_ptr += 2; + return true; + + // 4 byte values + case DW_FORM_strp : + case DW_FORM_data4 : + case DW_FORM_ref4 : + *offset_ptr += 4; + return true; + + // 8 byte values + case DW_FORM_data8 : + case DW_FORM_ref8 : + *offset_ptr += 8; + return true; + + // signed or unsigned LEB 128 values + // case DW_FORM_APPLE_db_str: + case DW_FORM_sdata : + case DW_FORM_udata : + case DW_FORM_ref_udata : + debug_info_data.Skip_LEB128(offset_ptr); + return true; + + case DW_FORM_indirect : + indirect = true; + form = debug_info_data.GetULEB128(offset_ptr); + break; + default: + return false; + } + } while (indirect); + return true; +} + +//bool +//DWARFFormValue::PutUnsigned(dw_form_t form, dw_offset_t offset, uint64_t value, BinaryStreamBuf& out_buff, const DWARFCompileUnit* cu, bool fixup_cu_relative_refs) +//{ +// assert(offset != DW_INVALID_OFFSET); +//// printf("PutUnsigned(%s, 0x%8.8x, 0x%16.16llx, %d)\n", DW_FORM_value_to_name(form), offset, value, fixup_cu_relative_refs); +// // Read the value for the form into value and follow and DW_FORM_indirect instances we run into +// switch (form) +// { +// case DW_FORM_addr: offset = out_buff.PutMax64(offset, value, DWARFCompileUnit::GetAddressByteSize(cu)); break; +// +// case DW_FORM_flag: +// case DW_FORM_data1: offset = out_buff.Put8(offset, value); break; +// case DW_FORM_data2: offset = out_buff.Put16(offset, value); break; +// case DW_FORM_data4: offset = out_buff.Put32(offset, value); break; +// case DW_FORM_data8: offset = out_buff.Put64(offset, value); break; +//// case DW_FORM_udata: offset = out_buff.Put32_as_ULEB128(offset, value); break; +//// case DW_FORM_sdata: offset = out_buff.Put32_as_SLEB128(offset, value); break; +// case DW_FORM_strp: offset = out_buff.Put32(offset, value); break; +//// case DW_FORM_APPLE_db_str: +//// offset = out_buff.Put32_as_ULEB128(offset, value); break; +// +// case DW_FORM_ref1: +// if (fixup_cu_relative_refs) value -= cu->GetOffset(); +// offset = out_buff.Put8(offset, value); +// break; +// case DW_FORM_ref2: +// if (fixup_cu_relative_refs) value -= cu->GetOffset(); +// offset = out_buff.Put16(offset, value); +// break; +// case DW_FORM_ref4: +// if (fixup_cu_relative_refs) value -= cu->GetOffset(); +// offset = out_buff.Put32(offset, value); +// break; +// case DW_FORM_ref8: +// if (fixup_cu_relative_refs) value -= cu->GetOffset(); +// offset = out_buff.Put64(offset, value); +// break; +//// case DW_FORM_ref_udata: +//// if (fixup_cu_relative_refs) value -= cu->GetOffset(); +//// offset = out_buff.Put32_as_ULEB128(offset, value); +//// break; +// case DW_FORM_ref_addr: +// // TODO: Add support for DWARF3 if we ever start emitting DWARF3. The DW_FORM_ref_addr +// // is always the same size as an address prior to DWARF3, and with DWARF3 or later it +// // is 4 hard coded to bytes. +// offset = out_buff.PutMax64(offset, value, DWARFCompileUnit::GetAddressByteSize(cu)); +// break; +// +// default: +// return false; +// } +// +// return true; +//} + +//bool +//DWARFFormValue::TransferValue(dw_form_t form, const DataExtractor& data, uint32_t* offset_ptr, const DWARFCompileUnit* cu, BinaryStreamBuf& out_buff) +//{ +// DWARFFormValue formValue(form); +// if (formValue.ExtractValue(data, offset_ptr,cu)) +// return TransferValue(formValue, cu, out_buff); +// return false; +//} + +//bool +//DWARFFormValue::TransferValue(const DWARFFormValue& formValue, const DWARFCompileUnit* cu, BinaryStreamBuf& out_buff) +//{ +// // Read the value for the form into value and follow and DW_FORM_indirect instances we run into +// dw_form_t form = formValue.Form(); +// switch (form) +// { +// case DW_FORM_addr: +// case DW_FORM_ref_addr: +// { +// uint8_t addr_size = DWARFCompileUnit::GetAddressByteSize(cu); +// out_buff.AppendMax64(formValue.Unsigned(), addr_size); +// } +// break; +// +// case DW_FORM_block: out_buff.Append32_as_ULEB128(formValue.Unsigned()); break; +// case DW_FORM_block1: out_buff.Append8(formValue.Unsigned()); break; +// case DW_FORM_block2: out_buff.Append16(formValue.Unsigned()); break; +// case DW_FORM_block4: out_buff.Append32(formValue.Unsigned()); break; +// +// case DW_FORM_flag: +// case DW_FORM_data1: out_buff.Append8(formValue.Unsigned()); break; +// case DW_FORM_data2: out_buff.Append16(formValue.Unsigned()); break; +// case DW_FORM_data4: out_buff.Append32(formValue.Unsigned()); break; +// case DW_FORM_data8: out_buff.Append64(formValue.Unsigned()); break; +// case DW_FORM_udata: out_buff.Append32_as_ULEB128(formValue.Unsigned()); break; +// case DW_FORM_sdata: out_buff.Append32_as_SLEB128(formValue.Signed()); break; +// +// case DW_FORM_string: out_buff.AppendCStr(formValue.m_value.value.cstr); break; +// case DW_FORM_strp: out_buff.Append32(formValue.Unsigned()); break; +//// case DW_FORM_APPLE_db_str: +//// out_buff.Append32_as_ULEB128(formValue.Unsigned()); break; +// +// case DW_FORM_ref1: out_buff.Append8(formValue.Unsigned()); break; +// case DW_FORM_ref2: out_buff.Append16(formValue.Unsigned()); break; +// case DW_FORM_ref4: out_buff.Append32(formValue.Unsigned()); break; +// case DW_FORM_ref8: out_buff.Append64(formValue.Unsigned()); break; +// case DW_FORM_ref_udata: out_buff.Append32_as_ULEB128(formValue.Unsigned()); break; +// +// case DW_FORM_indirect: +// assert(!"DW_FORM_indirect found in DWARFFormValue::TransferValue() for an extracted form..."); +// break; +// +// default: +// Log::Error("DWARFFormValue::TransferValue() Unrecognized form: 0x%4.4x", form); +// return false; +// break; +// } +// +// const uint8_t* block_data = formValue.BlockData(); +// if (block_data) +// out_buff.AppendData(block_data, formValue.Unsigned()); +// return true; +//} + +void +DWARFFormValue::Dump(Stream *s, const DataExtractor* debug_str_data, const DWARFCompileUnit* cu) const +{ + uint64_t uvalue = Unsigned(); + bool cu_relative_offset = false; + + bool verbose = s->GetVerbose(); + + switch (m_form) + { + case DW_FORM_addr: s->Address(uvalue, sizeof (uint64_t)); break; + case DW_FORM_flag: + case DW_FORM_data1: s->PutHex8(uvalue); break; + case DW_FORM_data2: s->PutHex16(uvalue); break; + case DW_FORM_data4: s->PutHex32(uvalue); break; + case DW_FORM_data8: s->PutHex64(uvalue); break; + case DW_FORM_string: s->QuotedCString(AsCString(NULL)); break; + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + if (uvalue > 0) + { + switch (m_form) + { + case DW_FORM_block: s->Printf("<0x%llx> ", uvalue); break; + case DW_FORM_block1: s->Printf("<0x%2.2x> ", (uint8_t)uvalue); break; + case DW_FORM_block2: s->Printf("<0x%4.4x> ", (uint16_t)uvalue); break; + case DW_FORM_block4: s->Printf("<0x%8.8x> ", (uint32_t)uvalue); break; + default: break; + } + + const uint8_t* data_ptr = m_value.data; + if (data_ptr) + { + const uint8_t* end_data_ptr = data_ptr + uvalue; // uvalue contains size of block + while (data_ptr < end_data_ptr) + { + s->Printf("%2.2x ", *data_ptr); + ++data_ptr; + } + } + else + s->PutCString("NULL"); + } + break; + + case DW_FORM_sdata: s->PutSLEB128(uvalue); break; + case DW_FORM_udata: s->PutULEB128(uvalue); break; + case DW_FORM_strp: + if (debug_str_data) + { + if (verbose) + s->Printf(" .debug_str[0x%8.8x] = ", (uint32_t)uvalue); + + const char* dbg_str = AsCString(debug_str_data); + if (dbg_str) + s->QuotedCString(dbg_str); + } + else + { + s->PutHex32(uvalue); + } + break; + + case DW_FORM_ref_addr: + { + s->Address(uvalue, sizeof (uint64_t) * 2); + break; + } + case DW_FORM_ref1: cu_relative_offset = true; if (verbose) s->Printf("cu + 0x%2.2x", (uint8_t)uvalue); break; + case DW_FORM_ref2: cu_relative_offset = true; if (verbose) s->Printf("cu + 0x%4.4x", (uint16_t)uvalue); break; + case DW_FORM_ref4: cu_relative_offset = true; if (verbose) s->Printf("cu + 0x%4.4x", (uint32_t)uvalue); break; + case DW_FORM_ref8: cu_relative_offset = true; if (verbose) s->Printf("cu + 0x%8.8llx", uvalue); break; + case DW_FORM_ref_udata: cu_relative_offset = true; if (verbose) s->Printf("cu + 0x%llx", uvalue); break; + + // All DW_FORM_indirect attributes should be resolved prior to calling this function + case DW_FORM_indirect: s->PutCString("DW_FORM_indirect"); break; + default: + s->Printf("DW_FORM(0x%4.4x)", m_form); + break; + } + + if (cu_relative_offset) + { + if (verbose) + s->PutCString(" => "); + + s->Printf("{0x%8.8x}", (uvalue + (cu ? cu->GetOffset() : 0))); + } +} + +const char* +DWARFFormValue::AsCString(const DataExtractor* debug_str_data_ptr) const +{ + if (IsInlinedCStr()) + return m_value.value.cstr; + else if (debug_str_data_ptr) + return debug_str_data_ptr->PeekCStr(m_value.value.uval); + return NULL; +} + +uint64_t +DWARFFormValue::Reference(const DWARFCompileUnit* cu) const +{ + uint64_t die_offset = m_value.value.uval; + switch (m_form) + { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + die_offset += (cu ? cu->GetOffset() : 0); + break; + + default: + break; + } + + return die_offset; +} + +//---------------------------------------------------------------------- +// Resolve any compile unit specific references so that we don't need +// the compile unit at a later time in order to work with the form +// value. +//---------------------------------------------------------------------- +bool +DWARFFormValue::ResolveCompileUnitReferences(const DWARFCompileUnit* cu) +{ + switch (m_form) + { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + m_value.value.uval += cu->GetOffset(); + m_form = DW_FORM_ref_addr; + return true; + break; + + default: + break; + } + + return false; +} + +const uint8_t* +DWARFFormValue::BlockData() const +{ + if (!IsInlinedCStr()) + return m_value.data; + return NULL; +} + + +bool +DWARFFormValue::IsBlockForm(const dw_form_t form) +{ + switch (form) + { + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + return true; + } + return false; +} + +bool +DWARFFormValue::IsDataForm(const dw_form_t form) +{ + switch (form) + { + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + return true; + } + return false; +} + +int +DWARFFormValue::Compare (const DWARFFormValue& a_value, const DWARFFormValue& b_value, const DWARFCompileUnit* a_cu, const DWARFCompileUnit* b_cu, const DataExtractor* debug_str_data_ptr) +{ + dw_form_t a_form = a_value.Form(); + dw_form_t b_form = b_value.Form(); + if (a_form < b_form) + return -1; + if (a_form > b_form) + return 1; + switch (a_form) + { + case DW_FORM_addr: + case DW_FORM_flag: + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + case DW_FORM_udata: + case DW_FORM_ref_addr: + { + uint64_t a = a_value.Unsigned(); + uint64_t b = b_value.Unsigned(); + if (a < b) + return -1; + if (a > b) + return 1; + return 0; + } + + case DW_FORM_sdata: + { + int64_t a = a_value.Signed(); + int64_t b = b_value.Signed(); + if (a < b) + return -1; + if (a > b) + return 1; + return 0; + } + + case DW_FORM_string: + case DW_FORM_strp: + { + const char *a_string = a_value.AsCString(debug_str_data_ptr); + const char *b_string = b_value.AsCString(debug_str_data_ptr); + if (a_string == b_string) + return 0; + else if (a_string && b_string) + return strcmp(a_string, b_string); + else if (a_string == NULL) + return -1; // A string is NULL, and B is valid + else + return 1; // A string valid, and B is NULL + } + + + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + { + uint64_t a_len = a_value.Unsigned(); + uint64_t b_len = b_value.Unsigned(); + if (a_len < b_len) + return -1; + if (a_len > b_len) + return 1; + // The block lengths are the same + return memcmp(a_value.BlockData(), b_value.BlockData(), a_value.Unsigned()); + } + break; + + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + { + uint64_t a = a_value.Reference(a_cu); + uint64_t b = b_value.Reference(b_cu); + if (a < b) + return -1; + if (a > b) + return 1; + return 0; + } + + case DW_FORM_indirect: + assert(!"This shouldn't happen after the form has been extracted..."); + break; + + default: + assert(!"Unhandled DW_FORM"); + break; + } + return -1; +} + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h new file mode 100644 index 00000000000..3db63664f38 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h @@ -0,0 +1,81 @@ +//===-- DWARFFormValue.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_DWARFFormValue_h_ + #define SymbolFileDWARF_DWARFFormValue_h_ + +#include "SymbolFileDWARF.h" +#include <stddef.h> // for NULL + +class DWARFFormValue +{ +public: + typedef struct ValueTypeTag + { + ValueTypeTag() : + data(NULL), + value() + { + value.uval = 0; + } + + union + { + uint64_t uval; + int64_t sval; + const char* cstr; + } value; + const uint8_t* data; + } ValueType; + + enum + { + eValueTypeInvalid = 0, + eValueTypeUnsigned, + eValueTypeSigned, + eValueTypeCStr, + eValueTypeBlock + }; + + DWARFFormValue(dw_form_t form = 0); + dw_form_t Form() const { return m_form; } + void SetForm(dw_form_t form) { m_form = form; } + const ValueType& Value() const { return m_value; } + void Dump(lldb_private::Stream *s, const lldb_private::DataExtractor* debug_str_data, const DWARFCompileUnit* cu) const; + bool ExtractValue(const lldb_private::DataExtractor& data, uint32_t* offset_ptr, const DWARFCompileUnit* cu); + bool IsInlinedCStr() const { return (m_value.data != NULL) && m_value.data == (uint8_t*)m_value.value.cstr; } + const uint8_t* BlockData() const; + uint64_t Reference(const DWARFCompileUnit* cu) const; + bool ResolveCompileUnitReferences(const DWARFCompileUnit* cu); + uint64_t Unsigned() const { return m_value.value.uval; } + void SetUnsigned(uint64_t uval) { m_value.value.uval = uval; } + int64_t Signed() const { return m_value.value.sval; } + void SetSigned(int64_t sval) { m_value.value.sval = sval; } + const char* AsCString(const lldb_private::DataExtractor* debug_str_data_ptr) const; + bool SkipValue(const lldb_private::DataExtractor& debug_info_data, uint32_t* offset_ptr, const DWARFCompileUnit* cu) const; + static bool SkipValue(const dw_form_t form, const lldb_private::DataExtractor& debug_info_data, uint32_t* offset_ptr, const DWARFCompileUnit* cu); +// static bool TransferValue(dw_form_t form, const lldb_private::DataExtractor& debug_info_data, uint32_t* offset_ptr, const DWARFCompileUnit* cu, BinaryStreamBuf& out_buff); +// static bool TransferValue(const DWARFFormValue& formValue, const DWARFCompileUnit* cu, BinaryStreamBuf& out_buff); +// static bool PutUnsigned(dw_form_t form, dw_offset_t offset, uint64_t value, BinaryStreamBuf& out_buff, const DWARFCompileUnit* cu, bool fixup_cu_relative_refs); + static bool IsBlockForm(const dw_form_t form); + static bool IsDataForm(const dw_form_t form); + + static int Compare (const DWARFFormValue& a, const DWARFFormValue& b, const DWARFCompileUnit* a_cu, const DWARFCompileUnit* b_cu, const lldb_private::DataExtractor* debug_str_data_ptr); +protected: + dw_form_t m_form; // Form for this value + ValueType m_value; // Contains all data for the form +}; + + +#endif // SymbolFileDWARF_DWARFFormValue_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.cpp new file mode 100644 index 00000000000..9229c2a2d22 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.cpp @@ -0,0 +1,172 @@ +//===-- DWARFLocationDescription.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFLocationDescription.h" +#include "DWARFDefines.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/Stream.h" + + +using namespace lldb_private; + +static int print_dwarf_exp_op (Stream *s, const DataExtractor& data, uint32_t* offset_ptr, int address_size, int dwarf_ref_size); + +int +print_dwarf_expression (Stream *s, + const DataExtractor& data, + int address_size, + int dwarf_ref_size, + bool location_expression) +{ + int op_count = 0; + uint32_t offset = 0; + while (data.ValidOffset(offset)) + { + if (location_expression && op_count > 0) + { + // err (baton, "Dwarf location expressions may only have one operand!"); + return 1; + } + if (op_count > 0) + { + s->PutCString(", "); + } + if (print_dwarf_exp_op (s, data, &offset, address_size, dwarf_ref_size) == 1) + return 1; + op_count++; + } + + return 0; +} + +static int +print_dwarf_exp_op (Stream *s, + const DataExtractor& data, + uint32_t* offset_ptr, + int address_size, + int dwarf_ref_size) +{ + uint8_t opcode = data.GetU8(offset_ptr); + DRC_class opcode_class; + uint64_t uint; + int64_t sint; + + int size; + + opcode_class = DW_OP_value_to_class (opcode) & (~DRC_DWARFv3); + + s->Printf("%s ", DW_OP_value_to_englishy_name (opcode)); + + /* Does this take zero parameters? If so we can shortcut this function. */ + if (opcode_class == DRC_ZEROOPERANDS) + return 0; + + if (opcode_class == DRC_TWOOPERANDS && opcode == DW_OP_bregx) + { + uint = data.GetULEB128(offset_ptr); + sint = data.GetSLEB128(offset_ptr); + s->Printf("%llu %lli", uint, sint); + return 0; + } + if (opcode_class != DRC_ONEOPERAND) + { + s->Printf("UNKNOWN OP %u", opcode); + return 1; + } + + switch (opcode) + { + case DW_OP_addr: size = address_size; break; + case DW_OP_const1u: size = 1; break; + case DW_OP_const1s: size = -1; break; + case DW_OP_const2u: size = 2; break; + case DW_OP_const2s: size = -2; break; + case DW_OP_const4u: size = 4; break; + case DW_OP_const4s: size = -4; break; + case DW_OP_const8u: size = 8; break; + case DW_OP_const8s: size = -8; break; + case DW_OP_constu: size = 128; break; + case DW_OP_consts: size = -128; break; + case DW_OP_fbreg: size = -128; 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: + size = -128; break; + case DW_OP_pick: + size = 1; break; + case DW_OP_deref_size: + size = 1; break; + case DW_OP_xderef_size: + size = 1; break; + case DW_OP_plus_uconst: + size = 128; break; + case DW_OP_skip: + size = -2; break; + case DW_OP_bra: + size = -2; break; + case DW_OP_call2: + size = 2; break; + case DW_OP_call4: + size = 4; break; + case DW_OP_call_ref: + size = dwarf_ref_size; break; + case DW_OP_piece: + size = 128; break; + case DW_OP_regx: + size = 128; break; + default: + s->Printf("UNKNOWN ONE-OPERAND OPCODE, #%u", opcode); + return 1; + } + + switch (size) + { + case -1: sint = (int8_t) data.GetU8(offset_ptr); s->Printf("%+lli", sint); break; + case -2: sint = (int16_t) data.GetU16(offset_ptr); s->Printf("%+lli", sint); break; + case -4: sint = (int32_t) data.GetU32(offset_ptr); s->Printf("%+lli", sint); break; + case -8: sint = (int64_t) data.GetU64(offset_ptr); s->Printf("%+lli", sint); break; + case -128: sint = data.GetSLEB128(offset_ptr); s->Printf("%+lli", sint); break; + case 1: uint = data.GetU8(offset_ptr); s->Printf("0x%2.2llx", uint); break; + case 2: uint = data.GetU16(offset_ptr); s->Printf("0x%4.4llx", uint); break; + case 4: uint = data.GetU32(offset_ptr); s->Printf("0x%8.8llx", uint); break; + case 8: uint = data.GetU64(offset_ptr); s->Printf("0x%16.16llx", uint); break; + case 128: uint = data.GetULEB128(offset_ptr); s->Printf("0x%llx", uint); break; + } + + return 0; +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.h new file mode 100644 index 00000000000..413a95c0f49 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.h @@ -0,0 +1,24 @@ +//===-- DWARFLocationDescription.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_DWARFLocationDescription_h_ +#define SymbolFileDWARF_DWARFLocationDescription_h_ + +#include "SymbolFileDWARF.h" + +int +print_dwarf_expression (lldb_private::Stream *s, + const lldb_private::DataExtractor& data, + int address_size, + int dwarf_ref_size, + bool location_expression); + + + +#endif // SymbolFileDWARF_DWARFLocationDescription_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationList.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationList.cpp new file mode 100644 index 00000000000..6a8359d3986 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationList.cpp @@ -0,0 +1,89 @@ +//===-- DWARFLocationList.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFLocationList.h" + +#include "lldb/Core/Stream.h" + +#include "DWARFCompileUnit.h" +#include "DWARFDebugInfo.h" +#include "DWARFLocationDescription.h" + +using namespace lldb_private; + +dw_offset_t +DWARFLocationList::Dump(Stream *s, const DWARFCompileUnit* cu, const DataExtractor& debug_loc_data, dw_offset_t offset) +{ + uint64_t start_addr, end_addr; + uint32_t addr_size = DWARFCompileUnit::GetAddressByteSize(cu); + s->SetAddressByteSize(DWARFCompileUnit::GetAddressByteSize(cu)); + dw_addr_t base_addr = cu ? cu->GetBaseAddress() : 0; + while (debug_loc_data.ValidOffset(offset)) + { + start_addr = debug_loc_data.GetMaxU64(&offset,addr_size); + end_addr = debug_loc_data.GetMaxU64(&offset,addr_size); + + if (start_addr == 0 && end_addr == 0) + break; + + s->PutCString("\n "); + s->Indent(); + s->AddressRange(start_addr + base_addr, end_addr + base_addr, NULL, ": "); + uint32_t loc_length = debug_loc_data.GetU16(&offset); + + DataExtractor locationData(debug_loc_data, offset, loc_length); + // if ( dump_flags & DWARFDebugInfo::eDumpFlag_Verbose ) *ostrm_ptr << " ( "; + print_dwarf_expression (s, locationData, addr_size, 4, false); + offset += loc_length; + } + + return offset; +} + +bool +DWARFLocationList::Extract(const DataExtractor& debug_loc_data, dw_offset_t* offset_ptr, DataExtractor& location_list_data) +{ + // Initialize with no data just in case we don't find anything + location_list_data.Clear(); + + size_t loc_list_length = Size(debug_loc_data, *offset_ptr); + if (loc_list_length > 0) + { + location_list_data.SetData(debug_loc_data, *offset_ptr, loc_list_length); + *offset_ptr += loc_list_length; + return true; + } + + return false; +} + +size_t +DWARFLocationList::Size(const DataExtractor& debug_loc_data, dw_offset_t offset) +{ + const dw_offset_t debug_loc_offset = offset; + + while (debug_loc_data.ValidOffset(offset)) + { + dw_addr_t start_addr = debug_loc_data.GetAddress(&offset); + dw_addr_t end_addr = debug_loc_data.GetAddress(&offset); + + if (start_addr == 0 && end_addr == 0) + break; + + uint16_t loc_length = debug_loc_data.GetU16(&offset); + offset += loc_length; + } + + if (offset > debug_loc_offset) + return offset - debug_loc_offset; + return 0; +} + + + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationList.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationList.h new file mode 100644 index 00000000000..3efd5c4492b --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFLocationList.h @@ -0,0 +1,34 @@ +//===-- DWARFLocationList.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_DWARFLocationList_h_ +#define SymbolFileDWARF_DWARFLocationList_h_ + +#include "SymbolFileDWARF.h" + +class DWARFLocationList +{ +public: + static dw_offset_t + Dump (lldb_private::Stream *s, + const DWARFCompileUnit* cu, + const lldb_private::DataExtractor& debug_loc_data, + dw_offset_t offset); + + static bool + Extract (const lldb_private::DataExtractor& debug_loc_data, + dw_offset_t* offset_ptr, + lldb_private::DataExtractor& location_list_data); + + static size_t + Size (const lldb_private::DataExtractor& debug_loc_data, + dw_offset_t offset); + +}; +#endif // SymbolFileDWARF_DWARFLocationList_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp new file mode 100644 index 00000000000..571271b34bb --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp @@ -0,0 +1,207 @@ +//===-- LogChannelDWARF.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LogChannelDWARF.h" + +#include "lldb/Core/Args.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamFile.h" +#include "SymbolFileDWARF.h" + +using namespace lldb; +using namespace lldb_private; + + +// when the one and only logging channel is abled, then this will be non NULL. +static LogChannelDWARF* g_log_channel = NULL; + +LogChannelDWARF::LogChannelDWARF () : + LogChannel () +{ +} + +LogChannelDWARF::~LogChannelDWARF () +{ +} + + +void +LogChannelDWARF::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + LogChannelDWARF::CreateInstance); +} + +void +LogChannelDWARF::Terminate() +{ + PluginManager::UnregisterPlugin (LogChannelDWARF::CreateInstance); +} + +LogChannel* +LogChannelDWARF::CreateInstance () +{ + return new LogChannelDWARF (); +} + +const char * +LogChannelDWARF::GetPluginNameStatic() +{ + static std::string g_plugin_name; + if (g_plugin_name.empty()) + { + g_plugin_name = SymbolFileDWARF::GetPluginNameStatic(); + g_plugin_name += LogChannel::GetPluginSuffix (); + } + return g_plugin_name.c_str(); +} + + +const char * +LogChannelDWARF::GetPluginDescriptionStatic() +{ + return "DWARF log channel for debugging plug-in issues."; +} + +const char * +LogChannelDWARF::GetPluginName() +{ + return GetPluginDescriptionStatic(); +} + +const char * +LogChannelDWARF::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +LogChannelDWARF::GetPluginVersion() +{ + return 1; +} + + +void +LogChannelDWARF::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + + +Error +LogChannelDWARF::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorStringWithFormat("No commands are supported.\n"); + return error; +} + + +Log * +LogChannelDWARF::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + +void +LogChannelDWARF::Disable () +{ + g_log_channel = NULL; + m_log_sp.reset(); +} + +bool +LogChannelDWARF::Enable +( + StreamSP &log_stream_sp, + uint32_t log_options, + Stream *feedback_strm, // Feedback stream for argument errors etc + const Args &categories // The categories to enable within this logging stream, if empty, enable default set +) +{ + Disable (); + + m_log_sp.reset(new Log (log_stream_sp)); + g_log_channel = this; + uint32_t flag_bits = 0; + bool got_unknown_category = false; + const size_t argc = categories.GetArgumentCount(); + for (size_t i=0; i<argc; ++i) + { + const char *arg = categories.GetArgumentAtIndex(i); + + if (::strcasecmp (arg, "all") == 0 ) flag_bits |= DWARF_LOG_ALL; + else if (::strcasecmp (arg, "info") == 0 ) flag_bits |= DWARF_LOG_DEBUG_INFO; + else if (::strcasecmp (arg, "line") == 0 ) flag_bits |= DWARF_LOG_DEBUG_LINE; + else if (::strcasecmp (arg, "pubnames") == 0 ) flag_bits |= DWARF_LOG_DEBUG_PUBNAMES; + else if (::strcasecmp (arg, "pubtypes") == 0 ) flag_bits |= DWARF_LOG_DEBUG_PUBTYPES; + else if (::strcasecmp (arg, "default") == 0 ) flag_bits |= DWARF_LOG_DEFAULT; + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + if (got_unknown_category == false) + { + got_unknown_category = true; + ListCategories (feedback_strm); + } + } + } + if (flag_bits == 0) + flag_bits = DWARF_LOG_DEFAULT; + m_log_sp->GetMask().SetAllFlagBits(flag_bits); + m_log_sp->GetOptions().SetAllFlagBits(log_options); + return m_log_sp.get() != NULL; +} + +void +LogChannelDWARF::ListCategories (Stream *strm) +{ + strm->Printf("Logging categories for '%s':\n" + " all - turn on all available logging categories\n" + " info - log the parsing if .debug_info\n" + " line - log the parsing if .debug_line\n" + " pubnames - log the parsing if .debug_pubnames\n" + " pubtypes - log the parsing if .debug_pubtypes\n\n", + SymbolFileDWARF::GetPluginNameStatic()); +} + +Log * +LogChannelDWARF::GetLog () +{ + if (g_log_channel) + return g_log_channel->m_log_sp.get(); + else + return NULL; +} + +Log * +LogChannelDWARF::GetLogIfAll (uint32_t mask) +{ + Log *log = GetLog(); + if (log) + if (log->GetMask().IsSet(mask)) + return log; + return NULL; +} + + +void +LogChannelDWARF::LogIf (uint32_t mask, const char *format, ...) +{ + if (g_log_channel) + { + LogSP log_sp(g_log_channel->m_log_sp); + va_list args; + va_start (args, format); + log_sp->VAPrintf (format, args); + va_end (args); + } +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h new file mode 100644 index 00000000000..943d1da194f --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h @@ -0,0 +1,91 @@ +//===-- LogChannelDWARF.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_LogChannelDWARF_h_ +#define liblldb_LogChannelDWARF_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +// Project includes +#include "lldb/Core/Log.h" + +#define DWARF_LOG_VERBOSE (1u << 0) +#define DWARF_LOG_DEBUG_INFO (1u << 1) +#define DWARF_LOG_DEBUG_LINE (1u << 2) +#define DWARF_LOG_DEBUG_PUBNAMES (1u << 3) +#define DWARF_LOG_DEBUG_PUBTYPES (1u << 4) +#define DWARF_LOG_ALL (UINT32_MAX) +#define DWARF_LOG_DEFAULT (DWARF_LOG_DEBUG_INFO) + +class LogChannelDWARF : public lldb_private::LogChannel +{ +public: + LogChannelDWARF (); + + virtual + ~LogChannelDWARF (); + + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::LogChannel * + CreateInstance (); + + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + + virtual void + Disable (); + + virtual bool + Enable (lldb::StreamSP &log_stream_sp, + uint32_t log_options, + lldb_private::Stream *feedback_strm, // Feedback stream for argument errors etc + const lldb_private::Args &categories); // The categories to enable within this logging stream, if empty, enable default set + + virtual void + ListCategories (lldb_private::Stream *strm); + + static lldb_private::Log * + GetLog (); + + static lldb_private::Log * + GetLogIfAll (uint32_t mask); + + static void + LogIf (uint32_t mask, const char *format, ...); +}; + +#endif // liblldb_LogChannelDWARF_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp new file mode 100644 index 00000000000..ffee695d3ab --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -0,0 +1,3615 @@ +//===-- SymbolFileDWARF.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolFileDWARF.h" + +// Other libraries and framework includes +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclGroup.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Specifiers.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/Value.h" + +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/VariableList.h" + +#include "DWARFCompileUnit.h" +#include "DWARFDebugAbbrev.h" +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfo.h" +#include "DWARFDebugInfoEntry.h" +#include "DWARFDebugLine.h" +#include "DWARFDebugPubnames.h" +#include "DWARFDebugRanges.h" +#include "DWARFDIECollection.h" +#include "DWARFFormValue.h" +#include "DWARFLocationList.h" +#include "LogChannelDWARF.h" + +#include <map> + +#define DIE_IS_BEING_PARSED ((void*)1) + +using namespace lldb; +using namespace lldb_private; + + +static const ConstString& +GetSectionNameDebugInfo() +{ + static const ConstString g_sect_name("__debug_info"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugAbbrev() +{ + static const ConstString g_sect_name ("__debug_abbrev"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugAranges() +{ + static const ConstString g_sect_name ("__debug_aranges"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugFrame() +{ + static const ConstString g_sect_name ("__debug_frame"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugLine() +{ + static const ConstString g_sect_name ("__debug_line"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugLoc() +{ + static const ConstString g_sect_name ("__debug_loc"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugMacInfo() +{ + static const ConstString g_sect_name ("__debug_macinfo"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugPubNames() +{ + static const ConstString g_sect_name ("__debug_pubnames"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugPubTypes() +{ + static const ConstString g_sect_name ("__debug_pubtypes"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugRanges() +{ + static const ConstString g_sect_name ("__debug_ranges"); + return g_sect_name; +} + +static const ConstString& +GetSectionNameDebugStr() +{ + static const ConstString g_sect_name ("__debug_str"); + return g_sect_name; +} + +static uint32_t +DwarfToClangAccessibility (uint32_t dwarf_accessibility) +{ + switch (dwarf_accessibility) + { + case DW_ACCESS_public: + return clang::AS_public; + case DW_ACCESS_private: + return clang::AS_private; + case DW_ACCESS_protected: + return clang::AS_protected; + default: + return clang::AS_none; + } +} + +void +SymbolFileDWARF::Initialize() +{ + LogChannelDWARF::Initialize(); + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +SymbolFileDWARF::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); + LogChannelDWARF::Initialize(); +} + + +const char * +SymbolFileDWARF::GetPluginNameStatic() +{ + return "symbol-file.dwarf2"; +} + +const char * +SymbolFileDWARF::GetPluginDescriptionStatic() +{ + return "DWARF and DWARF3 debug symbol file reader."; +} + + +SymbolFile* +SymbolFileDWARF::CreateInstance (ObjectFile* obj_file) +{ + return new SymbolFileDWARF(obj_file); +} + +//---------------------------------------------------------------------- +// Gets the first parent that is a lexical block, function or inlined +// subroutine, or compile unit. +//---------------------------------------------------------------------- +static const DWARFDebugInfoEntry * +GetParentSymbolContextDIE(const DWARFDebugInfoEntry *child_die) +{ + const DWARFDebugInfoEntry *die; + for (die = child_die->GetParent(); die != NULL; die = die->GetParent()) + { + dw_tag_t tag = die->Tag(); + + switch (tag) + { + case DW_TAG_compile_unit: + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: + return die; + } + } + return NULL; +} + + +SymbolFileDWARF::SymbolFileDWARF(ObjectFile* ofile) : + SymbolFile(ofile), + m_flags(), + m_data_debug_abbrev(), + m_data_debug_aranges(), + m_data_debug_frame(), + m_data_debug_info(), + m_data_debug_line(), + m_data_debug_loc(), + m_data_debug_macinfo(), + m_data_debug_pubnames(), + m_data_debug_pubtypes(), + m_data_debug_ranges(), + m_data_debug_str(), + m_abbr(), + m_aranges(), + m_info(), + m_line(), + m_name_to_function_die(), + m_name_to_inlined_die(), + m_name_to_global_die(), + m_name_to_type_die(), + m_indexed(false), +// m_pubnames(), +// m_pubtypes(), +// m_pubbasetypes(), + m_ranges()//, +// m_type_fixups(), +// m_indirect_fixups() +{ +} + +SymbolFileDWARF::~SymbolFileDWARF() +{ +} + +bool +SymbolFileDWARF::SupportedVersion(uint16_t version) +{ + return version == 2 || version == 3; +} + +uint32_t +SymbolFileDWARF::GetAbilities () +{ + uint32_t abilities = 0; + if (m_obj_file != NULL) + { + const Section* section = NULL; + const SectionList *section_list = m_obj_file->GetSectionList(); + if (section_list == NULL) + return 0; + + uint64_t debug_abbrev_file_size = 0; + uint64_t debug_aranges_file_size = 0; + uint64_t debug_frame_file_size = 0; + uint64_t debug_info_file_size = 0; + uint64_t debug_line_file_size = 0; + uint64_t debug_loc_file_size = 0; + uint64_t debug_macinfo_file_size = 0; + uint64_t debug_pubnames_file_size = 0; + uint64_t debug_pubtypes_file_size = 0; + uint64_t debug_ranges_file_size = 0; + uint64_t debug_str_file_size = 0; + + static ConstString g_dwarf_section_name ("__DWARF"); + + section = section_list->FindSectionByName(g_dwarf_section_name).get(); + + if (section) + section->MemoryMapSectionDataFromObjectFile(m_obj_file, m_dwarf_data); + + section = section_list->FindSectionByName (GetSectionNameDebugInfo()).get(); + if (section != NULL) + { + debug_info_file_size = section->GetByteSize(); + + section = section_list->FindSectionByName (GetSectionNameDebugAbbrev()).get(); + if (section) + debug_abbrev_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugAbbrevData); + + section = section_list->FindSectionByName (GetSectionNameDebugAranges()).get(); + if (section) + debug_aranges_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugArangesData); + + section = section_list->FindSectionByName (GetSectionNameDebugFrame()).get(); + if (section) + debug_frame_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugFrameData); + + section = section_list->FindSectionByName (GetSectionNameDebugLine()).get(); + if (section) + debug_line_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugLineData); + + section = section_list->FindSectionByName (GetSectionNameDebugLoc()).get(); + if (section) + debug_loc_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugLocData); + + section = section_list->FindSectionByName (GetSectionNameDebugMacInfo()).get(); + if (section) + debug_macinfo_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugMacInfoData); + + section = section_list->FindSectionByName (GetSectionNameDebugPubNames()).get(); + if (section) + debug_pubnames_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugPubNamesData); + + section = section_list->FindSectionByName (GetSectionNameDebugPubTypes()).get(); + if (section) + debug_pubtypes_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugPubTypesData); + + section = section_list->FindSectionByName (GetSectionNameDebugRanges()).get(); + if (section) + debug_ranges_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugRangesData); + + section = section_list->FindSectionByName (GetSectionNameDebugStr()).get(); + if (section) + debug_str_file_size = section->GetByteSize(); + else + m_flags.Set (flagsGotDebugStrData); + } + + if (debug_abbrev_file_size > 0 && debug_info_file_size > 0) + abilities |= CompileUnits | Functions | Blocks | GlobalVariables | LocalVariables | VariableTypes; + + if (debug_line_file_size > 0) + abilities |= LineTables; + + if (debug_aranges_file_size > 0) + abilities |= AddressAcceleratorTable; + + if (debug_pubnames_file_size > 0) + abilities |= FunctionAcceleratorTable; + + if (debug_pubtypes_file_size > 0) + abilities |= TypeAcceleratorTable; + + if (debug_macinfo_file_size > 0) + abilities |= MacroInformation; + + if (debug_frame_file_size > 0) + abilities |= CallFrameInformation; + } + return abilities; +} + +const DataExtractor& +SymbolFileDWARF::GetCachedSectionData (uint32_t got_flag, const ConstString §ion_name, DataExtractor &data) +{ + if (m_flags.IsClear (got_flag)) + { + m_flags.Set (got_flag); + const SectionList *section_list = m_obj_file->GetSectionList(); + if (section_list) + { + Section *section = section_list->FindSectionByName (section_name).get(); + if (section ) + { + // See if we memory mapped the DWARF segment? + if (m_dwarf_data.GetByteSize()) + { + data.SetData(m_dwarf_data, section->GetOffset (), section->GetByteSize()); + } + else + { + if (section->ReadSectionDataFromObjectFile(m_obj_file, data) == 0) + data.Clear(); + } + } + } + } + return data; +} + +const DataExtractor& +SymbolFileDWARF::get_debug_abbrev_data() +{ + return GetCachedSectionData (flagsGotDebugAbbrevData, GetSectionNameDebugAbbrev(), m_data_debug_abbrev); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_aranges_data() +{ + return GetCachedSectionData (flagsGotDebugArangesData, GetSectionNameDebugAranges(), m_data_debug_aranges); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_frame_data() +{ + return GetCachedSectionData (flagsGotDebugFrameData, GetSectionNameDebugFrame(), m_data_debug_frame); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_info_data() +{ + return GetCachedSectionData (flagsGotDebugInfoData, GetSectionNameDebugInfo(), m_data_debug_info); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_line_data() +{ + return GetCachedSectionData (flagsGotDebugLineData, GetSectionNameDebugLine(), m_data_debug_line); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_loc_data() +{ + return GetCachedSectionData (flagsGotDebugLocData, GetSectionNameDebugLoc(), m_data_debug_loc); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_macinfo_data() +{ + return GetCachedSectionData (flagsGotDebugMacInfoData, GetSectionNameDebugMacInfo(), m_data_debug_macinfo); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_pubnames_data() +{ + return GetCachedSectionData (flagsGotDebugPubNamesData, GetSectionNameDebugPubNames(), m_data_debug_pubnames); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_pubtypes_data() +{ + return GetCachedSectionData (flagsGotDebugPubTypesData, GetSectionNameDebugPubTypes(), m_data_debug_pubtypes); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_ranges_data() +{ + return GetCachedSectionData (flagsGotDebugRangesData, GetSectionNameDebugRanges(), m_data_debug_ranges); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_str_data() +{ + return GetCachedSectionData (flagsGotDebugStrData, GetSectionNameDebugStr(), m_data_debug_str); +} + + +DWARFDebugAbbrev* +SymbolFileDWARF::DebugAbbrev() +{ + if (m_abbr.get() == NULL) + { + const DataExtractor &debug_abbrev_data = get_debug_abbrev_data(); + if (debug_abbrev_data.GetByteSize() > 0) + { + m_abbr.reset(new DWARFDebugAbbrev()); + if (m_abbr.get()) + m_abbr->Parse(debug_abbrev_data); + } + } + return m_abbr.get(); +} + +const DWARFDebugAbbrev* +SymbolFileDWARF::DebugAbbrev() const +{ + return m_abbr.get(); +} + +DWARFDebugAranges* +SymbolFileDWARF::DebugAranges() +{ + if (m_aranges.get() == NULL) + { + Timer scoped_timer(__PRETTY_FUNCTION__, "%s this = %p", __PRETTY_FUNCTION__, this); + m_aranges.reset(new DWARFDebugAranges()); + if (m_aranges.get()) + { + const DataExtractor &debug_aranges_data = get_debug_aranges_data(); + if (debug_aranges_data.GetByteSize() > 0) + m_aranges->Extract(debug_aranges_data); + else + m_aranges->Generate(this); + } + } + return m_aranges.get(); +} + +const DWARFDebugAranges* +SymbolFileDWARF::DebugAranges() const +{ + return m_aranges.get(); +} + + +DWARFDebugInfo* +SymbolFileDWARF::DebugInfo() +{ + if (m_info.get() == NULL) + { + Timer scoped_timer(__PRETTY_FUNCTION__, "%s this = %p", __PRETTY_FUNCTION__, this); + if (get_debug_info_data().GetByteSize() > 0) + { + m_info.reset(new DWARFDebugInfo()); + if (m_info.get()) + { + m_info->SetDwarfData(this); + } + } + } + return m_info.get(); +} + +const DWARFDebugInfo* +SymbolFileDWARF::DebugInfo() const +{ + return m_info.get(); +} + +//DWARFDebugLine* +//SymbolFileDWARF::DebugLine() +//{ +// if (m_line.get() == NULL) +// { +// Timer scoped_timer(__PRETTY_FUNCTION__); +// const DataExtractor &debug_line_data = debug_line(); +// if (debug_line_data.GetByteSize() > 0) +// { +// m_line.reset(new DWARFDebugLine()); +// if (m_line.get()) +// m_line->Parse(debug_line_data); +// } +// } +// return m_line.get(); +//} +// +//const DWARFDebugLine* +//SymbolFileDWARF::DebugLine() const +//{ +// return m_line.get(); +//} + + +DWARFCompileUnit* +SymbolFileDWARF::GetDWARFCompileUnitForUID(lldb::user_id_t cu_uid) +{ + DWARFDebugInfo* info = DebugInfo(); + if (info) + return info->GetCompileUnit(cu_uid).get(); + return NULL; +} + +//DWARFCompileUnit* +//SymbolFileDWARF::GetNextUnparsedDWARFCompileUnit(DWARFCompileUnit* prev_cu) +//{ +// DWARFCompileUnit* cu = NULL; +// DWARFDebugInfo* info = DebugInfo(); +// if (info) +// { +// uint32_t cu_idx = 0; +// if (prev_cu != NULL) +// { +// // Find the index of the previus DWARF compile unit if one was provided +// while ((cu = info->GetCompileUnitAtIndex(cu_idx)) != NULL) +// { +// ++cu_idx; +// if (cu == prev_cu) +// break; +// } +// } +// +// // Now find the next unparsed DWARF compile unit. We do this by checking the +// // user data in the DWARFCompileUnit class that starts as NULL, and eventually +// // holds a pointer to the CompileUnit that was created for it after it has +// // been parsed. +// while ((cu = info->GetCompileUnitAtIndex(cu_idx)) != NULL) +// { +// if (cu->GetUserData() == NULL) +// break; +// } +// } +// return cu; +//} + +DWARFDebugRanges* +SymbolFileDWARF::DebugRanges() +{ + if (m_ranges.get() == NULL) + { + Timer scoped_timer(__PRETTY_FUNCTION__, "%s this = %p", __PRETTY_FUNCTION__, this); + if (get_debug_ranges_data().GetByteSize() > 0) + { + m_ranges.reset(new DWARFDebugRanges()); + if (m_ranges.get()) + m_ranges->Extract(this); + } + } + return m_ranges.get(); +} + +const DWARFDebugRanges* +SymbolFileDWARF::DebugRanges() const +{ + return m_ranges.get(); +} +// +//DWARFDebugPubnames* +//SymbolFileDWARF::DebugPubnames() +//{ +// if (m_pubnames.get() == NULL) +// { +// Timer scoped_timer(__PRETTY_FUNCTION__, "%s this = %p", __PRETTY_FUNCTION__, this); +// const DataExtractor &debug_pubnames_data = get_debug_pubnames_data(); +// if (debug_pubnames_data.GetByteSize() > 0) +// { +// // Pass false to indicate this is a pubnames section +// m_pubnames.reset(new DWARFDebugPubnames()); +// if (m_pubnames.get()) +// { +// // "m_pubnames->GeneratePubnames" is costly, but needed for global variables +// m_pubnames->GeneratePubnames(this); +// +//#if 0 +// StreamFile s(stdout); +// s.Printf (".debug_pubnames for %s/%s:\n", +// m_obj_file->GetModule()->GetFileSpec().GetDirectory().AsCString(), +// m_obj_file->GetModule()->GetFileSpec().GetFilename().AsCString()); +// m_pubnames->Dump (&s); +//#endif +// // "m_pubnames->Extract" is quicker, but the pubnames don't always contain what we need. +// //m_pubnames->Extract(debug_pubnames_data); +// } +// } +// } +// return m_pubnames.get(); +//} +// +//const DWARFDebugPubnames* +//SymbolFileDWARF::DebugPubnames() const +//{ +// return m_pubnames.get(); +//} + +//DWARFDebugPubnames* +//SymbolFileDWARF::DebugPubBaseTypes() +//{ +// if (m_pubbasetypes.get() == NULL) +// { +// Timer scoped_timer(__PRETTY_FUNCTION__, "%s this = %p", __PRETTY_FUNCTION__, this); +// // Pass false to indicate this is a pubnames section +// m_pubbasetypes.reset(new DWARFDebugPubnames()); +// if (m_pubbasetypes.get()) +// m_pubbasetypes->GeneratePubBaseTypes(this); +// } +// return m_pubbasetypes.get(); +//} +// +//const DWARFDebugPubnames* +//SymbolFileDWARF::DebugPubBaseTypes() const +//{ +// return m_pubbasetypes.get(); +//} +// +//const DWARFDebugPubnames* +//SymbolFileDWARF::DebugPubtypes() const +//{ +// return m_pubtypes.get(); +//} +// +//DWARFDebugPubnames* +//SymbolFileDWARF::DebugPubtypes() +//{ +// if (m_pubtypes.get() == NULL) +// { +// Timer scoped_timer(__PRETTY_FUNCTION__, "%s this = %p", __PRETTY_FUNCTION__, this); +// const DataExtractor &debug_pubtypes_data = get_debug_pubtypes_data(); +// if (debug_pubtypes_data.GetByteSize() > 0) +// { +// // Pass false to indicate this is a pubnames section +// m_pubtypes.reset(new DWARFDebugPubnames()); +// if (m_pubtypes.get()) +// m_pubtypes->Extract(debug_pubtypes_data); +// } +// } +// return m_pubtypes.get(); +//} +// + +bool +SymbolFileDWARF::ParseCompileUnit(DWARFCompileUnit* cu, CompUnitSP& compile_unit_sp) +{ + if (cu != NULL) + { + const DWARFDebugInfoEntry * cu_die = cu->GetCompileUnitDIEOnly (); + if (cu_die) + { + const char * cu_die_name = cu_die->GetName(this, cu); + const char * cu_comp_dir = cu_die->GetAttributeValueAsString(this, cu, DW_AT_comp_dir, NULL); + Language::Type language = (Language::Type)cu_die->GetAttributeValueAsUnsigned(this, cu, DW_AT_language, 0); + if (cu_die_name) + { + if (cu_die_name[0] == '/' || cu_comp_dir == NULL && cu_comp_dir[0]) + { + compile_unit_sp.reset(new CompileUnit(m_obj_file->GetModule(), cu, cu_die_name, cu->GetOffset(), language)); + } + else + { + std::string fullpath(cu_comp_dir); + if (*fullpath.rbegin() != '/') + fullpath += '/'; + fullpath += cu_die_name; + + compile_unit_sp.reset(new CompileUnit(m_obj_file->GetModule(), cu, fullpath.c_str(), cu->GetOffset(), language)); + } + + if (compile_unit_sp.get()) + { + cu->SetUserData(compile_unit_sp.get()); + return true; + } + } + } + } + return false; +} + +#if defined LLDB_SYMBOL_FILE_DWARF_SHRINK_TEST + +void +SymbolFileDWARF::ShrinkDSYM(CompileUnit *dc_cu, DWARFCompileUnit *dw_cu, const FileSpec& cu_fspec, const FileSpec& base_types_fspec, FSToDIES& fs_to_dies, const DWARFDebugInfoEntry *die) +{ + while (die != NULL) + { + dw_tag_t tag = die->Tag(); + + switch (tag) + { + case DW_TAG_base_type: + // Put all base types into the base type compile unit + fs_to_dies[base_types_fspec].Insert(die); + break; + + default: + { + uint32_t decl_file = die->GetAttributeValueAsUnsigned(this, dw_cu, DW_AT_decl_file, 0); + if (decl_file) + { + fs_to_dies[dc_cu->GetSupportFiles().GetFileSpecAtIndex(decl_file)].Insert(die); + } + else + { + // add this to the current compile unit + fs_to_dies[cu_fspec].Insert(die); + } + } + break; + } + + die = die->GetSibling(); + } +} + + + +#endif + +uint32_t +SymbolFileDWARF::GetNumCompileUnits() +{ + DWARFDebugInfo* info = DebugInfo(); + if (info) + { +#if defined LLDB_SYMBOL_FILE_DWARF_SHRINK_TEST + uint32_t cu_idx; + FSToDIES fs_to_dies; + + FileSpec base_type_fspec("DW_TAG_base_type"); + const uint32_t num_comp_units = info->GetNumCompileUnits(); + + for (cu_idx=0; cu_idx < num_comp_units; ++cu_idx) + { + DWARFCompileUnit* cu = info->GetCompileUnitAtIndex(cu_idx); + if (cu != NULL) + { + const DWARFDebugInfoEntry *cu_die = cu->DIE(); + if (cu_die) + { + CompUnitSP dc_cu_sp; + ParseCompileUnit(cu, dc_cu_sp); + if (dc_cu_sp.get()) + { + FileSpec cu_fspec(*dc_cu_sp.get()); + + ShrinkDSYM(dc_cu_sp.get(), cu, cu_fspec, base_type_fspec, fs_to_dies, cu->DIE()->GetFirstChild()); + } + } + } + } + + Stream strm(stdout); + FSToDIES::const_iterator pos, end = fs_to_dies.end(); + for (pos = fs_to_dies.begin(); pos != end; ++pos) + { + strm << "\n\nMinimal Compile Unit: " << pos->first << ":\n"; + const DWARFDIECollection& dies = pos->second; + dies.Dump(strm, NULL); + } + return num_comp_units; +#else + return info->GetNumCompileUnits(); +#endif + } + return 0; +} + +CompUnitSP +SymbolFileDWARF::ParseCompileUnitAtIndex(uint32_t cu_idx) +{ + CompUnitSP comp_unit; + DWARFDebugInfo* info = DebugInfo(); + if (info) + { + DWARFCompileUnit* cu = info->GetCompileUnitAtIndex(cu_idx); + if (cu != NULL) + { + // Our symbol vendor shouldn't be asking us to add a compile unit that + // has already been added to it, which this DWARF plug-in knows as it + // stores the lldb compile unit (CompileUnit) pointer in each + // DWARFCompileUnit object when it gets added. + assert(cu->GetUserData() == NULL); + ParseCompileUnit(cu, comp_unit); + } + } + return comp_unit; +} + +static void +AddRangesToBlock +( + BlockList& blocks, + lldb::user_id_t blockID, + DWARFDebugRanges::RangeList& ranges, + addr_t block_base_addr +) +{ + ranges.SubtractOffset (block_base_addr); + size_t range_idx = 0; + const DWARFDebugRanges::Range *debug_range; + for (range_idx = 0; (debug_range = ranges.RangeAtIndex(range_idx)) != NULL; range_idx++) + { + blocks.AddRange(blockID, debug_range->begin_offset, debug_range->end_offset); + } +} + + +Function * +SymbolFileDWARF::ParseCompileUnitFunction (const SymbolContext& sc, const DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die) +{ + DWARFDebugRanges::RangeList func_ranges; + const char *name = NULL; + const char *mangled = NULL; + int decl_file = 0; + int decl_line = 0; + int decl_column = 0; + int call_file = 0; + int call_line = 0; + int call_column = 0; + DWARFExpression frame_base; + + // Parse the function prototype as a type that can then be added to concrete function instance + ParseTypes (sc, dwarf_cu, die, false, false); + //FixupTypes(); + + if (die->GetDIENamesAndRanges(this, dwarf_cu, name, mangled, func_ranges, decl_file, decl_line, decl_column, call_file, call_line, call_column, &frame_base)) + { + // Union of all ranges in the function DIE (if the function is discontiguous) + AddressRange func_range; + lldb::addr_t lowest_func_addr = func_ranges.LowestAddress(0); + lldb::addr_t highest_func_addr = func_ranges.HighestAddress(0); + if (lowest_func_addr != LLDB_INVALID_ADDRESS && lowest_func_addr <= highest_func_addr) + { + func_range.GetBaseAddress().ResolveAddressUsingFileSections (lowest_func_addr, m_obj_file->GetSectionList()); + if (func_range.GetBaseAddress().IsValid()) + func_range.SetByteSize(highest_func_addr - lowest_func_addr); + } + + if (func_range.GetBaseAddress().IsValid()) + { + Mangled func_name; + if (mangled) + func_name.SetValue(mangled, true); + else if (name) + func_name.SetValue(name, false); + + FunctionSP func_sp; + std::auto_ptr<Declaration> decl_ap; + if (decl_file != 0 || decl_line != 0 || decl_column != 0) + decl_ap.reset(new Declaration(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(decl_file), decl_line, decl_column)); + + Type *func_type = NULL; + + if (die->GetUserData() != DIE_IS_BEING_PARSED) + func_type = (Type*)die->GetUserData(); + + assert(func_type == NULL || func_type != DIE_IS_BEING_PARSED); + + func_range.GetBaseAddress().ResolveLinkedAddress(); + + func_sp.reset(new Function (sc.comp_unit, + die->GetOffset(), // UserID is the DIE offset + die->GetOffset(), + func_name, + func_type, + func_range)); // first address range + + if (func_sp.get() != NULL) + { + func_sp->GetFrameBaseExpression() = frame_base; + sc.comp_unit->AddFunction(func_sp); + return func_sp.get(); + } + } + } + return NULL; +} + +size_t +SymbolFileDWARF::ParseCompileUnitFunctions(const SymbolContext &sc) +{ + assert (sc.comp_unit); + size_t functions_added = 0; + const DWARFCompileUnit* dwarf_cu = GetDWARFCompileUnitForUID(sc.comp_unit->GetID()); + if (dwarf_cu) + { + DWARFDIECollection function_dies; + const size_t num_funtions = dwarf_cu->AppendDIEsWithTag (DW_TAG_subprogram, function_dies); + size_t func_idx; + for (func_idx = 0; func_idx < num_funtions; ++func_idx) + { + const DWARFDebugInfoEntry *die = function_dies.GetDIEPtrAtIndex(func_idx); + if (sc.comp_unit->FindFunctionByUID (die->GetOffset()).get() == NULL) + { + if (ParseCompileUnitFunction(sc, dwarf_cu, die)) + ++functions_added; + } + } + //FixupTypes(); + } + return functions_added; +} + +bool +SymbolFileDWARF::ParseCompileUnitSupportFiles (const SymbolContext& sc, FileSpecList& support_files) +{ + assert (sc.comp_unit); + DWARFCompileUnit* cu = GetDWARFCompileUnitForUID(sc.comp_unit->GetID()); + assert (cu); + const DWARFDebugInfoEntry * cu_die = cu->GetCompileUnitDIEOnly(); + + if (cu_die) + { + const char * cu_comp_dir = cu_die->GetAttributeValueAsString(this, cu, DW_AT_comp_dir, NULL); + dw_offset_t stmt_list = cu_die->GetAttributeValueAsUnsigned(this, cu, DW_AT_stmt_list, DW_INVALID_OFFSET); + + // All file indexes in DWARF are one based and a file of index zero is + // supposed to be the compile unit itself. + support_files.Append (*sc.comp_unit); + + return DWARFDebugLine::ParseSupportFiles(get_debug_line_data(), cu_comp_dir, stmt_list, support_files); + } + return false; +} + +struct ParseDWARFLineTableCallbackInfo +{ + LineTable* line_table; + const SectionList *section_list; + lldb::addr_t prev_sect_file_base_addr; + lldb::addr_t curr_sect_file_base_addr; + bool is_oso_for_debug_map; + bool prev_in_final_executable; + DWARFDebugLine::Row prev_row; + SectionSP prev_section_sp; + SectionSP curr_section_sp; +}; + +//---------------------------------------------------------------------- +// ParseStatementTableCallback +//---------------------------------------------------------------------- +static void +ParseDWARFLineTableCallback(dw_offset_t offset, const DWARFDebugLine::State& state, void* userData) +{ + LineTable* line_table = ((ParseDWARFLineTableCallbackInfo*)userData)->line_table; + if (state.row == DWARFDebugLine::State::StartParsingLineTable) + { + // Just started parsing the line table + } + else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) + { + // Done parsing line table, nothing to do for the cleanup + } + else + { + ParseDWARFLineTableCallbackInfo* info = (ParseDWARFLineTableCallbackInfo*)userData; + // We have a new row, lets append it + + if (info->curr_section_sp.get() == NULL || info->curr_section_sp->ContainsFileAddress(state.address) == false) + { + info->prev_section_sp = info->curr_section_sp; + info->prev_sect_file_base_addr = info->curr_sect_file_base_addr; + // If this is an end sequence entry, then we subtract one from the + // address to make sure we get an address that is not the end of + // a section. + if (state.end_sequence && state.address != 0) + info->curr_section_sp = info->section_list->FindSectionContainingFileAddress (state.address - 1); + else + info->curr_section_sp = info->section_list->FindSectionContainingFileAddress (state.address); + + if (info->curr_section_sp.get()) + info->curr_sect_file_base_addr = info->curr_section_sp->GetFileAddress (); + else + info->curr_sect_file_base_addr = 0; + } + if (info->curr_section_sp.get()) + { + lldb::addr_t curr_line_section_offset = state.address - info->curr_sect_file_base_addr; + // Check for the fancy section magic to determine if we + + if (info->is_oso_for_debug_map) + { + // When this is a debug map object file that contains DWARF + // (referenced from an N_OSO debug map nlist entry) we will have + // a file address in the file range for our section from the + // original .o file, and a load address in the executable that + // contains the debug map. + // + // If the sections for the file range and load range are + // different, we have a remapped section for the function and + // this address is resolved. If they are the same, then the + // function for this address didn't make it into the final + // executable. + bool curr_in_final_executable = info->curr_section_sp->GetLinkedSection () != NULL; + + // If we are doing DWARF with debug map, then we need to carefully + // add each line table entry as there may be gaps as functions + // get moved around or removed. + if (!info->prev_row.end_sequence && info->prev_section_sp.get()) + { + if (info->prev_in_final_executable) + { + bool terminate_previous_entry = false; + if (!curr_in_final_executable) + { + // Check for the case where the previous line entry + // in a function made it into the final executable, + // yet the current line entry falls in a function + // that didn't. The line table used to be contiguous + // through this address range but now it isn't. We + // need to terminate the previous line entry so + // that we can reconstruct the line range correctly + // for it and to keep the line table correct. + terminate_previous_entry = true; + } + else if (info->curr_section_sp.get() != info->prev_section_sp.get()) + { + // Check for cases where the line entries used to be + // contiguous address ranges, but now they aren't. + // This can happen when order files specify the + // ordering of the functions. + lldb::addr_t prev_line_section_offset = info->prev_row.address - info->prev_sect_file_base_addr; + Section *curr_sect = info->curr_section_sp.get(); + Section *prev_sect = info->prev_section_sp.get(); + assert (curr_sect->GetLinkedSection()); + assert (prev_sect->GetLinkedSection()); + lldb::addr_t object_file_addr_delta = state.address - info->prev_row.address; + lldb::addr_t curr_linked_file_addr = curr_sect->GetLinkedFileAddress() + curr_line_section_offset; + lldb::addr_t prev_linked_file_addr = prev_sect->GetLinkedFileAddress() + prev_line_section_offset; + lldb::addr_t linked_file_addr_delta = curr_linked_file_addr - prev_linked_file_addr; + if (object_file_addr_delta != linked_file_addr_delta) + terminate_previous_entry = true; + } + + if (terminate_previous_entry) + { + line_table->InsertLineEntry (info->prev_section_sp, + state.address - info->prev_sect_file_base_addr, + info->prev_row.line, + info->prev_row.column, + info->prev_row.file, + false, // is_stmt + false, // basic_block + false, // state.prologue_end + false, // state.epilogue_begin + true); // end_sequence); + } + } + } + + if (curr_in_final_executable) + { + line_table->InsertLineEntry (info->curr_section_sp, + curr_line_section_offset, + state.line, + state.column, + state.file, + state.is_stmt, + state.basic_block, + state.prologue_end, + state.epilogue_begin, + state.end_sequence); + info->prev_section_sp = info->curr_section_sp; + } + else + { + // If the current address didn't make it into the final + // executable, the current section will be the __text + // segment in the .o file, so we need to clear this so + // we can catch the next function that did make it into + // the final executable. + info->prev_section_sp.reset(); + info->curr_section_sp.reset(); + } + + info->prev_in_final_executable = curr_in_final_executable; + } + else + { + // We are not in an object file that contains DWARF for an + // N_OSO, this is just a normal DWARF file. The DWARF spec + // guarantees that the addresses will be in increasing order + // so, since we store line tables in file address order, we + // can always just append the line entry without needing to + // search for the correct insertion point (we don't need to + // use LineEntry::InsertLineEntry()). + line_table->AppendLineEntry (info->curr_section_sp, + curr_line_section_offset, + state.line, + state.column, + state.file, + state.is_stmt, + state.basic_block, + state.prologue_end, + state.epilogue_begin, + state.end_sequence); + } + } + + info->prev_row = state; + } +} + +bool +SymbolFileDWARF::ParseCompileUnitLineTable (const SymbolContext &sc) +{ + assert (sc.comp_unit); + if (sc.comp_unit->GetLineTable() != NULL) + return true; + + DWARFCompileUnit* dwarf_cu = GetDWARFCompileUnitForUID(sc.comp_unit->GetID()); + if (dwarf_cu) + { + const DWARFDebugInfoEntry *dwarf_cu_die = dwarf_cu->GetCompileUnitDIEOnly(); + const dw_offset_t cu_line_offset = dwarf_cu_die->GetAttributeValueAsUnsigned(this, dwarf_cu, DW_AT_stmt_list, DW_INVALID_OFFSET); + if (cu_line_offset != DW_INVALID_OFFSET) + { + std::auto_ptr<LineTable> line_table_ap(new LineTable(sc.comp_unit)); + if (line_table_ap.get()) + { + ParseDWARFLineTableCallbackInfo info = { line_table_ap.get(), m_obj_file->GetSectionList(), 0, 0, m_flags.IsSet (flagsDWARFIsOSOForDebugMap), false}; + uint32_t offset = cu_line_offset; + DWARFDebugLine::ParseStatementTable(get_debug_line_data(), &offset, ParseDWARFLineTableCallback, &info); + sc.comp_unit->SetLineTable(line_table_ap.release()); + return true; + } + } + } + return false; +} + +size_t +SymbolFileDWARF::ParseFunctionBlocks +( + const SymbolContext& sc, + lldb::user_id_t parentBlockID, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + addr_t subprogram_low_pc, + bool parse_siblings, + bool parse_children +) +{ + size_t blocks_added = 0; + while (die != NULL) + { + dw_tag_t tag = die->Tag(); + + switch (tag) + { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: + { + DWARFDebugRanges::RangeList ranges; + const char *name = NULL; + const char *mangled_name = NULL; + BlockList& blocks = sc.function->GetBlocks(false); + + lldb::user_id_t blockID = blocks.AddChild(parentBlockID, die->GetOffset()); + int decl_file = 0; + int decl_line = 0; + int decl_column = 0; + int call_file = 0; + int call_line = 0; + int call_column = 0; + if (die->GetDIENamesAndRanges(this, dwarf_cu, name, mangled_name, ranges, decl_file, decl_line, decl_column, call_file, call_line, call_column)) + { + if (tag == DW_TAG_subprogram) + { + assert (subprogram_low_pc == LLDB_INVALID_ADDRESS); + subprogram_low_pc = ranges.LowestAddress(0); + } + + AddRangesToBlock (blocks, blockID, ranges, subprogram_low_pc); + + if (tag != DW_TAG_subprogram && (name != NULL || mangled_name != NULL)) + { + std::auto_ptr<Declaration> decl_ap; + if (decl_file != 0 || decl_line != 0 || decl_column != 0) + decl_ap.reset(new Declaration(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(decl_file), decl_line, decl_column)); + + std::auto_ptr<Declaration> call_ap; + if (call_file != 0 || call_line != 0 || call_column != 0) + call_ap.reset(new Declaration(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(call_file), call_line, call_column)); + + blocks.SetInlinedFunctionInfo(blockID, name, mangled_name, decl_ap.get(), call_ap.get()); + } + + ++blocks_added; + + if (parse_children && die->HasChildren()) + { + blocks_added += ParseFunctionBlocks(sc, blockID, dwarf_cu, die->GetFirstChild(), subprogram_low_pc, true, true); + } + } + } + break; + default: + break; + } + + if (parse_siblings) + die = die->GetSibling(); + else + die = NULL; + } + return blocks_added; +} + +size_t +SymbolFileDWARF::ParseChildMembers +( + const SymbolContext& sc, + TypeSP& type_sp, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + std::vector<clang::CXXBaseSpecifier *>& base_classes, + std::vector<int>& member_accessibilities, + int& default_accessibility, + bool &is_a_class +) +{ + if (parent_die == NULL) + return 0; + + TypeList* type_list = m_obj_file->GetModule()->GetTypeList(); + + size_t count = 0; + const DWARFDebugInfoEntry *die; + for (die = parent_die->GetFirstChild(); die != NULL; die = die->GetSibling()) + { + dw_tag_t tag = die->Tag(); + + switch (tag) + { + case DW_TAG_member: + { + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_attributes > 0) + { + Declaration decl; + DWARFExpression location; + const char *name = NULL; + lldb::user_id_t encoding_uid = LLDB_INVALID_UID; + uint32_t accessibility = clang::AS_none; + off_t member_offset = 0; + size_t byte_size = 0; + size_t bit_offset = 0; + size_t bit_size = 0; + uint32_t i; + for (i=0; i<num_attributes; ++i) + { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: name = form_value.AsCString(&get_debug_str_data()); break; + case DW_AT_type: encoding_uid = form_value.Reference(dwarf_cu); break; + case DW_AT_bit_offset: bit_offset = form_value.Unsigned(); break; + case DW_AT_bit_size: bit_size = form_value.Unsigned(); break; + case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; + case DW_AT_data_member_location: + if (form_value.BlockData()) + { + Value initialValue(0); + Value memberOffset(0); + const DataExtractor& debug_info_data = get_debug_info_data(); + uint32_t block_length = form_value.Unsigned(); + uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); + if (DWARFExpression::Evaluate(NULL, NULL, debug_info_data, NULL, NULL, block_offset, block_length, eRegisterKindDWARF, &initialValue, memberOffset, NULL)) + { + member_offset = memberOffset.ResolveValue(NULL, NULL).UInt(); + } + } + break; + + case DW_AT_accessibility: accessibility = DwarfToClangAccessibility (form_value.Unsigned()); break; + case DW_AT_declaration: + case DW_AT_description: + case DW_AT_mutable: + case DW_AT_visibility: + default: + case DW_AT_sibling: + break; + } + } + } + + Type *member_type = ResolveTypeUID(encoding_uid); + assert(member_type); + if (accessibility == clang::AS_none) + accessibility = default_accessibility; + member_accessibilities.push_back(accessibility); + + type_list->GetClangASTContext().AddFieldToRecordType (type_sp->GetOpaqueClangQualType(), name, member_type->GetOpaqueClangQualType(), accessibility, bit_size); + } + } + break; + + case DW_TAG_subprogram: + { + is_a_class = true; + if (default_accessibility == clang::AS_none) + default_accessibility = clang::AS_private; + // TODO: implement DW_TAG_subprogram type parsing +// UserDefTypeChildInfo method_info(die->GetOffset()); +// +// FunctionSP func_sp (sc.comp_unit->FindFunctionByUID (die->GetOffset())); +// if (func_sp.get() == NULL) +// ParseCompileUnitFunction(sc, dwarf_cu, die); +// +// method_info.SetEncodingTypeUID(die->GetOffset()); +// struct_udt->AddMethod(method_info); + } + break; + + case DW_TAG_inheritance: + { + is_a_class = true; + if (default_accessibility == clang::AS_none) + default_accessibility = clang::AS_private; + // TODO: implement DW_TAG_inheritance type parsing + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_attributes > 0) + { + Declaration decl; + DWARFExpression location; + lldb::user_id_t encoding_uid = LLDB_INVALID_UID; + uint32_t accessibility = default_accessibility; + bool is_virtual = false; + bool is_base_of_class = true; + off_t member_offset = 0; + uint32_t i; + for (i=0; i<num_attributes; ++i) + { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_type: encoding_uid = form_value.Reference(dwarf_cu); break; + case DW_AT_data_member_location: + if (form_value.BlockData()) + { + Value initialValue(0); + Value memberOffset(0); + const DataExtractor& debug_info_data = get_debug_info_data(); + uint32_t block_length = form_value.Unsigned(); + uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); + if (DWARFExpression::Evaluate(NULL, NULL, debug_info_data, NULL, NULL, block_offset, block_length, eRegisterKindDWARF, &initialValue, memberOffset, NULL)) + { + member_offset = memberOffset.ResolveValue(NULL, NULL).UInt(); + } + } + break; + + case DW_AT_accessibility: + accessibility = DwarfToClangAccessibility(form_value.Unsigned()); + break; + + case DW_AT_virtuality: is_virtual = form_value.Unsigned() != 0; break; + default: + case DW_AT_sibling: + break; + } + } + } + + Type *base_class_dctype = ResolveTypeUID(encoding_uid); + assert(base_class_dctype); + base_classes.push_back (type_list->GetClangASTContext().CreateBaseClassSpecifier (base_class_dctype->GetOpaqueClangQualType(), accessibility, is_virtual, is_base_of_class)); + assert(base_classes.back()); + } + } + break; + + default: + break; + } + } + return count; +} + + +clang::DeclContext* +SymbolFileDWARF::GetClangDeclContextForTypeUID (lldb::user_id_t type_uid) +{ + DWARFDebugInfo* debug_info = DebugInfo(); + if (debug_info) + { + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = debug_info->GetDIEPtr(type_uid, &cu_sp); + if (die) + return GetClangDeclContextForDIE (cu_sp.get(), die); + } + return NULL; +} + +Type* +SymbolFileDWARF::ResolveTypeUID(lldb::user_id_t type_uid) +{ + DWARFDebugInfo* debug_info = DebugInfo(); + if (debug_info) + { + const DWARFDebugInfoEntry* type_die = debug_info->GetDIEPtr(type_uid, NULL); + if (type_die != NULL) + { + void *type = type_die->GetUserData(); + if (type == NULL) + { + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = debug_info->GetDIEPtr(type_uid, &cu_sp); + if (die != NULL) + { + TypeSP owning_type_sp; + TypeSP type_sp(GetTypeForDIE(cu_sp.get(), die, owning_type_sp, 0, 0)); + } + type = type_die->GetUserData(); + } + if (type != DIE_IS_BEING_PARSED) + return (Type *)type; + } + } + return NULL; +} + +CompileUnit* +SymbolFileDWARF::GetCompUnitForDWARFCompUnit(DWARFCompileUnit* cu, uint32_t cu_idx) +{ + // Check if the symbol vendor already knows about this compile unit? + if (cu->GetUserData() == NULL) + { + // The symbol vendor doesn't know about this compile unit, we + // need to parse and add it to the symbol vendor object. + CompUnitSP dc_cu; + ParseCompileUnit(cu, dc_cu); + if (dc_cu.get()) + { + // Figure out the compile unit index if we weren't given one + if (cu_idx == UINT_MAX) + DebugInfo()->GetCompileUnit(cu->GetOffset(), &cu_idx); + + m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(dc_cu, cu_idx); + } + } + return (CompileUnit*)cu->GetUserData(); +} + +bool +SymbolFileDWARF::GetFunction (DWARFCompileUnit* cu, const DWARFDebugInfoEntry* func_die, SymbolContext& sc) +{ + sc.Clear(); + // Check if the symbol vendor already knows about this compile unit? + sc.module_sp = m_obj_file->GetModule()->GetSP(); + sc.comp_unit = GetCompUnitForDWARFCompUnit(cu, UINT_MAX); + + sc.function = sc.comp_unit->FindFunctionByUID (func_die->GetOffset()).get(); + if (sc.function == NULL) + sc.function = ParseCompileUnitFunction(sc, cu, func_die); + + return sc.function != NULL; +} + +uint32_t +SymbolFileDWARF::ResolveSymbolContext (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc) +{ + Timer scoped_timer(__PRETTY_FUNCTION__, + "SymbolFileDWARF::ResolveSymbolContext (so_addr = { section = %p, offset = 0x%llx }, resolve_scope = 0x%8.8x)", + so_addr.GetSection(), + so_addr.GetOffset(), + resolve_scope); + uint32_t resolved = 0; + if (resolve_scope & ( eSymbolContextCompUnit | + eSymbolContextFunction | + eSymbolContextBlock | + eSymbolContextLineEntry)) + { + lldb::addr_t file_vm_addr = so_addr.GetFileAddress(); + + DWARFDebugAranges* debug_aranges = DebugAranges(); + DWARFDebugInfo* debug_info = DebugInfo(); + if (debug_aranges) + { + dw_offset_t cu_offset = debug_aranges->FindAddress(file_vm_addr); + if (cu_offset != DW_INVALID_OFFSET) + { + uint32_t cu_idx; + DWARFCompileUnit* cu = debug_info->GetCompileUnit(cu_offset, &cu_idx).get(); + if (cu) + { + sc.comp_unit = GetCompUnitForDWARFCompUnit(cu, cu_idx); + assert(sc.comp_unit != NULL); + resolved |= eSymbolContextCompUnit; + + if (resolve_scope & eSymbolContextLineEntry) + { + LineTable *line_table = sc.comp_unit->GetLineTable(); + if (line_table == NULL) + { + if (ParseCompileUnitLineTable(sc)) + line_table = sc.comp_unit->GetLineTable(); + } + if (line_table != NULL) + { + if (so_addr.IsLinkedAddress()) + { + Address linked_addr (so_addr); + linked_addr.ResolveLinkedAddress(); + if (line_table->FindLineEntryByAddress (linked_addr, sc.line_entry)) + { + resolved |= eSymbolContextLineEntry; + } + } + else if (line_table->FindLineEntryByAddress (so_addr, sc.line_entry)) + { + resolved |= eSymbolContextLineEntry; + } + } + } + + if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) + { + DWARFDebugInfoEntry *function_die = NULL; + DWARFDebugInfoEntry *block_die = NULL; + if (resolve_scope & eSymbolContextBlock) + { + cu->LookupAddress(file_vm_addr, &function_die, &block_die); + } + else + { + cu->LookupAddress(file_vm_addr, &function_die, NULL); + } + + if (function_die != NULL) + { + sc.function = sc.comp_unit->FindFunctionByUID (function_die->GetOffset()).get(); + if (sc.function == NULL) + sc.function = ParseCompileUnitFunction(sc, cu, function_die); + } + + if (sc.function != NULL) + { + resolved |= eSymbolContextFunction; + + if (resolve_scope & eSymbolContextBlock) + { + BlockList& blocks = sc.function->GetBlocks(true); + + if (block_die != NULL) + sc.block = blocks.GetBlockByID(block_die->GetOffset()); + else + sc.block = blocks.GetBlockByID(function_die->GetOffset()); + if (sc.block) + resolved |= eSymbolContextBlock; + } + } + } + } + } + } + } + return resolved; +} + + + +uint32_t +SymbolFileDWARF::ResolveSymbolContext(const FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) +{ + const uint32_t prev_size = sc_list.GetSize(); + if (resolve_scope & eSymbolContextCompUnit) + { + DWARFDebugInfo* debug_info = DebugInfo(); + if (debug_info) + { + uint32_t cu_idx; + DWARFCompileUnit* cu = NULL; + + for (cu_idx = 0; (cu = debug_info->GetCompileUnitAtIndex(cu_idx)) != NULL; ++cu_idx) + { + CompileUnit *dc_cu = GetCompUnitForDWARFCompUnit(cu, cu_idx); + bool file_spec_matches_cu_file_spec = dc_cu != NULL && FileSpec::Compare(file_spec, *dc_cu, false) == 0; + if (check_inlines || file_spec_matches_cu_file_spec) + { + SymbolContext sc (m_obj_file->GetModule()); + sc.comp_unit = GetCompUnitForDWARFCompUnit(cu, cu_idx); + assert(sc.comp_unit != NULL); + + uint32_t file_idx = UINT32_MAX; + + // If we are looking for inline functions only and we don't + // find it in the support files, we are done. + if (check_inlines) + { + file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex (1, file_spec); + if (file_idx == UINT32_MAX) + continue; + } + + if (line != 0) + { + LineTable *line_table = sc.comp_unit->GetLineTable(); + + if (line_table != NULL && line != 0) + { + // We will have already looked up the file index if + // we are searching for inline entries. + if (!check_inlines) + file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex (1, file_spec); + + if (file_idx != UINT32_MAX) + { + uint32_t found_line; + uint32_t line_idx = line_table->FindLineEntryIndexByFileIndex (0, file_idx, line, false, &sc.line_entry); + found_line = sc.line_entry.line; + + while (line_idx != UINT_MAX) + { + sc.function = NULL; + sc.block = NULL; + if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) + { + const lldb::addr_t file_vm_addr = sc.line_entry.range.GetBaseAddress().GetFileAddress(); + if (file_vm_addr != LLDB_INVALID_ADDRESS) + { + DWARFDebugInfoEntry *function_die = NULL; + DWARFDebugInfoEntry *block_die = NULL; + cu->LookupAddress(file_vm_addr, &function_die, resolve_scope & eSymbolContextBlock ? &block_die : NULL); + + if (function_die != NULL) + { + sc.function = sc.comp_unit->FindFunctionByUID (function_die->GetOffset()).get(); + if (sc.function == NULL) + sc.function = ParseCompileUnitFunction(sc, cu, function_die); + } + + if (sc.function != NULL) + { + BlockList& blocks = sc.function->GetBlocks(true); + + if (block_die != NULL) + sc.block = blocks.GetBlockByID(block_die->GetOffset()); + else + sc.block = blocks.GetBlockByID(function_die->GetOffset()); + } + } + } + + sc_list.Append(sc); + line_idx = line_table->FindLineEntryIndexByFileIndex (line_idx + 1, file_idx, found_line, true, &sc.line_entry); + } + } + } + else if (file_spec_matches_cu_file_spec && !check_inlines) + { + // only append the context if we aren't looking for inline call sites + // by file and line and if the file spec matches that of the compile unit + sc_list.Append(sc); + } + } + else if (file_spec_matches_cu_file_spec && !check_inlines) + { + // only append the context if we aren't looking for inline call sites + // by file and line and if the file spec matches that of the compile unit + sc_list.Append(sc); + } + + if (!check_inlines) + break; + } + } + } + } + return sc_list.GetSize() - prev_size; +} + +void +SymbolFileDWARF::Index () +{ + if (m_indexed) + return; + m_indexed = true; + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileDWARF::Index (%s)", + GetObjectFile()->GetFileSpec().GetFilename().AsCString()); + + DWARFDebugInfo* debug_info = DebugInfo(); + if (debug_info) + { + uint32_t cu_idx = 0; + const uint32_t num_compile_units = GetNumCompileUnits(); + for (cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) + { + DWARFCompileUnit* cu = debug_info->GetCompileUnitAtIndex(cu_idx); + + bool clear_dies = cu->ExtractDIEsIfNeeded (false) > 1; + + cu->Index (m_name_to_function_die, + m_name_to_inlined_die, + m_name_to_global_die, + m_name_to_type_die); + + // Keep memory down by clearing DIEs if this generate function + // caused them to be parsed + if (clear_dies) + cu->ClearDIEs (true); + } + + m_name_to_function_die.Sort(); + m_name_to_inlined_die.Sort(); + m_name_to_global_die.Sort(); + m_name_to_type_die.Sort(); + } +} + +uint32_t +SymbolFileDWARF::FindGlobalVariables (const ConstString &name, bool append, uint32_t max_matches, VariableList& variables) +{ + std::vector<dw_offset_t> die_offsets; + + // If we aren't appending the results to this list, then clear the list + if (!append) + variables.Clear(); + + // Remember how many variables are in the list before we search in case + // we are appending the results to a variable list. + const uint32_t original_size = variables.GetSize(); + + // Index the DWARF if we haven't already + if (!m_indexed) + Index (); + + const UniqueCStringMap<dw_offset_t>::Entry *entry; + + for (entry = m_name_to_global_die.FindFirstValueForName (name.AsCString()); + entry != NULL; + entry = m_name_to_global_die.FindNextValueForName (name.AsCString(), entry)) + { + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = DebugInfo()->GetDIEPtr (entry->value, &cu_sp); + DWARFCompileUnit* cu = cu_sp.get(); + if (die) + { + SymbolContext sc; + sc.module_sp = m_obj_file->GetModule()->GetSP(); + assert (sc.module_sp); + + sc.comp_unit = GetCompUnitForDWARFCompUnit(cu, UINT_MAX); + assert(sc.comp_unit != NULL); + + ParseVariables(sc, cu_sp.get(), die, false, false, &variables); + + if (variables.GetSize() - original_size >= max_matches) + break; + } + } + + // Return the number of variable that were appended to the list + return variables.GetSize() - original_size; +} + +uint32_t +SymbolFileDWARF::FindGlobalVariables(const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variables) +{ + std::vector<dw_offset_t> die_offsets; + + // If we aren't appending the results to this list, then clear the list + if (!append) + variables.Clear(); + + // Remember how many variables are in the list before we search in case + // we are appending the results to a variable list. + const uint32_t original_size = variables.GetSize(); + + // Index the DWARF if we haven't already + if (!m_indexed) + Index (); + + // Create the pubnames information so we can quickly lookup external symbols by name + const size_t num_entries = m_name_to_global_die.GetSize(); + for (size_t i=0; i<num_entries; i++) + { + if (!regex.Execute(m_name_to_global_die.GetCStringAtIndex (i))) + continue; + + const dw_offset_t die_offset = *m_name_to_global_die.GetValueAtIndex (i); + + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = DebugInfo()->GetDIEPtr (die_offset, &cu_sp); + DWARFCompileUnit* cu = cu_sp.get(); + if (die) + { + SymbolContext sc; + sc.module_sp = m_obj_file->GetModule()->GetSP(); + assert (sc.module_sp); + + + sc.comp_unit = GetCompUnitForDWARFCompUnit(cu, UINT_MAX); + assert(sc.comp_unit != NULL); + + ParseVariables(sc, cu_sp.get(), die, false, false, &variables); + + if (variables.GetSize() - original_size >= max_matches) + break; + } + } + + // Return the number of variable that were appended to the list + return variables.GetSize() - original_size; +} + + +uint32_t +SymbolFileDWARF::FindFunctions(const ConstString &name, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileDWARF::FindFunctions (name = '%s')", + name.AsCString()); + + std::vector<dw_offset_t> die_offsets; + + // If we aren't appending the results to this list, then clear the list + if (!append) + sc_list.Clear(); + + // Remember how many sc_list are in the list before we search in case + // we are appending the results to a variable list. + uint32_t original_size = sc_list.GetSize(); + + // Index the DWARF if we haven't already + if (!m_indexed) + Index (); + + const UniqueCStringMap<dw_offset_t>::Entry *entry; + + SymbolContext sc; + for (entry = m_name_to_function_die.FindFirstValueForName (name.AsCString()); + entry != NULL; + entry = m_name_to_function_die.FindNextValueForName (name.AsCString(), entry)) + { + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = DebugInfo()->GetDIEPtr (entry->value, &cu_sp); + if (die) + { + if (GetFunction (cu_sp.get(), die, sc)) + { + // We found the function, so we should find the line table + // and line table entry as well + LineTable *line_table = sc.comp_unit->GetLineTable(); + if (line_table == NULL) + { + if (ParseCompileUnitLineTable(sc)) + line_table = sc.comp_unit->GetLineTable(); + } + if (line_table != NULL) + line_table->FindLineEntryByAddress (sc.function->GetAddressRange().GetBaseAddress(), sc.line_entry); + + sc_list.Append(sc); + } + } + } + + // Return the number of variable that were appended to the list + return sc_list.GetSize() - original_size; +} + + +uint32_t +SymbolFileDWARF::FindFunctions(const RegularExpression& regex, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileDWARF::FindFunctions (regex = '%s')", + regex.GetText()); + + std::vector<dw_offset_t> die_offsets; + + // If we aren't appending the results to this list, then clear the list + if (!append) + sc_list.Clear(); + + // Remember how many sc_list are in the list before we search in case + // we are appending the results to a variable list. + uint32_t original_size = sc_list.GetSize(); + + // Index the DWARF if we haven't already + if (!m_indexed) + Index (); + + // Create the pubnames information so we can quickly lookup external symbols by name + // Create the pubnames information so we can quickly lookup external symbols by name + const size_t num_entries = m_name_to_function_die.GetSize(); + SymbolContext sc; + for (size_t i=0; i<num_entries; i++) + { + if (!regex.Execute(m_name_to_function_die.GetCStringAtIndex (i))) + continue; + + const dw_offset_t die_offset = *m_name_to_function_die.GetValueAtIndex (i); + + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = DebugInfo()->GetDIEPtr (die_offset, &cu_sp); + if (die) + { + if (GetFunction (cu_sp.get(), die, sc)) + { + // We found the function, so we should find the line table + // and line table entry as well + LineTable *line_table = sc.comp_unit->GetLineTable(); + if (line_table == NULL) + { + if (ParseCompileUnitLineTable(sc)) + line_table = sc.comp_unit->GetLineTable(); + } + if (line_table != NULL) + line_table->FindLineEntryByAddress (sc.function->GetAddressRange().GetBaseAddress(), sc.line_entry); + + + sc_list.Append(sc); + } + } + } + + // Return the number of variable that were appended to the list + return sc_list.GetSize() - original_size; +} + +#if 0 +uint32_t +SymbolFileDWARF::FindTypes(const SymbolContext& sc, const ConstString &name, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) +{ + // If we aren't appending the results to this list, then clear the list + if (!append) + types.Clear(); + + // Create the pubnames information so we can quickly lookup external symbols by name + DWARFDebugPubnames* pubtypes = DebugPubtypes(); + if (pubtypes) + { + std::vector<dw_offset_t> die_offsets; + if (!pubtypes->Find(name.AsCString(), false, die_offsets)) + { + DWARFDebugPubnames* pub_base_types = DebugPubBaseTypes(); + if (pub_base_types && !pub_base_types->Find(name.AsCString(), false, die_offsets)) + return 0; + } + return FindTypes(die_offsets, max_matches, encoding, udt_uid, types); + } + return 0; +} + + +uint32_t +SymbolFileDWARF::FindTypes(const SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) +{ + // If we aren't appending the results to this list, then clear the list + if (!append) + types.Clear(); + + // Create the pubnames information so we can quickly lookup external symbols by name + DWARFDebugPubnames* pubtypes = DebugPubtypes(); + if (pubtypes) + { + std::vector<dw_offset_t> die_offsets; + if (!pubtypes->Find(regex, die_offsets)) + { + DWARFDebugPubnames* pub_base_types = DebugPubBaseTypes(); + if (pub_base_types && !pub_base_types->Find(regex, die_offsets)) + return 0; + } + + return FindTypes(die_offsets, max_matches, encoding, udt_uid, types); + } + + return 0; +} + + + +uint32_t +SymbolFileDWARF::FindTypes(std::vector<dw_offset_t> die_offsets, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) +{ + // Remember how many sc_list are in the list before we search in case + // we are appending the results to a variable list. + uint32_t original_size = types.Size(); + + const uint32_t num_die_offsets = die_offsets.size(); + // Parse all of the types we found from the pubtypes matches + uint32_t i; + uint32_t num_matches = 0; + for (i = 0; i < num_die_offsets; ++i) + { + dw_offset_t die_offset = die_offsets[i]; + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = DebugInfo()->GetDIEPtr(die_offset, &cu_sp); + + assert(die != NULL); + + bool get_type_for_die = true; + if (encoding) + { + // Check if this type has already been uniqued and registers with the module? + Type* type = (Type*)die->GetUserData(); + if (type != NULL && type != DIE_IS_BEING_PARSED) + { + get_type_for_die = type->GetEncoding() == encoding; + } + else + { + dw_tag_t tag = die->Tag(); + switch (encoding) + { + case Type::address: + case Type::boolean: + case Type::complex_float: + case Type::float_type: + case Type::signed_int: + case Type::signed_char: + case Type::unsigned_int: + case Type::unsigned_char: + case Type::imaginary_float: + case Type::packed_decimal: + case Type::numeric_string: + case Type::edited_string: + case Type::signed_fixed: + case Type::unsigned_fixed: + case Type::decimal_float: + if (tag != DW_TAG_base_type) + get_type_for_die = false; + else + { + if (die->GetAttributeValueAsUnsigned(this, cu_sp.get(), DW_AT_encoding, Type::invalid) != encoding) + get_type_for_die = false; + } + break; + + case Type::indirect_const: get_type_for_die = tag == DW_TAG_const_type; break; + case Type::indirect_restrict: get_type_for_die = tag == DW_TAG_restrict_type; break; + case Type::indirect_volatile: get_type_for_die = tag == DW_TAG_volatile_type; break; + case Type::indirect_typedef: get_type_for_die = tag == DW_TAG_typedef; break; + case Type::indirect_pointer: get_type_for_die = tag == DW_TAG_pointer_type; break; + case Type::indirect_reference: get_type_for_die = tag == DW_TAG_reference_type; break; + + case Type::user_defined_type: + switch (tag) + { + case DW_TAG_array_type: + get_type_for_die = UserDefTypeArray::OwnsUserDefTypeUID(udt_uid); + break; + + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + get_type_for_die = UserDefTypeStruct::OwnsUserDefTypeUID(udt_uid); + break; + + case DW_TAG_enumeration_type: + get_type_for_die = UserDefTypeEnum::OwnsUserDefTypeUID(udt_uid); + break; + + case DW_TAG_subprogram: + case DW_TAG_subroutine_type: + get_type_for_die = UserDefTypeFunction::OwnsUserDefTypeUID(udt_uid); + break; + } + } + } + } + + if (get_type_for_die) + { + TypeSP owning_type_sp; + TypeSP type_sp(GetTypeForDIE(cu_sp.get(), die, owning_type_sp, NULL, 0, 0)); + + if (type_sp.get()) + { + // See if we are filtering results based on encoding? + bool add_type = (encoding == Type::invalid); + if (!add_type) + { + // We are filtering base on encoding, so lets check the resulting type encoding + add_type = (encoding == type_sp->GetEncoding()); + if (add_type) + { + // The type encoding matches, if this is a user defined type, lets + // make sure the exact user define type uid matches if one was provided + if (encoding == Type::user_defined_type && udt_uid != LLDB_INVALID_UID) + { + UserDefType* udt = type_sp->GetUserDefinedType().get(); + if (udt) + add_type = udt->UserDefinedTypeUID() == udt_uid; + } + } + } + // Add the type to our list as long as everything matched + if (add_type) + { + types.InsertUnique(type_sp); + if (++num_matches >= max_matches) + break; + } + } + } + } + + // Return the number of variable that were appended to the list + return types.Size() - original_size; +} + +#endif + + +size_t +SymbolFileDWARF::ParseChildParameters +( + const SymbolContext& sc, + TypeSP& type_sp, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + TypeList* type_list, + std::vector<void *>& function_param_types, + std::vector<clang::ParmVarDecl*>& function_param_decls +) +{ + if (parent_die == NULL) + return 0; + + size_t count = 0; + const DWARFDebugInfoEntry *die; + for (die = parent_die->GetFirstChild(); die != NULL; die = die->GetSibling()) + { + dw_tag_t tag = die->Tag(); + switch (tag) + { + case DW_TAG_formal_parameter: + { + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_attributes > 0) + { + const char *name = NULL; + Declaration decl; + dw_offset_t param_type_die_offset = DW_INVALID_OFFSET; + // one of None, Auto, Register, Extern, Static, PrivateExtern + + clang::VarDecl::StorageClass storage = clang::VarDecl::None; + uint32_t i; + for (i=0; i<num_attributes; ++i) + { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: name = form_value.AsCString(&get_debug_str_data()); break; + case DW_AT_type: param_type_die_offset = form_value.Reference(dwarf_cu); break; + case DW_AT_location: + // if (form_value.BlockData()) + // { + // const DataExtractor& debug_info_data = debug_info(); + // uint32_t block_length = form_value.Unsigned(); + // DataExtractor location(debug_info_data, form_value.BlockData() - debug_info_data.GetDataStart(), block_length); + // } + // else + // { + // } + // break; + case DW_AT_artificial: + case DW_AT_const_value: + case DW_AT_default_value: + case DW_AT_description: + case DW_AT_endianity: + case DW_AT_is_optional: + case DW_AT_segment: + case DW_AT_variable_parameter: + default: + case DW_AT_abstract_origin: + case DW_AT_sibling: + break; + } + } + } + Type *dc_type = ResolveTypeUID(param_type_die_offset); + if (dc_type) + { + function_param_types.push_back (dc_type->GetOpaqueClangQualType()); + + clang::ParmVarDecl *param_var_decl = type_list->GetClangASTContext().CreateParmeterDeclaration (name, dc_type->GetOpaqueClangQualType(), storage); + assert(param_var_decl); + function_param_decls.push_back(param_var_decl); + } + } + } + break; + + default: + break; + } + } + return count; +} + +size_t +SymbolFileDWARF::ParseChildEnumerators +( + const SymbolContext& sc, + TypeSP& type_sp, + void * enumerator_qual_type, + uint32_t enumerator_byte_size, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die +) +{ + if (parent_die == NULL) + return 0; + + size_t enumerators_added = 0; + const DWARFDebugInfoEntry *die; + for (die = parent_die->GetFirstChild(); die != NULL; die = die->GetSibling()) + { + const dw_tag_t tag = die->Tag(); + if (tag == DW_TAG_enumerator) + { + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_child_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_child_attributes > 0) + { + const char *name = NULL; + bool got_value = false; + int64_t enum_value = 0; + Declaration decl; + + uint32_t i; + for (i=0; i<num_child_attributes; ++i) + { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_const_value: + got_value = true; + enum_value = form_value.Unsigned(); + break; + + case DW_AT_name: + name = form_value.AsCString(&get_debug_str_data()); + break; + + case DW_AT_description: + default: + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_sibling: + break; + } + } + } + + if (name && name[0] && got_value) + { + TypeList* type_list = m_obj_file->GetModule()->GetTypeList(); + type_list->GetClangASTContext().AddEnumerationValueToEnumerationType (type_sp->GetOpaqueClangQualType(), enumerator_qual_type, decl, name, enum_value, enumerator_byte_size * 8); + ++enumerators_added; + } + } + } + } + return enumerators_added; +} + +void +SymbolFileDWARF::ParseChildArrayInfo +( + const SymbolContext& sc, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + int64_t& first_index, + std::vector<uint64_t>& element_orders, + uint32_t& byte_stride, + uint32_t& bit_stride +) +{ + if (parent_die == NULL) + return; + + const DWARFDebugInfoEntry *die; + for (die = parent_die->GetFirstChild(); die != NULL; die = die->GetSibling()) + { + const dw_tag_t tag = die->Tag(); + switch (tag) + { + case DW_TAG_enumerator: + { + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_child_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_child_attributes > 0) + { + const char *name = NULL; + bool got_value = false; + int64_t enum_value = 0; + + uint32_t i; + for (i=0; i<num_child_attributes; ++i) + { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_const_value: + got_value = true; + enum_value = form_value.Unsigned(); + break; + + case DW_AT_name: + name = form_value.AsCString(&get_debug_str_data()); + break; + + case DW_AT_description: + default: + case DW_AT_decl_file: + case DW_AT_decl_line: + case DW_AT_decl_column: + case DW_AT_sibling: + break; + } + } + } + } + } + break; + + case DW_TAG_subrange_type: + { + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_child_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_child_attributes > 0) + { + const char *name = NULL; + bool got_value = false; + uint64_t byte_size = 0; + int64_t enum_value = 0; + uint64_t num_elements = 0; + uint64_t lower_bound = 0; + uint64_t upper_bound = 0; + uint32_t i; + for (i=0; i<num_child_attributes; ++i) + { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_const_value: + got_value = true; + enum_value = form_value.Unsigned(); + break; + + case DW_AT_name: + name = form_value.AsCString(&get_debug_str_data()); + break; + + case DW_AT_count: + num_elements = form_value.Unsigned(); + break; + + case DW_AT_bit_stride: + bit_stride = form_value.Unsigned(); + break; + + case DW_AT_byte_stride: + byte_stride = form_value.Unsigned(); + break; + + case DW_AT_byte_size: + byte_size = form_value.Unsigned(); + break; + + case DW_AT_lower_bound: + lower_bound = form_value.Unsigned(); + break; + + case DW_AT_upper_bound: + upper_bound = form_value.Unsigned(); + break; + + default: + //printf("0x%8.8x: %-30s skipping attribute at 0x%8.8x: %s\n", die->GetOffset(), DW_TAG_value_to_name(tag), attributes.die_offsets[i], DW_AT_value_to_name(attr)); // remove this, debug only + + case DW_AT_abstract_origin: + case DW_AT_accessibility: + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_declaration: + case DW_AT_description: + case DW_AT_sibling: + case DW_AT_threads_scaled: + case DW_AT_type: + case DW_AT_visibility: + break; + } + } + } + + if (upper_bound > lower_bound) + num_elements = upper_bound - lower_bound + 1; + + if (num_elements > 0) + element_orders.push_back (num_elements); + } + } + break; + } + } +} + +Type* +SymbolFileDWARF::GetUniquedTypeForDIEOffset(dw_offset_t type_die_offset, TypeSP& owning_type_sp, int32_t child_type, uint32_t idx, bool safe) +{ + if (type_die_offset != DW_INVALID_OFFSET) + { + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* type_die = DebugInfo()->GetDIEPtr(type_die_offset, &cu_sp); + assert(type_die != NULL); + GetTypeForDIE(cu_sp.get(), type_die, owning_type_sp, child_type, idx); + // Return the uniqued type if there is one + Type* type = (Type*)type_die->GetUserData(); + if (type == DIE_IS_BEING_PARSED && safe) + return NULL; + return type; + } + return NULL; +} + +TypeSP +SymbolFileDWARF::GetTypeForDIE(DWARFCompileUnit *cu, const DWARFDebugInfoEntry* die, TypeSP& owning_type_sp, int32_t child_type, uint32_t idx) +{ + TypeSP type_sp; + if (die != NULL) + { + assert(cu != NULL); + Type *type_ptr = (Type *)die->GetUserData(); + if (type_ptr == NULL) + { + SymbolContext sc(GetCompUnitForDWARFCompUnit(cu)); + bool type_is_new = false; + type_sp = ParseType(sc, cu, die, type_is_new); + type_ptr = (Type *)die->GetUserData(); + if (owning_type_sp.get() == NULL) + owning_type_sp = type_sp; + } + else if (type_ptr != DIE_IS_BEING_PARSED) + { + // Grab the existing type from the master types lists + type_sp = m_obj_file->GetModule()->GetTypeList()->FindType(type_ptr->GetID()); + } + + } + return type_sp; +} + +clang::DeclContext * +SymbolFileDWARF::GetClangDeclContextForDIEOffset (dw_offset_t die_offset) +{ + if (die_offset != DW_INVALID_OFFSET) + { + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = DebugInfo()->GetDIEPtr(die_offset, &cu_sp); + return GetClangDeclContextForDIE (cu_sp.get(), die); + } + return NULL; +} + + + +clang::DeclContext * +SymbolFileDWARF::GetClangDeclContextForDIE (const DWARFCompileUnit *cu, const DWARFDebugInfoEntry *die) +{ + DIEToDeclContextMap::iterator pos = m_die_to_decl_ctx.find(die); + if (pos != m_die_to_decl_ctx.end()) + return pos->second; + + while (die != NULL) + { + switch (die->Tag()) + { + case DW_TAG_namespace: + { + const char *namespace_name = die->GetAttributeValueAsString(this, cu, DW_AT_name, NULL); + if (namespace_name) + { + TypeList* type_list = m_obj_file->GetModule()->GetTypeList(); + assert(type_list); + Declaration decl; // TODO: fill in the decl object + clang::NamespaceDecl *namespace_decl = type_list->GetClangASTContext().GetUniqueNamespaceDeclaration (namespace_name, decl, GetClangDeclContextForDIE (cu, die->GetParent())); + if (namespace_decl) + m_die_to_decl_ctx[die] = (clang::DeclContext*)namespace_decl; + return namespace_decl; + } + } + break; + + default: + break; + } + clang::DeclContext *decl_ctx; + decl_ctx = GetClangDeclContextForDIEOffset (die->GetAttributeValueAsUnsigned(this, cu, DW_AT_specification, DW_INVALID_OFFSET)); + if (decl_ctx) + return decl_ctx; + + decl_ctx = GetClangDeclContextForDIEOffset (die->GetAttributeValueAsUnsigned(this, cu, DW_AT_abstract_origin, DW_INVALID_OFFSET)); + if (decl_ctx) + return decl_ctx; + + die = die->GetParent(); + } + return NULL; +} + +TypeSP +SymbolFileDWARF::ParseType(const SymbolContext& sc, const DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die, bool &type_is_new) +{ + TypeSP type_sp; + + uint32_t accessibility = clang::AS_none; + if (die != NULL) + { + dw_tag_t tag = die->Tag(); + if (die->GetUserData() == NULL) + { + type_is_new = true; + + bool is_forward_declaration = false; + DWARFDebugInfoEntry::Attributes attributes; + const char *type_name_cstr = NULL; + ConstString type_name_dbstr; + Type::EncodingUIDType encoding_uid_type = Type::eIsTypeWithUID; + void *clang_type = NULL; + + TypeList* type_list = m_obj_file->GetModule()->GetTypeList(); + dw_attr_t attr; + + switch (tag) + { + case DW_TAG_base_type: + case DW_TAG_pointer_type: + case DW_TAG_reference_type: + case DW_TAG_typedef: + case DW_TAG_const_type: + case DW_TAG_restrict_type: + case DW_TAG_volatile_type: + { + //printf("0x%8.8x: %s (ParesTypes)\n", die->GetOffset(), DW_TAG_value_to_name(tag)); + // Set a bit that lets us know that we are currently parsing this + const_cast<DWARFDebugInfoEntry*>(die)->SetUserData(DIE_IS_BEING_PARSED); + + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + Declaration decl; + uint32_t encoding = 0; + size_t byte_size = 0; + lldb::user_id_t encoding_uid = LLDB_INVALID_UID; + + if (num_attributes > 0) + { + uint32_t i; + for (i=0; i<num_attributes; ++i) + { + attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: + type_name_cstr = form_value.AsCString(&get_debug_str_data()); + type_name_dbstr.SetCString(type_name_cstr); + break; + case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; + case DW_AT_encoding: encoding = form_value.Unsigned(); break; + case DW_AT_type: encoding_uid = form_value.Reference(dwarf_cu); break; + default: + case DW_AT_sibling: + break; + } + } + } + } + + switch (tag) + { + default: + case DW_TAG_base_type: + clang_type = type_list->GetClangASTContext().GetBuiltinTypeForDWARFEncodingAndBitSize (type_name_cstr, encoding, byte_size * 8); + break; + + case DW_TAG_pointer_type: + // The encoding_uid will be embedded into the + // Type object and will be looked up when the Type::GetOpaqueClangQualType() + encoding_uid_type = Type::ePointerToTypeWithUID; + break; + + case DW_TAG_reference_type: + // The encoding_uid will be embedded into the + // Type object and will be looked up when the Type::GetOpaqueClangQualType() + encoding_uid_type = Type::eLValueReferenceToTypeWithUID; + break; + + case DW_TAG_typedef: + // The encoding_uid will be embedded into the + // Type object and will be looked up when the Type::GetOpaqueClangQualType() + encoding_uid_type = Type::eTypedefToTypeWithUID; + break; + + case DW_TAG_const_type: + // The encoding_uid will be embedded into the + // Type object and will be looked up when the Type::GetOpaqueClangQualType() + encoding_uid_type = Type::eIsConstTypeWithUID; //ClangASTContext::AddConstModifier (clang_type); + break; + + case DW_TAG_restrict_type: + // The encoding_uid will be embedded into the + // Type object and will be looked up when the Type::GetOpaqueClangQualType() + encoding_uid_type = Type::eIsRestrictTypeWithUID; //ClangASTContext::AddRestrictModifier (clang_type); + break; + + case DW_TAG_volatile_type: + // The encoding_uid will be embedded into the + // Type object and will be looked up when the Type::GetOpaqueClangQualType() + encoding_uid_type = Type::eIsVolatileTypeWithUID; //ClangASTContext::AddVolatileModifier (clang_type); + break; + } + + type_sp.reset( new Type(die->GetOffset(), this, type_name_dbstr, byte_size, NULL, encoding_uid, encoding_uid_type, &decl, clang_type)); + + const_cast<DWARFDebugInfoEntry*>(die)->SetUserData(type_sp.get()); + + +// Type* encoding_type = GetUniquedTypeForDIEOffset(encoding_uid, type_sp, NULL, 0, 0, false); +// if (encoding_type != NULL) +// { +// if (encoding_type != DIE_IS_BEING_PARSED) +// type_sp->SetEncodingType(encoding_type); +// else +// m_indirect_fixups.push_back(type_sp.get()); +// } + } + break; + + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + { + //printf("0x%8.8x: %s (ParesTypes)\n", die->GetOffset(), DW_TAG_value_to_name(tag)); + // Set a bit that lets us know that we are currently parsing this + const_cast<DWARFDebugInfoEntry*>(die)->SetUserData(DIE_IS_BEING_PARSED); + + size_t byte_size = 0; + //bool struct_is_class = false; + Declaration decl; + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_attributes > 0) + { + uint32_t i; + for (i=0; i<num_attributes; ++i) + { + attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: + type_name_cstr = form_value.AsCString(&get_debug_str_data()); + type_name_dbstr.SetCString(type_name_cstr); + break; + case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; + case DW_AT_accessibility: accessibility = DwarfToClangAccessibility(form_value.Unsigned()); break; break; + case DW_AT_declaration: is_forward_declaration = form_value.Unsigned() != 0; break; + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_description: + case DW_AT_start_scope: + case DW_AT_visibility: + default: + case DW_AT_sibling: + break; + } + } + } + } + + int tag_decl_kind = -1; + int default_accessibility = clang::AS_none; + if (tag == DW_TAG_structure_type) + { + tag_decl_kind = clang::TTK_Struct; + default_accessibility = clang::AS_public; + } + else if (tag == DW_TAG_union_type) + { + tag_decl_kind = clang::TTK_Union; + default_accessibility = clang::AS_public; + } + else if (tag == DW_TAG_class_type) + { + tag_decl_kind = clang::TTK_Class; + default_accessibility = clang::AS_private; + } + + assert (tag_decl_kind != -1); + clang_type = type_list->GetClangASTContext().CreateRecordType (type_name_cstr, tag_decl_kind, GetClangDeclContextForDIE (dwarf_cu, die)); + + m_die_to_decl_ctx[die] = ClangASTContext::GetDeclContextForType (clang_type); + type_sp.reset( new Type(die->GetOffset(), this, type_name_dbstr, byte_size, NULL, LLDB_INVALID_UID, Type::eIsTypeWithUID, &decl, clang_type)); + + const_cast<DWARFDebugInfoEntry*>(die)->SetUserData(type_sp.get()); + +// assert(type_sp.get()); +// if (accessibility) +// type_sp->SetAccess(accessibility); +// + type_list->GetClangASTContext().StartTagDeclarationDefinition (clang_type); + if (die->HasChildren()) + { + std::vector<clang::CXXBaseSpecifier *> base_classes; + std::vector<int> member_accessibilities; + bool is_a_class = false; + ParseChildMembers(sc, type_sp, dwarf_cu, die, base_classes, member_accessibilities, default_accessibility, is_a_class); + // If we have a DW_TAG_structure_type instead of a DW_TAG_class_type we + // need to tell the clang type it is actually a class. + if (is_a_class && tag_decl_kind != clang::TTK_Class) + type_list->GetClangASTContext().SetTagTypeKind (clang_type, clang::TTK_Class); + + // Since DW_TAG_structure_type gets used for both classes + // and structures, we may need to set any DW_TAG_member + // fields to have a "private" access if none was specified. + // When we parsed the child members we tracked that actual + // accessibility value for each DW_TAG_member in the + // "member_accessibilities" array. If the value for the + // member is zero, then it was set to the "default_accessibility" + // which for structs was "public". Below we correct this + // by setting any fields to "private" that weren't correctly + // set. + if (is_a_class && !member_accessibilities.empty()) + { + // This is a class and all members that didn't have + // their access specified are private. + type_list->GetClangASTContext().SetDefaultAccessForRecordFields (clang_type, clang::AS_private, member_accessibilities.data(), member_accessibilities.size()); + } + + if (!base_classes.empty()) + { + type_list->GetClangASTContext().SetBaseClassesForClassType (clang_type, base_classes.data(), base_classes.size()); + } + } + type_list->GetClangASTContext().CompleteTagDeclarationDefinition (clang_type); + } + break; + + case DW_TAG_enumeration_type: + { + //printf("0x%8.8x: %s (ParesTypes)\n", die->GetOffset(), DW_TAG_value_to_name(tag)); + // Set a bit that lets us know that we are currently parsing this + const_cast<DWARFDebugInfoEntry*>(die)->SetUserData(DIE_IS_BEING_PARSED); + + size_t byte_size = 0; + lldb::user_id_t encoding_uid = DW_INVALID_OFFSET; + Declaration decl; + + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_attributes > 0) + { + uint32_t i; + + for (i=0; i<num_attributes; ++i) + { + attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: + type_name_cstr = form_value.AsCString(&get_debug_str_data()); + type_name_dbstr.SetCString(type_name_cstr); + break; + case DW_AT_type: encoding_uid = form_value.Reference(dwarf_cu); break; + case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; + case DW_AT_accessibility: accessibility = DwarfToClangAccessibility(form_value.Unsigned()); break; + case DW_AT_declaration: is_forward_declaration = form_value.Unsigned() != 0; break; + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_bit_stride: + case DW_AT_byte_stride: + case DW_AT_data_location: + case DW_AT_description: + case DW_AT_start_scope: + case DW_AT_visibility: + case DW_AT_specification: + case DW_AT_abstract_origin: + case DW_AT_sibling: + break; + } + } + } + + clang_type = type_list->GetClangASTContext().CreateEnumerationType(decl, type_name_cstr); + m_die_to_decl_ctx[die] = ClangASTContext::GetDeclContextForType (clang_type); + type_sp.reset( new Type(die->GetOffset(), this, type_name_dbstr, byte_size, NULL, encoding_uid, Type::eIsTypeWithUID, &decl, clang_type)); + + const_cast<DWARFDebugInfoEntry*>(die)->SetUserData(type_sp.get()); + + if (die->HasChildren()) + { + type_list->GetClangASTContext().StartTagDeclarationDefinition (clang_type); + void *enumerator_qual_type = type_list->GetClangASTContext().GetBuiltinTypeForDWARFEncodingAndBitSize (NULL, DW_ATE_signed, byte_size * 8); + ParseChildEnumerators(sc, type_sp, enumerator_qual_type, byte_size, dwarf_cu, die); + type_list->GetClangASTContext().CompleteTagDeclarationDefinition (clang_type); + } + } + } + break; + + case DW_TAG_subprogram: + case DW_TAG_subroutine_type: + { + //printf("0x%8.8x: %s (ParesTypes)\n", die->GetOffset(), DW_TAG_value_to_name(tag)); + // Set a bit that lets us know that we are currently parsing this + const_cast<DWARFDebugInfoEntry*>(die)->SetUserData(DIE_IS_BEING_PARSED); + + const char *mangled = NULL; + dw_offset_t type_die_offset = DW_INVALID_OFFSET; + Declaration decl; + bool isVariadic = false; + bool is_inline = false; + unsigned type_quals = 0; + clang::FunctionDecl::StorageClass storage = clang::FunctionDecl::None;//, Extern, Static, PrivateExtern + + + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_attributes > 0) + { + uint32_t i; + for (i=0; i<num_attributes; ++i) + { + attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: + type_name_cstr = form_value.AsCString(&get_debug_str_data()); + type_name_dbstr.SetCString(type_name_cstr); + break; + + case DW_AT_MIPS_linkage_name: mangled = form_value.AsCString(&get_debug_str_data()); break; + case DW_AT_type: type_die_offset = form_value.Reference(dwarf_cu); break; + case DW_AT_accessibility: accessibility = DwarfToClangAccessibility(form_value.Unsigned()); break; + case DW_AT_declaration: is_forward_declaration = form_value.Unsigned() != 0; break; + case DW_AT_external: + if (form_value.Unsigned()) + { + if (storage == clang::FunctionDecl::None) + storage = clang::FunctionDecl::Extern; + else + storage = clang::FunctionDecl::PrivateExtern; + } + break; + case DW_AT_inline: + is_inline = form_value.Unsigned() != 0; + break; + + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_address_class: + case DW_AT_artificial: + case DW_AT_calling_convention: + case DW_AT_data_location: + case DW_AT_elemental: + case DW_AT_entry_pc: + case DW_AT_explicit: + case DW_AT_frame_base: + case DW_AT_high_pc: + case DW_AT_low_pc: + case DW_AT_object_pointer: + case DW_AT_prototyped: + case DW_AT_pure: + case DW_AT_ranges: + case DW_AT_recursive: + case DW_AT_return_addr: + case DW_AT_segment: + case DW_AT_specification: + case DW_AT_start_scope: + case DW_AT_static_link: + case DW_AT_trampoline: + case DW_AT_visibility: + case DW_AT_virtuality: + case DW_AT_vtable_elem_location: + case DW_AT_abstract_origin: + case DW_AT_description: + case DW_AT_sibling: + break; + } + } + } + + void *return_clang_type = NULL; + Type *func_type = ResolveTypeUID(type_die_offset); + if (func_type) + return_clang_type = func_type->GetOpaqueClangQualType(); + else + return_clang_type = type_list->GetClangASTContext().GetVoidBuiltInType(); + + std::vector<void *> function_param_types; + std::vector<clang::ParmVarDecl*> function_param_decls; + + // Parse the function children for the parameters + ParseChildParameters(sc, type_sp, dwarf_cu, die, type_list, function_param_types, function_param_decls); + + clang_type = type_list->GetClangASTContext().CreateFunctionType (return_clang_type, &function_param_types[0], function_param_types.size(), isVariadic, type_quals); + if (type_name_cstr) + { + clang::FunctionDecl *function_decl = type_list->GetClangASTContext().CreateFunctionDeclaration (type_name_cstr, clang_type, storage, is_inline); + // Add the decl to our DIE to decl context map + assert (function_decl); + m_die_to_decl_ctx[die] = function_decl; + if (!function_param_decls.empty()) + type_list->GetClangASTContext().SetFunctionParameters (function_decl, function_param_decls.data(), function_param_decls.size()); + } + type_sp.reset( new Type(die->GetOffset(), this, type_name_dbstr, 0, NULL, LLDB_INVALID_UID, Type::eIsTypeWithUID, &decl, clang_type)); + + const_cast<DWARFDebugInfoEntry*>(die)->SetUserData(type_sp.get()); + assert(type_sp.get()); + } + } + break; + + case DW_TAG_array_type: + { + //printf("0x%8.8x: %s (ParesTypes)\n", die->GetOffset(), DW_TAG_value_to_name(tag)); + // Set a bit that lets us know that we are currently parsing this + const_cast<DWARFDebugInfoEntry*>(die)->SetUserData(DIE_IS_BEING_PARSED); + + size_t byte_size = 0; + lldb::user_id_t type_die_offset = DW_INVALID_OFFSET; + Declaration decl; + int64_t first_index = 0; + uint32_t byte_stride = 0; + uint32_t bit_stride = 0; + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + + if (num_attributes > 0) + { + uint32_t i; + for (i=0; i<num_attributes; ++i) + { + attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: + type_name_cstr = form_value.AsCString(&get_debug_str_data()); + type_name_dbstr.SetCString(type_name_cstr); + break; + + case DW_AT_type: type_die_offset = form_value.Reference(dwarf_cu); break; + case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; + case DW_AT_byte_stride: byte_stride = form_value.Unsigned(); break; + case DW_AT_bit_stride: bit_stride = form_value.Unsigned(); break; + case DW_AT_accessibility: accessibility = DwarfToClangAccessibility(form_value.Unsigned()); break; + case DW_AT_declaration: is_forward_declaration = form_value.Unsigned() != 0; break; + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_description: + case DW_AT_ordering: + case DW_AT_start_scope: + case DW_AT_visibility: + case DW_AT_specification: + case DW_AT_abstract_origin: + case DW_AT_sibling: + break; + } + } + } + + Type *element_type = ResolveTypeUID(type_die_offset); + + if (element_type) + { + std::vector<uint64_t> element_orders; + ParseChildArrayInfo(sc, dwarf_cu, die, first_index, element_orders, byte_stride, bit_stride); + if (byte_stride == 0 && bit_stride == 0) + byte_stride = element_type->GetByteSize(); + void *array_element_type = element_type->GetOpaqueClangQualType(); + uint64_t array_element_bit_stride = byte_stride * 8 + bit_stride; + uint64_t num_elements = 0; + std::vector<uint64_t>::const_reverse_iterator pos; + std::vector<uint64_t>::const_reverse_iterator end = element_orders.rend(); + for (pos = element_orders.rbegin(); pos != end; ++pos) + { + num_elements = *pos; + clang_type = type_list->GetClangASTContext().CreateArrayType (array_element_type, num_elements, num_elements * array_element_bit_stride); + array_element_type = clang_type; + array_element_bit_stride = array_element_bit_stride * num_elements; + } + ConstString empty_name; + type_sp.reset( new Type(die->GetOffset(), this, empty_name, array_element_bit_stride / 8, NULL, LLDB_INVALID_UID, Type::eIsTypeWithUID, &decl, clang_type)); + const_cast<DWARFDebugInfoEntry*>(die)->SetUserData(type_sp.get()); + } + } + } + break; + + default: + break; + } + + if (type_sp.get()) + { + const DWARFDebugInfoEntry *sc_parent_die = GetParentSymbolContextDIE(die); + dw_tag_t sc_parent_tag = sc_parent_die ? sc_parent_die->Tag() : 0; + + SymbolContextScope * symbol_context_scope = NULL; + if (sc_parent_tag == DW_TAG_compile_unit) + { + symbol_context_scope = sc.comp_unit; + } + else if (sc.function != NULL) + { + symbol_context_scope = sc.function->GetBlocks(true).GetBlockByID(sc_parent_die->GetOffset()); + if (symbol_context_scope == NULL) + symbol_context_scope = sc.function; + } + + if (symbol_context_scope != NULL) + { + type_sp->SetSymbolContextScope(symbol_context_scope); + } + +// if (udt_sp.get()) +// { +// if (is_forward_declaration) +// udt_sp->GetFlags().Set(UserDefType::flagIsForwardDefinition); +// type_sp->SetUserDefinedType(udt_sp); +// } + + if (type_sp.unique()) + { + // We are ready to put this type into the uniqued list up at the module level + TypeSP uniqued_type_sp(m_obj_file->GetModule()->GetTypeList()->InsertUnique(type_sp)); + + const_cast<DWARFDebugInfoEntry*>(die)->SetUserData(uniqued_type_sp.get()); + + type_sp = uniqued_type_sp; + } + } + } + else + { + switch (tag) + { + case DW_TAG_base_type: + case DW_TAG_pointer_type: + case DW_TAG_reference_type: + case DW_TAG_typedef: + case DW_TAG_const_type: + case DW_TAG_restrict_type: + case DW_TAG_volatile_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + case DW_TAG_enumeration_type: + case DW_TAG_subprogram: + case DW_TAG_subroutine_type: + case DW_TAG_array_type: + { + Type *existing_type = (Type*)die->GetUserData(); + if (existing_type != DIE_IS_BEING_PARSED) + { + type_sp = m_obj_file->GetModule()->GetTypeList()->FindType(existing_type->GetID()); + } + } + break; + default: + //assert(!"invalid type tag..."); + break; + } + } + } + return type_sp; +} + +size_t +SymbolFileDWARF::ParseTypes (const SymbolContext& sc, const DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die, bool parse_siblings, bool parse_children) +{ + size_t types_added = 0; + while (die != NULL) + { + bool type_is_new = false; + if (ParseType(sc, dwarf_cu, die, type_is_new).get()) + { + if (type_is_new) + ++types_added; + } + + if (parse_children && die->HasChildren()) + { + if (die->Tag() == DW_TAG_subprogram) + { + SymbolContext child_sc(sc); + child_sc.function = sc.comp_unit->FindFunctionByUID(die->GetOffset()).get(); + types_added += ParseTypes(child_sc, dwarf_cu, die->GetFirstChild(), true, true); + } + else + types_added += ParseTypes(sc, dwarf_cu, die->GetFirstChild(), true, true); + } + + if (parse_siblings) + die = die->GetSibling(); + else + die = NULL; + } + return types_added; +} + + +size_t +SymbolFileDWARF::ParseFunctionBlocks (const SymbolContext &sc) +{ + assert(sc.comp_unit && sc.function); + size_t functions_added = 0; + DWARFCompileUnit* dwarf_cu = GetDWARFCompileUnitForUID(sc.comp_unit->GetID()); + if (dwarf_cu) + { + dw_offset_t function_die_offset = sc.function->GetID(); + const DWARFDebugInfoEntry *function_die = dwarf_cu->GetDIEPtr(function_die_offset); + if (function_die) + { + ParseFunctionBlocks(sc, Block::RootID, dwarf_cu, function_die, LLDB_INVALID_ADDRESS, false, true); + } + } + + return functions_added; +} + + +size_t +SymbolFileDWARF::ParseTypes (const SymbolContext &sc) +{ + // At least a compile unit must be valid + assert(sc.comp_unit); + size_t types_added = 0; + DWARFCompileUnit* dwarf_cu = GetDWARFCompileUnitForUID(sc.comp_unit->GetID()); + if (dwarf_cu) + { + if (sc.function) + { + dw_offset_t function_die_offset = sc.function->GetID(); + const DWARFDebugInfoEntry *func_die = dwarf_cu->GetDIEPtr(function_die_offset); + if (func_die && func_die->HasChildren()) + { + types_added = ParseTypes(sc, dwarf_cu, func_die->GetFirstChild(), true, true); + } + } + else + { + const DWARFDebugInfoEntry *dwarf_cu_die = dwarf_cu->DIE(); + if (dwarf_cu_die && dwarf_cu_die->HasChildren()) + { + types_added = ParseTypes(sc, dwarf_cu, dwarf_cu_die->GetFirstChild(), true, true); + } + } + } + + return types_added; +} + +size_t +SymbolFileDWARF::ParseVariablesForContext (const SymbolContext& sc) +{ + if (sc.comp_unit != NULL) + { + DWARFCompileUnit* dwarf_cu = GetDWARFCompileUnitForUID(sc.comp_unit->GetID()); + + if (dwarf_cu == NULL) + return 0; + + if (sc.function) + { + const DWARFDebugInfoEntry *function_die = dwarf_cu->GetDIEPtr(sc.function->GetID()); + return ParseVariables(sc, dwarf_cu, function_die->GetFirstChild(), true, true); + } + else if (sc.comp_unit) + { + uint32_t vars_added = 0; + VariableListSP variables (sc.comp_unit->GetVariableList(false)); + + if (variables.get() == NULL) + { + variables.reset(new VariableList()); + sc.comp_unit->SetVariableList(variables); + + // Index if we already haven't to make sure the compile units + // get indexed and make their global DIE index list + if (!m_indexed) + Index (); + + const size_t num_globals = dwarf_cu->GetNumGlobals(); + for (size_t idx=0; idx<num_globals; ++idx) + { + VariableSP var_sp (ParseVariableDIE(sc, dwarf_cu, dwarf_cu->GetGlobalDIEAtIndex (idx))); + if (var_sp) + { + variables->AddVariable(var_sp); + ++vars_added; + } + } + } + return vars_added; + } + } + return 0; +} + + +VariableSP +SymbolFileDWARF::ParseVariableDIE +( + const SymbolContext& sc, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die +) +{ + + VariableSP var_sp; + + const dw_tag_t tag = die->Tag(); + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, attributes); + if (num_attributes > 0) + { + const char *name = NULL; + Declaration decl; + uint32_t i; + TypeSP type_sp; + Type *var_type = NULL; + DWARFExpression location; + bool is_external = false; + bool is_artificial = false; + uint32_t accessibility = clang::AS_none; + + for (i=0; i<num_attributes; ++i) + { + dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: name = form_value.AsCString(&get_debug_str_data()); break; + case DW_AT_type: var_type = GetUniquedTypeForDIEOffset(form_value.Reference(dwarf_cu), type_sp, 0, 0, false); break; + case DW_AT_external: is_external = form_value.Unsigned() != 0; break; + case DW_AT_location: + { + if (form_value.BlockData()) + { + const DataExtractor& debug_info_data = get_debug_info_data(); + + uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); + uint32_t block_length = form_value.Unsigned(); + location.SetOpcodeData(get_debug_info_data(), block_offset, block_length, NULL); + } + else + { + const DataExtractor& debug_loc_data = get_debug_loc_data(); + const dw_offset_t debug_loc_offset = form_value.Unsigned(); + + size_t loc_list_length = DWARFLocationList::Size(debug_loc_data, debug_loc_offset); + if (loc_list_length > 0) + { + Address base_address(dwarf_cu->GetBaseAddress(), m_obj_file->GetSectionList()); + location.SetOpcodeData(debug_loc_data, debug_loc_offset, loc_list_length, &base_address); + } + } + } + break; + + case DW_AT_artificial: is_artificial = form_value.Unsigned() != 0; break; + case DW_AT_accessibility: accessibility = DwarfToClangAccessibility(form_value.Unsigned()); break; + case DW_AT_const_value: + case DW_AT_declaration: + case DW_AT_description: + case DW_AT_endianity: + case DW_AT_segment: + case DW_AT_start_scope: + case DW_AT_visibility: + default: + case DW_AT_abstract_origin: + case DW_AT_sibling: + case DW_AT_specification: + break; + } + } + } + + if (location.IsValid()) + { + assert(var_type != DIE_IS_BEING_PARSED); + + ConstString var_name(name); + + ValueType scope = eValueTypeInvalid; + + const DWARFDebugInfoEntry *sc_parent_die = GetParentSymbolContextDIE(die); + dw_tag_t parent_tag = sc_parent_die ? sc_parent_die->Tag() : 0; + + if (tag == DW_TAG_formal_parameter) + scope = eValueTypeVariableArgument; + else if (is_external || parent_tag == DW_TAG_compile_unit) + scope = eValueTypeVariableGlobal; + else + scope = eValueTypeVariableLocal; + + SymbolContextScope * symbol_context_scope = NULL; + if (parent_tag == DW_TAG_compile_unit) + { + symbol_context_scope = sc.comp_unit; + } + else if (sc.function != NULL) + { + symbol_context_scope = sc.function->GetBlocks(true).GetBlockByID(sc_parent_die->GetOffset()); + if (symbol_context_scope == NULL) + symbol_context_scope = sc.function; + } + + assert(symbol_context_scope != NULL); + var_sp.reset (new Variable(die->GetOffset(), + var_name, + var_type, + scope, + symbol_context_scope, + &decl, + location, + is_external, + is_artificial)); + const_cast<DWARFDebugInfoEntry*>(die)->SetUserData(var_sp.get()); + } + } + return var_sp; +} + +size_t +SymbolFileDWARF::ParseVariables +( + const SymbolContext& sc, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *orig_die, + bool parse_siblings, + bool parse_children, + VariableList* cc_variable_list +) +{ + if (orig_die == NULL) + return 0; + + size_t vars_added = 0; + const DWARFDebugInfoEntry *die = orig_die; + const DWARFDebugInfoEntry *sc_parent_die = GetParentSymbolContextDIE(orig_die); + dw_tag_t parent_tag = sc_parent_die ? sc_parent_die->Tag() : 0; + VariableListSP variables; + switch (parent_tag) + { + case DW_TAG_compile_unit: + if (sc.comp_unit != NULL) + { + variables = sc.comp_unit->GetVariableList(false); + if (variables.get() == NULL) + { + variables.reset(new VariableList()); + sc.comp_unit->SetVariableList(variables); + } + } + else + { + assert(!"Parent DIE was a compile unit, yet we don't have a valid compile unit in the symbol context..."); + vars_added = 0; + } + break; + + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: + if (sc.function != NULL) + { + // Check to see if we already have parsed the variables for the given scope + variables = sc.function->GetBlocks(true).GetVariableList(sc_parent_die->GetOffset(), false, false); + if (variables.get() == NULL) + { + variables.reset(new VariableList()); + sc.function->GetBlocks(true).SetVariableList(sc_parent_die->GetOffset(), variables); + } + } + else + { + assert(!"Parent DIE was a function or block, yet we don't have a function in the symbol context..."); + vars_added = 0; + } + break; + + default: + assert(!"Didn't find appropriate parent DIE for variable list..."); + break; + } + + // We need to have a variable list at this point that we can add variables to + assert(variables.get()); + + while (die != NULL) + { + dw_tag_t tag = die->Tag(); + + // Check to see if we have already parsed this variable or constant? + if (die->GetUserData() == NULL) + { + // We haven't already parsed it, lets do that now. + if ((tag == DW_TAG_variable) || + (tag == DW_TAG_constant) || + (tag == DW_TAG_formal_parameter && sc.function)) + { + VariableSP var_sp (ParseVariableDIE(sc, dwarf_cu, die)); + if (var_sp) + { + variables->AddVariable(var_sp); + ++vars_added; + } + } + } + + bool skip_children = (sc.function == NULL && tag == DW_TAG_subprogram); + + if (!skip_children && parse_children && die->HasChildren()) + { + vars_added += ParseVariables(sc, dwarf_cu, die->GetFirstChild(), true, true); + //vars_added += ParseVariables(sc, dwarf_cu, die->GetFirstChild(), parse_siblings, parse_children); + } + + if (parse_siblings) + die = die->GetSibling(); + else + die = NULL; + } + + if (cc_variable_list) + { + cc_variable_list->AddVariables(variables.get()); + } + + return vars_added; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +SymbolFileDWARF::GetPluginName() +{ + return "SymbolFileDWARF"; +} + +const char * +SymbolFileDWARF::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +SymbolFileDWARF::GetPluginVersion() +{ + return 1; +} + +void +SymbolFileDWARF::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +SymbolFileDWARF::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +SymbolFileDWARF::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h new file mode 100644 index 00000000000..95545a4844e --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -0,0 +1,331 @@ +//===-- SymbolFileDWARF.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_SymbolFileDWARF_h_ +#define liblldb_SymbolFileDWARF_h_ + +// C Includes +// C++ Includes +#include <list> +#include <memory> +#include <map> +#include <vector> + +// Other libraries and framework includes +#include "llvm/ADT/DenseMap.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Flags.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolContext.h" + +// Project includes +#include "DWARFDefines.h" + + +//---------------------------------------------------------------------- +// Forward Declarations for this DWARF plugin +//---------------------------------------------------------------------- +class DWARFAbbreviationDeclaration; +class DWARFAbbreviationDeclarationSet; +class DWARFCompileUnit; +class DWARFDebugAbbrev; +class DWARFDebugAranges; +class DWARFDebugInfo; +class DWARFDebugInfoEntry; +class DWARFDebugLine; +class DWARFDebugPubnames; +class DWARFDebugRanges; +class DWARFDIECollection; +class DWARFFormValue; + +class SymbolFileDWARF : public lldb_private::SymbolFile +{ +public: + friend class SymbolFileDWARFDebugMap; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile* + CreateInstance (lldb_private::ObjectFile* obj_file); + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolFileDWARF(lldb_private::ObjectFile* ofile); + virtual ~SymbolFileDWARF(); + + virtual uint32_t GetAbilities (); + + //------------------------------------------------------------------ + // Compile Unit function calls + //------------------------------------------------------------------ + virtual uint32_t GetNumCompileUnits(); + virtual lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index); + + virtual size_t ParseCompileUnitFunctions (const lldb_private::SymbolContext& sc); + virtual bool ParseCompileUnitLineTable (const lldb_private::SymbolContext& sc); + virtual bool ParseCompileUnitSupportFiles (const lldb_private::SymbolContext& sc, lldb_private::FileSpecList& support_files); + virtual size_t ParseFunctionBlocks (const lldb_private::SymbolContext& sc); + virtual size_t ParseTypes (const lldb_private::SymbolContext& sc); + virtual size_t ParseVariablesForContext (const lldb_private::SymbolContext& sc); + + virtual lldb_private::Type* ResolveTypeUID(lldb::user_id_t type_uid); + virtual clang::DeclContext* GetClangDeclContextForTypeUID (lldb::user_id_t type_uid); + + virtual uint32_t ResolveSymbolContext (const lldb_private::Address& so_addr, uint32_t resolve_scope, lldb_private::SymbolContext& sc); + virtual uint32_t ResolveSymbolContext (const lldb_private::FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, lldb_private::SymbolContextList& sc_list); + virtual uint32_t FindGlobalVariables(const lldb_private::ConstString &name, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + virtual uint32_t FindGlobalVariables(const lldb_private::RegularExpression& regex, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + virtual uint32_t FindFunctions(const lldb_private::ConstString &name, bool append, lldb_private::SymbolContextList& sc_list); + virtual uint32_t FindFunctions(const lldb_private::RegularExpression& regex, bool append, lldb_private::SymbolContextList& sc_list); +// virtual uint32_t FindTypes(const lldb_private::SymbolContext& sc, const lldb_private::ConstString &name, bool append, uint32_t max_matches, lldb::Type::Encoding encoding, lldb::user_id_t udt_uid, lldb_private::TypeList& types); +// virtual uint32_t FindTypes(const lldb_private::SymbolContext& sc, const lldb_private::RegularExpression& regex, bool append, uint32_t max_matches, lldb::Type::Encoding encoding, lldb::user_id_t udt_uid, lldb_private::TypeList& types); + + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + + // Approach 2 - count + accessor + // Index compile units would scan the initial compile units and register + // them with the module. This would only be done on demand if and only if + // the compile units were needed. + //virtual size_t GetCompUnitCount() = 0; + //virtual CompUnitSP GetCompUnitAtIndex(size_t cu_idx) = 0; + + const lldb_private::DataExtractor& get_debug_abbrev_data(); + const lldb_private::DataExtractor& get_debug_aranges_data(); + const lldb_private::DataExtractor& get_debug_frame_data(); + const lldb_private::DataExtractor& get_debug_info_data(); + const lldb_private::DataExtractor& get_debug_line_data(); + const lldb_private::DataExtractor& get_debug_loc_data(); + const lldb_private::DataExtractor& get_debug_macinfo_data(); + const lldb_private::DataExtractor& get_debug_pubnames_data(); + const lldb_private::DataExtractor& get_debug_pubtypes_data(); + const lldb_private::DataExtractor& get_debug_ranges_data(); + const lldb_private::DataExtractor& get_debug_str_data(); + + DWARFDebugAbbrev* DebugAbbrev(); + const DWARFDebugAbbrev* DebugAbbrev() const; + + DWARFDebugAranges* DebugAranges(); + const DWARFDebugAranges*DebugAranges() const; + + DWARFDebugInfo* DebugInfo(); + const DWARFDebugInfo* DebugInfo() const; + +// These shouldn't be used unless we want to dump the DWARF line tables. +// DWARFDebugLine* DebugLine(); +// const DWARFDebugLine* DebugLine() const; + +// DWARFDebugPubnames* DebugPubnames(); +// const DWARFDebugPubnames* DebugPubnames() const; +// +// DWARFDebugPubnames* DebugPubBaseTypes(); +// const DWARFDebugPubnames* DebugPubBaseTypes() const; +// +// DWARFDebugPubnames* DebugPubtypes(); +// const DWARFDebugPubnames* DebugPubtypes() const; + + DWARFDebugRanges* DebugRanges(); + const DWARFDebugRanges* DebugRanges() const; + + const lldb_private::DataExtractor& + GetCachedSectionData (uint32_t got_flag, const lldb_private::ConstString §ion_name, lldb_private::DataExtractor &data); + + static bool SupportedVersion(uint16_t version); + + clang::DeclContext * + GetClangDeclContextForDIE (const DWARFCompileUnit *cu, const DWARFDebugInfoEntry *die); + + clang::DeclContext * + GetClangDeclContextForDIEOffset (dw_offset_t die_offset); + + lldb_private::Flags& + GetFlags () + { + return m_flags; + } + + const lldb_private::Flags& + GetFlags () const + { + return m_flags; + } + +protected: + + enum + { + flagsGotDebugAbbrevData = (1 << 0), + flagsGotDebugArangesData = (1 << 1), + flagsGotDebugFrameData = (1 << 2), + flagsGotDebugInfoData = (1 << 3), + flagsGotDebugLineData = (1 << 4), + flagsGotDebugLocData = (1 << 5), + flagsGotDebugMacInfoData = (1 << 6), + flagsGotDebugPubNamesData = (1 << 7), + flagsGotDebugPubTypesData = (1 << 8), + flagsGotDebugRangesData = (1 << 9), + flagsGotDebugStrData = (1 << 10), + // True if this is a .o file used when resolving a N_OSO entry with + // debug maps. + flagsDWARFIsOSOForDebugMap = (1 << 16) + }; + + DISALLOW_COPY_AND_ASSIGN (SymbolFileDWARF); + bool ParseCompileUnit(DWARFCompileUnit* cu, lldb::CompUnitSP& compile_unit_sp); + DWARFCompileUnit* GetDWARFCompileUnitForUID(lldb::user_id_t cu_uid); + DWARFCompileUnit* GetNextUnparsedDWARFCompileUnit(DWARFCompileUnit* prev_cu); + lldb_private::CompileUnit* GetCompUnitForDWARFCompUnit(DWARFCompileUnit* cu, uint32_t cu_idx = UINT_MAX); + bool GetFunction (DWARFCompileUnit* cu, const DWARFDebugInfoEntry* func_die, lldb_private::SymbolContext& sc); + lldb_private::Function * ParseCompileUnitFunction (const lldb_private::SymbolContext& sc, const DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die); + size_t ParseFunctionBlocks (const lldb_private::SymbolContext& sc, + lldb::user_id_t parentBlockID, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + lldb::addr_t subprogram_low_pc, + bool parse_siblings, + bool parse_children); + size_t ParseTypes (const lldb_private::SymbolContext& sc, const DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die, bool parse_siblings, bool parse_children); + lldb::TypeSP ParseType (const lldb_private::SymbolContext& sc, const DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die, bool &type_is_new); + + lldb::VariableSP ParseVariableDIE( + const lldb_private::SymbolContext& sc, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die); + + size_t ParseVariables( + const lldb_private::SymbolContext& sc, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + bool parse_siblings, + bool parse_children, + lldb_private::VariableList* cc_variable_list = NULL); + + size_t ParseChildMembers( + const lldb_private::SymbolContext& sc, + lldb::TypeSP& type_sp, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + std::vector<clang::CXXBaseSpecifier *>& base_classes, + std::vector<int>& member_accessibilities, + int &default_accessibility, + bool &is_a_class); + + size_t ParseChildParameters( + const lldb_private::SymbolContext& sc, + lldb::TypeSP& type_sp, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + lldb_private::TypeList* type_list, + std::vector<void *>& function_args, + std::vector<clang::ParmVarDecl*>& function_param_decls); + + size_t ParseChildEnumerators( + const lldb_private::SymbolContext& sc, + lldb::TypeSP& type_sp, + void *enumerator_qual_type, + uint32_t enumerator_byte_size, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *enum_die); + + void ParseChildArrayInfo( + const lldb_private::SymbolContext& sc, + const DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + int64_t& first_index, + std::vector<uint64_t>& element_orders, + uint32_t& byte_stride, + uint32_t& bit_stride); + + lldb_private::Type* GetUniquedTypeForDIEOffset(dw_offset_t type_die_offset, lldb::TypeSP& owning_type_sp, int32_t child_type, uint32_t idx, bool safe); + lldb::TypeSP GetTypeForDIE(DWARFCompileUnit *cu, const DWARFDebugInfoEntry* die, lldb::TypeSP& owning_type_sp, int32_t child_type, uint32_t idx); +// uint32_t FindTypes(std::vector<dw_offset_t> die_offsets, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types); + + void Index(); + + lldb_private::Flags m_flags; + lldb_private::DataExtractor m_dwarf_data; + lldb_private::DataExtractor m_data_debug_abbrev; + lldb_private::DataExtractor m_data_debug_aranges; + lldb_private::DataExtractor m_data_debug_frame; + lldb_private::DataExtractor m_data_debug_info; + lldb_private::DataExtractor m_data_debug_line; + lldb_private::DataExtractor m_data_debug_loc; + lldb_private::DataExtractor m_data_debug_macinfo; + lldb_private::DataExtractor m_data_debug_pubnames; + lldb_private::DataExtractor m_data_debug_pubtypes; + lldb_private::DataExtractor m_data_debug_ranges; + lldb_private::DataExtractor m_data_debug_str; + + // The auto_ptr items below are generated on demand if and when someone accesses + // them through a non const version of this class. + std::auto_ptr<DWARFDebugAbbrev> m_abbr; + std::auto_ptr<DWARFDebugAranges> m_aranges; + std::auto_ptr<DWARFDebugInfo> m_info; + std::auto_ptr<DWARFDebugLine> m_line; + lldb_private::UniqueCStringMap<dw_offset_t> m_name_to_function_die; // All concrete functions + lldb_private::UniqueCStringMap<dw_offset_t> m_name_to_inlined_die; // All inlined functions + lldb_private::UniqueCStringMap<dw_offset_t> m_name_to_global_die; // Global and static variables + lldb_private::UniqueCStringMap<dw_offset_t> m_name_to_type_die; // All type DIE offsets + bool m_indexed; + +// std::auto_ptr<DWARFDebugPubnames> m_pubnames; +// std::auto_ptr<DWARFDebugPubnames> m_pubbasetypes; // Just like m_pubtypes, but for DW_TAG_base_type DIEs +// std::auto_ptr<DWARFDebugPubnames> m_pubtypes; + std::auto_ptr<DWARFDebugRanges> m_ranges; + + typedef llvm::DenseMap<const DWARFDebugInfoEntry *, clang::DeclContext *> DIEToDeclContextMap; + DIEToDeclContextMap m_die_to_decl_ctx; + +// TypeFixupColl m_type_fixups; +// std::vector<Type*> m_indirect_fixups; + +//#define LLDB_SYMBOL_FILE_DWARF_SHRINK_TEST 1 +#if defined(LLDB_SYMBOL_FILE_DWARF_SHRINK_TEST) + + typedef std::map<FileSpec, DWARFDIECollection> FSToDIES; + void ShrinkDSYM(CompileUnit *dc_cu, DWARFCompileUnit *dw_cu, const FileSpec& cu_fspec, const FileSpec& base_types_cu_fspec, FSToDIES& fs_to_dies, const DWARFDebugInfoEntry *die); +#endif +}; + +#endif // liblldb_SymbolFileDWARF_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp new file mode 100644 index 00000000000..7bf968dd9ce --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -0,0 +1,873 @@ +//===-- SymbolFileDWARFDebugMap.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolFileDWARFDebugMap.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/Timer.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/VariableList.h" + +#include "SymbolFileDWARF.h" + +using namespace lldb; +using namespace lldb_private; + +void +SymbolFileDWARFDebugMap::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +SymbolFileDWARFDebugMap::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +SymbolFileDWARFDebugMap::GetPluginNameStatic() +{ + return "symbol-file.dwarf2-debugmap"; +} + +const char * +SymbolFileDWARFDebugMap::GetPluginDescriptionStatic() +{ + return "DWARF and DWARF3 debug symbol file reader (debug map)."; +} + +SymbolFile* +SymbolFileDWARFDebugMap::CreateInstance (ObjectFile* obj_file) +{ + return new SymbolFileDWARFDebugMap (obj_file); +} + + +SymbolFileDWARFDebugMap::SymbolFileDWARFDebugMap (ObjectFile* ofile) : + SymbolFile(ofile), + m_flags(), + m_compile_unit_infos(), + m_func_indexes(), + m_glob_indexes() +{ +} + + +SymbolFileDWARFDebugMap::~SymbolFileDWARFDebugMap() +{ +} + +void +SymbolFileDWARFDebugMap::InitOSO () +{ + if (m_flags.test(kHaveInitializedOSOs)) + return; + + m_flags.set(kHaveInitializedOSOs); + // In order to get the abilities of this plug-in, we look at the list of + // N_OSO entries (object files) from the symbol table and make sure that + // these files exist and also contain valid DWARF. If we get any of that + // then we return the abilities of the first N_OSO's DWARF. + + Symtab* symtab = m_obj_file->GetSymtab(); + if (symtab) + { + //StreamFile s(0, 4, eByteOrderHost, stdout); + std::vector<uint32_t> oso_indexes; + const uint32_t oso_index_count = symtab->AppendSymbolIndexesWithType(eSymbolTypeObjectFile, oso_indexes); + + symtab->AppendSymbolIndexesWithType(eSymbolTypeFunction, m_func_indexes); + symtab->AppendSymbolIndexesWithType(eSymbolTypeGlobal, m_glob_indexes); + + symtab->SortSymbolIndexesByValue(m_func_indexes, true); + symtab->SortSymbolIndexesByValue(m_glob_indexes, true); + + if (oso_index_count > 0) + { + m_compile_unit_infos.resize(oso_index_count); +// s.Printf("%s N_OSO symbols:\n", __PRETTY_FUNCTION__); +// symtab->Dump(&s, oso_indexes); + + for (uint32_t i=0; i<oso_index_count; ++i) + { + m_compile_unit_infos[i].so_symbol = symtab->SymbolAtIndex(oso_indexes[i] - 1); + if (m_compile_unit_infos[i].so_symbol->GetSiblingIndex() == 0) + m_compile_unit_infos[i].so_symbol = symtab->SymbolAtIndex(oso_indexes[i] - 2); + m_compile_unit_infos[i].oso_symbol = symtab->SymbolAtIndex(oso_indexes[i]); + } + } + } +} + +Module * +SymbolFileDWARFDebugMap::GetModuleByOSOIndex (uint32_t oso_idx) +{ + const uint32_t cu_count = GetNumCompileUnits(); + if (oso_idx < cu_count) + return GetModuleByCompUnitInfo (&m_compile_unit_infos[oso_idx]); + return NULL; +} + +Module * +SymbolFileDWARFDebugMap::GetModuleByCompUnitInfo (CompileUnitInfo *comp_unit_info) +{ + if (comp_unit_info->oso_module_sp.get() == NULL) + { + Symbol *oso_symbol = comp_unit_info->oso_symbol; + if (oso_symbol) + { + FileSpec oso_file_spec(oso_symbol->GetMangled().GetName().AsCString()); + + ModuleList::GetSharedModule (oso_file_spec, + m_obj_file->GetModule()->GetArchitecture(), + NULL, // UUID pointer + NULL, // object name + 0, // object offset + comp_unit_info->oso_module_sp, + NULL, + NULL); + //comp_unit_info->oso_module_sp.reset(new Module (oso_file_spec, m_obj_file->GetModule()->GetArchitecture())); + } + } + return comp_unit_info->oso_module_sp.get(); +} + + +bool +SymbolFileDWARFDebugMap::GetFileSpecForSO (uint32_t oso_idx, FileSpec &file_spec) +{ + if (oso_idx < m_compile_unit_infos.size()) + { + if (!m_compile_unit_infos[oso_idx].so_file) + { + + if (m_compile_unit_infos[oso_idx].so_symbol == NULL) + return false; + + std::string so_path (m_compile_unit_infos[oso_idx].so_symbol->GetMangled().GetName().AsCString()); + if (m_compile_unit_infos[oso_idx].so_symbol[1].GetType() == eSymbolTypeSourceFile) + so_path += m_compile_unit_infos[oso_idx].so_symbol[1].GetMangled().GetName().AsCString(); + m_compile_unit_infos[oso_idx].so_file.SetFile(so_path.c_str()); + } + file_spec = m_compile_unit_infos[oso_idx].so_file; + return true; + } + return false; +} + + + +ObjectFile * +SymbolFileDWARFDebugMap::GetObjectFileByOSOIndex (uint32_t oso_idx) +{ + Module *oso_module = GetModuleByOSOIndex (oso_idx); + if (oso_module) + return oso_module->GetObjectFile(); + return NULL; +} + +SymbolFileDWARF * +SymbolFileDWARFDebugMap::GetSymbolFile (const SymbolContext& sc) +{ + CompileUnitInfo *comp_unit_info = GetCompUnitInfo (sc); + if (comp_unit_info) + return GetSymbolFileByCompUnitInfo (comp_unit_info); + return NULL; +} + +ObjectFile * +SymbolFileDWARFDebugMap::GetObjectFileByCompUnitInfo (CompileUnitInfo *comp_unit_info) +{ + Module *oso_module = GetModuleByCompUnitInfo (comp_unit_info); + if (oso_module) + return oso_module->GetObjectFile(); + return NULL; +} + +SymbolFileDWARF * +SymbolFileDWARFDebugMap::GetSymbolFileByOSOIndex (uint32_t oso_idx) +{ + if (oso_idx < m_compile_unit_infos.size()) + return GetSymbolFileByCompUnitInfo (&m_compile_unit_infos[oso_idx]); + return NULL; +} + +SymbolFileDWARF * +SymbolFileDWARFDebugMap::GetSymbolFileByCompUnitInfo (CompileUnitInfo *comp_unit_info) +{ + if (comp_unit_info->oso_symbol_vendor == NULL) + { + ObjectFile *oso_objfile = GetObjectFileByCompUnitInfo (comp_unit_info); + + if (oso_objfile) + { + comp_unit_info->oso_symbol_vendor = oso_objfile->GetModule()->GetSymbolVendor(); +// SymbolFileDWARF *oso_dwarf = new SymbolFileDWARF(oso_objfile); +// comp_unit_info->oso_dwarf_sp.reset (oso_dwarf); + if (comp_unit_info->oso_symbol_vendor) + { + // Set a bit that lets this DWARF file know that it is being + // used along with a debug map and that it will have the + // remapped sections that we do below. + ((SymbolFileDWARF *)comp_unit_info->oso_symbol_vendor->GetSymbolFile())->GetFlags().Set(SymbolFileDWARF::flagsDWARFIsOSOForDebugMap); + comp_unit_info->debug_map_sections_sp.reset(new SectionList); + + Symtab *exe_symtab = m_obj_file->GetSymtab(); + Module *oso_module = oso_objfile->GetModule(); + Symtab *oso_symtab = oso_objfile->GetSymtab(); +//#define DEBUG_OSO_DMAP // Do not check in with this defined... +#if defined(DEBUG_OSO_DMAP) + StreamFile s(stdout); + s << "OSO symtab:\n"; + oso_symtab->Dump(&s, NULL); + s << "OSO sections before:\n"; + oso_objfile->GetSectionList()->Dump(&s, NULL, true); +#endif + + ///const uint32_t fun_resolve_flags = SymbolContext::Module | eSymbolContextCompUnit | eSymbolContextFunction; + //SectionList *oso_sections = oso_objfile->Sections(); + // Now we need to make sections that map from zero based object + // file addresses to where things eneded up in the main executable. + uint32_t oso_start_idx = comp_unit_info->oso_symbol->GetID() + 1; + const uint32_t oso_end_idx = comp_unit_info->so_symbol->GetSiblingIndex(); + uint32_t sect_id = 0x10000; + for (uint32_t idx = oso_start_idx; idx < oso_end_idx; ++idx) + { + Symbol *exe_symbol = exe_symtab->SymbolAtIndex(idx); + if (exe_symbol) + { + switch (exe_symbol->GetType()) + { + case eSymbolTypeFunction: + { + // For each N_FUN, or function that we run into in the debug map + // we make a new section that we add to the sections found in the + // .o file. This new section has the file address set to what the + // addresses are in the .o file, and the load address is adjusted + // to match where it ended up in the final executable! We do this + // before we parse any dwarf info so that when it goes get parsed + // all section/offset addresses that get registered will resolve + // correctly to the new addresses in the main executable. + + // First we find the original symbol in the .o file's symbol table + Symbol *oso_fun_symbol = oso_symtab->FindFirstSymbolWithNameAndType(exe_symbol->GetMangled().GetName(), eSymbolTypeCode); + if (oso_fun_symbol) + { + // If we found the symbol, then we + Section* exe_fun_section = const_cast<Section *>(exe_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection()); + Section* oso_fun_section = const_cast<Section *>(oso_fun_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection()); + if (oso_fun_section) + { + // Now we create a section that we will add as a child of the + // section in which the .o symbol (the N_FUN) exists. + + // We use the exe_symbol size because the one in the .o file + // will just be a symbol with no size, and the exe_symbol + // size will reflect any size changes (ppc has been known to + // shrink function sizes when it gets rid of jump islands that + // aren't needed anymore). + SectionSP oso_fun_section_sp (new Section (const_cast<Section *>(oso_fun_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection()), + oso_module, // Module (the .o file) + sect_id++, // Section ID starts at 0x10000 and increments so the section IDs don't overlap with the standard mach IDs + exe_symbol->GetMangled().GetName(), // Name the section the same as the symbol for which is was generated! + eSectionTypeDebug, + oso_fun_symbol->GetAddressRangePtr()->GetBaseAddress().GetOffset(), // File VM address offset in the current section + exe_symbol->GetByteSize(), // File size (we need the size from the executable) + 0, 0, 0)); + + oso_fun_section_sp->SetLinkedLocation (exe_fun_section, + exe_symbol->GetValue().GetFileAddress() - exe_fun_section->GetFileAddress()); + oso_fun_section->GetChildren().AddSection(oso_fun_section_sp); + comp_unit_info->debug_map_sections_sp->AddSection(oso_fun_section_sp); + } + } + } + break; + + case eSymbolTypeGlobal: + case eSymbolTypeStatic: + { + // For each N_GSYM we remap the address for the global by making + // a new section that we add to the sections found in the .o file. + // This new section has the file address set to what the + // addresses are in the .o file, and the load address is adjusted + // to match where it ended up in the final executable! We do this + // before we parse any dwarf info so that when it goes get parsed + // all section/offset addresses that get registered will resolve + // correctly to the new addresses in the main executable. We + // initially set the section size to be 1 byte, but will need to + // fix up these addresses further after all globals have been + // parsed to span the gaps, or we can find the global variable + // sizes from the DWARF info as we are parsing. + +#if 0 + // First we find the non-stab entry that corresponds to the N_GSYM in the executable + Symbol *exe_gsym_symbol = exe_symtab->FindFirstSymbolWithNameAndType(exe_symbol->GetMangled().GetName(), eSymbolTypeData); +#else + // The mach-o object file parser already matches up the N_GSYM with with the non-stab + // entry, so we shouldn't have to do that. If this ever changes, enable the code above + // in the "#if 0" block. STSYM's always match the symbol as found below. + Symbol *exe_gsym_symbol = exe_symbol; +#endif + // Next we find the non-stab entry that corresponds to the N_GSYM in the .o file + Symbol *oso_gsym_symbol = oso_symtab->FindFirstSymbolWithNameAndType(exe_symbol->GetMangled().GetName(), eSymbolTypeData); + if (exe_gsym_symbol && oso_gsym_symbol) + { + // If we found the symbol, then we + Section* exe_gsym_section = const_cast<Section *>(exe_gsym_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection()); + Section* oso_gsym_section = const_cast<Section *>(oso_gsym_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection()); + if (oso_gsym_section) + { + SectionSP oso_gsym_section_sp (new Section (const_cast<Section *>(oso_gsym_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection()), + oso_module, // Module (the .o file) + sect_id++, // Section ID starts at 0x10000 and increments so the section IDs don't overlap with the standard mach IDs + exe_symbol->GetMangled().GetName(), // Name the section the same as the symbol for which is was generated! + eSectionTypeDebug, + oso_gsym_symbol->GetAddressRangePtr()->GetBaseAddress().GetOffset(), // File VM address offset in the current section + 1, // We don't know the size of the global, just do the main address for now. + 0, 0, 0)); + + oso_gsym_section_sp->SetLinkedLocation (exe_gsym_section, + exe_gsym_symbol->GetValue().GetFileAddress() - exe_gsym_section->GetFileAddress()); + oso_gsym_section->GetChildren().AddSection(oso_gsym_section_sp); + comp_unit_info->debug_map_sections_sp->AddSection(oso_gsym_section_sp); + } + } + } + break; + +// case eSymbolTypeStatic: +// { +// // For each N_STSYM we remap the address for the global by making +// // a new section that we add to the sections found in the .o file. +// // This new section has the file address set to what the +// // addresses are in the .o file, and the load address is adjusted +// // to match where it ended up in the final executable! We do this +// // before we parse any dwarf info so that when it goes get parsed +// // all section/offset addresses that get registered will resolve +// // correctly to the new addresses in the main executable. We +// // initially set the section size to be 1 byte, but will need to +// // fix up these addresses further after all globals have been +// // parsed to span the gaps, or we can find the global variable +// // sizes from the DWARF info as we are parsing. +// +// +// Symbol *exe_stsym_symbol = exe_symbol; +// // First we find the non-stab entry that corresponds to the N_STSYM in the .o file +// Symbol *oso_stsym_symbol = oso_symtab->FindFirstSymbolWithNameAndType(exe_symbol->GetMangled().GetName(), eSymbolTypeData); +// if (exe_stsym_symbol && oso_stsym_symbol) +// { +// // If we found the symbol, then we +// Section* exe_stsym_section = const_cast<Section *>(exe_stsym_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection()); +// Section* oso_stsym_section = const_cast<Section *>(oso_stsym_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection()); +// if (oso_stsym_section) +// { +// // The load address of the symbol will use the section in the +// // executable that contains the debug map that corresponds to +// // the N_FUN symbol. We set the offset to reflect the offset +// // into that section since we are creating a new section. +// AddressRange stsym_load_range(exe_stsym_section, exe_stsym_symbol->GetValue().GetFileAddress() - exe_stsym_section->GetFileAddress(), 1); +// // We need the symbol's section offset address from the .o file, but +// // we need a non-zero size. +// AddressRange stsym_file_range(exe_stsym_symbol->GetAddressRangePtr()->GetBaseAddress().GetSection(), exe_stsym_symbol->GetAddressRangePtr()->GetBaseAddress().GetOffset(), 1); +// +// // Now we create a section that we will add as a child of the +// // section in which the .o symbol (the N_FUN) exists. +// +//// TODO: mimic what I did for N_FUN if that works... +//// // We use the 1 byte for the size because we don't know the +//// // size of the global symbol without seeing the DWARF. +//// SectionSP oso_fun_section_sp (new Section ( NULL, oso_module, // Module (the .o file) +//// sect_id++, // Section ID starts at 0x10000 and increments so the section IDs don't overlap with the standard mach IDs +//// exe_symbol->GetMangled().GetName(),// Name the section the same as the symbol for which is was generated! +//// // &stsym_load_range, // Load offset is the offset into the executable section for the N_FUN from the debug map +//// &stsym_file_range, // File section/offset is just the same os the symbol on the .o file +//// 0, 0, 0)); +//// +//// // Now we add the new section to the .o file's sections as a child +//// // of the section in which the N_SECT symbol exists. +//// oso_stsym_section->GetChildren().AddSection(oso_fun_section_sp); +//// comp_unit_info->debug_map_sections_sp->AddSection(oso_fun_section_sp); +// } +// } +// } +// break; + } + } + } +#if defined(DEBUG_OSO_DMAP) + s << "OSO sections after:\n"; + oso_objfile->GetSectionList()->Dump(&s, NULL, true); +#endif + } + } + } + if (comp_unit_info->oso_symbol_vendor) + return (SymbolFileDWARF *)comp_unit_info->oso_symbol_vendor->GetSymbolFile(); + return NULL; +} + +uint32_t +SymbolFileDWARFDebugMap::GetAbilities () +{ + // In order to get the abilities of this plug-in, we look at the list of + // N_OSO entries (object files) from the symbol table and make sure that + // these files exist and also contain valid DWARF. If we get any of that + // then we return the abilities of the first N_OSO's DWARF. + + const uint32_t oso_index_count = GetNumCompileUnits(); + if (oso_index_count > 0) + { + const uint32_t dwarf_abilities = SymbolFile::CompileUnits | + SymbolFile::Functions | + SymbolFile::Blocks | + SymbolFile::GlobalVariables | + SymbolFile::LocalVariables | + SymbolFile::VariableTypes | + SymbolFile::LineTables; + + for (uint32_t oso_idx=0; oso_idx<oso_index_count; ++oso_idx) + { + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex (oso_idx); + if (oso_dwarf) + { + uint32_t oso_abilities = oso_dwarf->GetAbilities(); + if ((oso_abilities & dwarf_abilities) == dwarf_abilities) + return oso_abilities; + } + } + } + return 0; +} + +uint32_t +SymbolFileDWARFDebugMap::GetNumCompileUnits() +{ + InitOSO (); + return m_compile_unit_infos.size(); +} + + +CompUnitSP +SymbolFileDWARFDebugMap::ParseCompileUnitAtIndex(uint32_t cu_idx) +{ + CompUnitSP comp_unit_sp; + const uint32_t cu_count = GetNumCompileUnits(); + + if (cu_idx < cu_count) + { + if (m_compile_unit_infos[cu_idx].oso_compile_unit_sp.get() == NULL) + { + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex (cu_idx); + if (oso_dwarf) + { + // There is only one compile unit for N_OSO entry right now, so + // it will always exist at index zero. + m_compile_unit_infos[cu_idx].oso_compile_unit_sp = m_compile_unit_infos[cu_idx].oso_symbol_vendor->GetCompileUnitAtIndex (0); + } + + if (m_compile_unit_infos[cu_idx].oso_compile_unit_sp.get() == NULL) + { + // We weren't able to get the DWARF for this N_OSO entry (the + // .o file may be missing or not at the specified path), make + // one up as best we can from the debug map. We set the uid + // of the compile unit to the symbol index with the MSBit set + // so that it doesn't collide with any uid values from the DWARF + Symbol *so_symbol = m_compile_unit_infos[cu_idx].so_symbol; + if (so_symbol) + { + m_compile_unit_infos[cu_idx].oso_compile_unit_sp.reset(new CompileUnit (m_obj_file->GetModule(), + NULL, + so_symbol->GetMangled().GetName().AsCString(), + cu_idx, + Language::Unknown)); + } + } + } + comp_unit_sp = m_compile_unit_infos[cu_idx].oso_compile_unit_sp; + } + + return comp_unit_sp; +} + +SymbolFileDWARFDebugMap::CompileUnitInfo * +SymbolFileDWARFDebugMap::GetCompUnitInfo (const SymbolContext& sc) +{ + const uint32_t cu_count = GetNumCompileUnits(); + for (uint32_t i=0; i<cu_count; ++i) + { + if (sc.comp_unit == m_compile_unit_infos[i].oso_compile_unit_sp.get()) + return &m_compile_unit_infos[i]; + } + return NULL; +} + +size_t +SymbolFileDWARFDebugMap::ParseCompileUnitFunctions (const SymbolContext& sc) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseCompileUnitFunctions (sc); + return 0; +} + +bool +SymbolFileDWARFDebugMap::ParseCompileUnitLineTable (const SymbolContext& sc) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseCompileUnitLineTable (sc); + return false; +} + +bool +SymbolFileDWARFDebugMap::ParseCompileUnitSupportFiles (const SymbolContext& sc, FileSpecList &support_files) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseCompileUnitSupportFiles (sc, support_files); + return false; +} + + +size_t +SymbolFileDWARFDebugMap::ParseFunctionBlocks (const SymbolContext& sc) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseFunctionBlocks (sc); + return 0; +} + + +size_t +SymbolFileDWARFDebugMap::ParseTypes (const SymbolContext& sc) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseTypes (sc); + return 0; +} + + +size_t +SymbolFileDWARFDebugMap::ParseVariablesForContext (const SymbolContext& sc) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseTypes (sc); + return 0; +} + + + +Type* +SymbolFileDWARFDebugMap::ResolveTypeUID(lldb::user_id_t type_uid) +{ + return NULL; +} + + +uint32_t +SymbolFileDWARFDebugMap::ResolveSymbolContext (const Address& exe_so_addr, uint32_t resolve_scope, SymbolContext& sc) +{ + uint32_t resolved_flags = 0; + Symtab* symtab = m_obj_file->GetSymtab(); + if (symtab) + { + const addr_t exe_file_addr = exe_so_addr.GetFileAddress(); + sc.symbol = symtab->FindSymbolContainingFileAddress (exe_file_addr, &m_func_indexes[0], m_func_indexes.size()); + + if (sc.symbol != NULL) + { + resolved_flags |= eSymbolContextSymbol; + + uint32_t oso_idx = 0; + CompileUnitInfo* comp_unit_info = GetCompileUnitInfoForSymbolWithIndex (sc.symbol->GetID(), &oso_idx); + if (comp_unit_info) + { + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex (oso_idx); + ObjectFile *oso_objfile = GetObjectFileByOSOIndex (oso_idx); + if (oso_dwarf && oso_objfile) + { + SectionList *oso_section_list = oso_objfile->GetSectionList(); + + + SectionSP oso_section_sp(oso_section_list->FindSectionByName(exe_so_addr.GetSection()->GetName())); + if (oso_section_sp) + { + SectionSP oso_symbol_section_sp (oso_section_sp->GetChildren().FindSectionContainingLinkedFileAddress (exe_file_addr)); + + if (oso_symbol_section_sp) + { + const addr_t linked_file_addr = oso_symbol_section_sp->GetLinkedFileAddress(); + Address oso_so_addr (oso_symbol_section_sp.get(), exe_file_addr - linked_file_addr); + if (oso_so_addr.IsSectionOffset()) + resolved_flags |= oso_dwarf->ResolveSymbolContext (oso_so_addr, resolve_scope, sc); + } + } + // Map the load address from in the executable back to a + // section/offset address in the .o file so we can do + // lookups in the .o DWARF. +// Address oso_so_addr (exe_load_addr, false, comp_unit_info->debug_map_sections_sp.get()); +// +// // Make sure we were able to resolve this back to a .o +// // section offset address, and if so, resolve the context +// // for everything that was asked for. +// if (oso_so_addr.IsSectionOffset()) +// resolved_flags |= oso_dwarf->ResolveSymbolContext (oso_so_addr, resolve_scope, sc); + } + } + } + } + return resolved_flags; +} + + +uint32_t +SymbolFileDWARFDebugMap::ResolveSymbolContext (const FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) +{ + uint32_t initial = sc_list.GetSize(); + const uint32_t cu_count = GetNumCompileUnits(); + + FileSpec so_file_spec; + for (uint32_t i=0; i<cu_count; ++i) + { + if (GetFileSpecForSO (i, so_file_spec)) + { + // By passing false to the comparison we will be able to match + // and files given a filename only. If both file_spec and + // so_file_spec have directories, we will still do a full match. + if (FileSpec::Compare (file_spec, so_file_spec, false) == 0) + { + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex (i); + + oso_dwarf->ResolveSymbolContext(file_spec, line, check_inlines, resolve_scope, sc_list); + } + } + } + return sc_list.GetSize() - initial; +} + +uint32_t +SymbolFileDWARFDebugMap::PrivateFindGlobalVariables +( + const ConstString &name, + const std::vector<uint32_t> &indexes, // Indexes into the symbol table that match "name" + uint32_t max_matches, + VariableList& variables +) +{ + const uint32_t original_size = variables.GetSize(); + const size_t match_count = indexes.size(); + for (size_t i=0; i<match_count; ++i) + { + uint32_t oso_idx; + CompileUnitInfo* comp_unit_info = GetCompileUnitInfoForSymbolWithIndex (indexes[i], &oso_idx); + if (comp_unit_info) + { + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex (oso_idx); + if (oso_dwarf) + { + if (oso_dwarf->FindGlobalVariables(name, true, max_matches, variables)) + if (variables.GetSize() > max_matches) + break; + } + } + } + return variables.GetSize() - original_size; +} + +uint32_t +SymbolFileDWARFDebugMap::FindGlobalVariables (const ConstString &name, bool append, uint32_t max_matches, VariableList& variables) +{ + + // If we aren't appending the results to this list, then clear the list + if (!append) + variables.Clear(); + + // Remember how many variables are in the list before we search in case + // we are appending the results to a variable list. + const uint32_t original_size = variables.GetSize(); + + Symtab* symtab = m_obj_file->GetSymtab(); + if (symtab) + { + std::vector<uint32_t> indexes; + const size_t match_count = m_obj_file->GetSymtab()->FindAllSymbolsWithNameAndType (name, eSymbolTypeGlobal, indexes); + if (match_count) + { + PrivateFindGlobalVariables (name, indexes, max_matches, variables); + } + } + // Return the number of variable that were appended to the list + return variables.GetSize() - original_size; +} + + +uint32_t +SymbolFileDWARFDebugMap::FindGlobalVariables (const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variables) +{ + return 0; +} + + +int +SymbolFileDWARFDebugMap::SymbolContainsSymbolIndex (uint32_t *symbol_idx_ptr, const CompileUnitInfo *comp_unit_info) +{ + const uint32_t symbol_idx = *symbol_idx_ptr; + + if (symbol_idx < comp_unit_info->so_symbol->GetID()) + return -1; + + if (symbol_idx < comp_unit_info->so_symbol->GetSiblingIndex()) + return 0; + + return 1; +} + + +SymbolFileDWARFDebugMap::CompileUnitInfo* +SymbolFileDWARFDebugMap::GetCompileUnitInfoForSymbolWithIndex (uint32_t symbol_idx, uint32_t *oso_idx_ptr) +{ + const uint32_t oso_index_count = m_compile_unit_infos.size(); + CompileUnitInfo *comp_unit_info = NULL; + if (oso_index_count) + { + comp_unit_info = (CompileUnitInfo*)bsearch(&symbol_idx, &m_compile_unit_infos[0], m_compile_unit_infos.size(), sizeof(CompileUnitInfo), (comparison_function)SymbolContainsSymbolIndex); + } + + if (oso_idx_ptr) + { + if (comp_unit_info != NULL) + *oso_idx_ptr = comp_unit_info - &m_compile_unit_infos[0]; + else + *oso_idx_ptr = UINT32_MAX; + } + return comp_unit_info; +} + +uint32_t +SymbolFileDWARFDebugMap::FindFunctions(const ConstString &name, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileDWARFDebugMap::FindFunctions (name = %s)", + name.GetCString()); + + + std::vector<uint32_t> indexes; + uint32_t initial_size = 0; + if (append) + initial_size = sc_list.GetSize(); + else + sc_list.Clear(); + + const size_t match_count = m_obj_file->GetSymtab()->FindAllSymbolsWithNameAndType (name, eSymbolTypeFunction, indexes); + if (match_count > 0) + { + for (size_t i=0; i<match_count; ++i) + { + uint32_t oso_idx; + CompileUnitInfo* comp_unit_info = GetCompileUnitInfoForSymbolWithIndex (indexes[i], &oso_idx); + if (comp_unit_info) + { + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex (oso_idx); + if (oso_dwarf) + oso_dwarf->FindFunctions(name, true, sc_list); + } + } +// Stream s(stdout); +// sc_list.Dump(&s); + } + + return sc_list.GetSize() - initial_size; +} + + +uint32_t +SymbolFileDWARFDebugMap::FindFunctions (const RegularExpression& regex, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileDWARFDebugMap::FindFunctions (regex = '%s')", + regex.GetText()); + + + return 0; +} + +// +//uint32_t +//SymbolFileDWARFDebugMap::FindTypes (const SymbolContext& sc, const ConstString &name, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) +//{ +// SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); +// if (oso_dwarf) +// return oso_dwarf->FindTypes (sc, name, append, max_matches, encoding, udt_uid, types); +// return 0; +//} +// +// +//uint32_t +//SymbolFileDWARFDebugMap::FindTypes (const SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) +//{ +// SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); +// if (oso_dwarf) +// return oso_dwarf->FindTypes (sc, regex, append, max_matches, encoding, udt_uid, types); +// return 0; +//} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +SymbolFileDWARFDebugMap::GetPluginName() +{ + return "SymbolFileDWARFDebugMap"; +} + +const char * +SymbolFileDWARFDebugMap::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +SymbolFileDWARFDebugMap::GetPluginVersion() +{ + return 1; +} + +void +SymbolFileDWARFDebugMap::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +SymbolFileDWARFDebugMap::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +SymbolFileDWARFDebugMap::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h new file mode 100644 index 00000000000..0a312ab9f26 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h @@ -0,0 +1,186 @@ +//===-- SymbolFileDWARFDebugMap.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_SymbolFileDWARFDebugMap_h_ +#define liblldb_SymbolFileDWARFDebugMap_h_ + + +#include <vector> +#include <bitset> +#include "lldb/Symbol/SymbolFile.h" + +class SymbolFileDWARF; + +class SymbolFileDWARFDebugMap : public lldb_private::SymbolFile +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile * + CreateInstance (lldb_private::ObjectFile* obj_file); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolFileDWARFDebugMap (lldb_private::ObjectFile* ofile); + virtual ~ SymbolFileDWARFDebugMap (); + + virtual uint32_t GetAbilities (); + + //------------------------------------------------------------------ + // Compile Unit function calls + //------------------------------------------------------------------ + virtual uint32_t GetNumCompileUnits (); + virtual lldb::CompUnitSP ParseCompileUnitAtIndex (uint32_t index); + + virtual size_t ParseCompileUnitFunctions (const lldb_private::SymbolContext& sc); + virtual bool ParseCompileUnitLineTable (const lldb_private::SymbolContext& sc); + virtual bool ParseCompileUnitSupportFiles (const lldb_private::SymbolContext& sc, lldb_private::FileSpecList &support_files); + virtual size_t ParseFunctionBlocks (const lldb_private::SymbolContext& sc); + virtual size_t ParseTypes (const lldb_private::SymbolContext& sc); + virtual size_t ParseVariablesForContext (const lldb_private::SymbolContext& sc); + + virtual lldb_private::Type* ResolveTypeUID (lldb::user_id_t type_uid); + virtual uint32_t ResolveSymbolContext (const lldb_private::Address& so_addr, uint32_t resolve_scope, lldb_private::SymbolContext& sc); + virtual uint32_t ResolveSymbolContext (const lldb_private::FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, lldb_private::SymbolContextList& sc_list); + virtual uint32_t FindGlobalVariables (const lldb_private::ConstString &name, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + virtual uint32_t FindGlobalVariables (const lldb_private::RegularExpression& regex, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + virtual uint32_t FindFunctions (const lldb_private::ConstString &name, bool append, lldb_private::SymbolContextList& sc_list); + virtual uint32_t FindFunctions (const lldb_private::RegularExpression& regex, bool append, lldb_private::SymbolContextList& sc_list); +// virtual uint32_t FindTypes (const lldb_private::SymbolContext& sc, const ConstString &name, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types); +// virtual uint32_t FindTypes (const lldb_private::SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + +protected: + enum + { + kHaveInitializedOSOs = (1 << 0), + kNumFlags + }; + + //------------------------------------------------------------------ + // Class specific types + //------------------------------------------------------------------ + struct CompileUnitInfo + { + lldb_private::FileSpec so_file; + lldb_private::Symbol *so_symbol; + lldb_private::Symbol *oso_symbol; + lldb::ModuleSP oso_module_sp; + lldb::CompUnitSP oso_compile_unit_sp; + lldb_private::SymbolVendor *oso_symbol_vendor; +// lldb_private::shared_ptr<SymbolFileDWARF> oso_dwarf_sp; +// lldb_private::shared_ptr<SymbolVendor> oso_dwarf_sp; + std::vector<uint32_t> function_indexes; + std::vector<uint32_t> static_indexes; + lldb::SharedPtr<lldb_private::SectionList>::Type debug_map_sections_sp; + + CompileUnitInfo() : + so_file(), + so_symbol(NULL), + oso_symbol(NULL), + oso_module_sp(), + oso_compile_unit_sp(), + oso_symbol_vendor(NULL), +// oso_dwarf_sp(), + function_indexes(), + static_indexes(), + debug_map_sections_sp() + { + } + }; + + //------------------------------------------------------------------ + // Protected Member Functions + //------------------------------------------------------------------ + void + InitOSO (); + + bool + GetFileSpecForSO (uint32_t oso_idx, lldb_private::FileSpec &file_spec); + + CompileUnitInfo * + GetCompUnitInfo (const lldb_private::SymbolContext& sc); + + lldb_private::Module * + GetModuleByCompUnitInfo (CompileUnitInfo *comp_unit_info); + + lldb_private::Module * + GetModuleByOSOIndex (uint32_t oso_idx); + + lldb_private::ObjectFile * + GetObjectFileByCompUnitInfo (CompileUnitInfo *comp_unit_info); + + lldb_private::ObjectFile * + GetObjectFileByOSOIndex (uint32_t oso_idx); + + SymbolFileDWARF * + GetSymbolFile (const lldb_private::SymbolContext& sc); + + SymbolFileDWARF * + GetSymbolFileByCompUnitInfo (CompileUnitInfo *comp_unit_info); + + SymbolFileDWARF * + GetSymbolFileByOSOIndex (uint32_t oso_idx); + + CompileUnitInfo* + GetCompileUnitInfoForSymbolWithIndex (uint32_t symbol_idx, uint32_t *oso_idx_ptr); + + static int + SymbolContainsSymbolIndex (uint32_t *symbol_idx_ptr, const CompileUnitInfo *comp_unit_info); + + uint32_t + PrivateFindGlobalVariables (const lldb_private::ConstString &name, + const std::vector<uint32_t> &name_symbol_indexes, + uint32_t max_matches, + lldb_private::VariableList& variables); + + //------------------------------------------------------------------ + // Member Variables + //------------------------------------------------------------------ + std::bitset<kNumFlags> m_flags; + std::vector<CompileUnitInfo> m_compile_unit_infos; + std::vector<uint32_t> m_func_indexes; // Sorted by address + std::vector<uint32_t> m_glob_indexes; +}; + +#endif // #ifndef liblldb_SymbolFileDWARFDebugMap_h_ diff --git a/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp b/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp new file mode 100644 index 00000000000..d7da35675c7 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp @@ -0,0 +1,401 @@ +//===-- SymbolFileSymtab.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolFileSymtab.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Timer.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symtab.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Function.h" + +using namespace lldb; +using namespace lldb_private; + +void +SymbolFileSymtab::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +SymbolFileSymtab::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +SymbolFileSymtab::GetPluginNameStatic() +{ + return "symbol-file.symtab"; +} + +const char * +SymbolFileSymtab::GetPluginDescriptionStatic() +{ + return "Reads debug symbols from an object file's symbol table."; +} + + +SymbolFile* +SymbolFileSymtab::CreateInstance (ObjectFile* obj_file) +{ + return new SymbolFileSymtab(obj_file); +} + +SymbolFileSymtab::SymbolFileSymtab(ObjectFile* obj_file) : + SymbolFile(obj_file), + m_source_indexes(), + m_func_indexes(), + m_code_indexes(), + m_data_indexes(), + m_addr_indexes() +{ +} + +SymbolFileSymtab::~SymbolFileSymtab() +{ +} + + +uint32_t +SymbolFileSymtab::GetAbilities () +{ + uint32_t abilities = 0; + const Symtab *symtab = m_obj_file->GetSymtab(); + if (symtab) + { + + //---------------------------------------------------------------------- + // The snippet of code below will get the indexes the module symbol + // table entries that are code, data, or function related (debug info), + // sort them by value (address) and dump the sorted symbols. + //---------------------------------------------------------------------- + symtab->AppendSymbolIndexesWithType(eSymbolTypeSourceFile, m_source_indexes); + if (!m_source_indexes.empty()) + { + abilities |= CompileUnits; + } + symtab->AppendSymbolIndexesWithType(eSymbolTypeFunction, m_func_indexes); + if (!m_func_indexes.empty()) + { + symtab->SortSymbolIndexesByValue(m_func_indexes, true); + abilities |= Functions; + } + + symtab->AppendSymbolIndexesWithType(eSymbolTypeCode, m_code_indexes); + if (!m_code_indexes.empty()) + { + symtab->SortSymbolIndexesByValue(m_code_indexes, true); + abilities |= Labels; + } + + symtab->AppendSymbolIndexesWithType(eSymbolTypeData, m_data_indexes); + + if (!m_data_indexes.empty()) + { + symtab->SortSymbolIndexesByValue(m_data_indexes, true); + abilities |= GlobalVariables; + } + } + + return abilities; +} + +uint32_t +SymbolFileSymtab::GetNumCompileUnits() +{ + // If we don't have any source file symbols we will just have one compile unit for + // the entire object file + if (m_source_indexes.empty()) + return 1; + + // If we have any source file symbols we will logically orgnize the object symbols + // using these. + return m_source_indexes.size(); +} + +CompUnitSP +SymbolFileSymtab::ParseCompileUnitAtIndex(uint32_t idx) +{ + CompUnitSP cu_sp; + + // If we don't have any source file symbols we will just have one compile unit for + // the entire object file + if (m_source_indexes.empty()) + { + const FileSpec &obj_file_spec = m_obj_file->GetFileSpec(); + if (obj_file_spec) + cu_sp.reset(new CompileUnit(m_obj_file->GetModule(), NULL, obj_file_spec, 0, Language::Unknown)); + + } + else if (idx < m_source_indexes.size()) + { + const Symbol *cu_symbol = m_obj_file->GetSymtab()->SymbolAtIndex(m_source_indexes[idx]); + if (cu_symbol) + cu_sp.reset(new CompileUnit(m_obj_file->GetModule(), NULL, cu_symbol->GetMangled().GetName().AsCString(), 0, Language::Unknown)); + } + return cu_sp; +} + +size_t +SymbolFileSymtab::ParseCompileUnitFunctions (const SymbolContext &sc) +{ + size_t num_added = 0; + // We must at least have a valid compile unit + assert (sc.comp_unit != NULL); + const Symtab *symtab = m_obj_file->GetSymtab(); + const Symbol *curr_symbol = NULL; + const Symbol *next_symbol = NULL; +// const char *prefix = m_obj_file->SymbolPrefix(); +// if (prefix == NULL) +// prefix == ""; +// +// const uint32_t prefix_len = strlen(prefix); + + // If we don't have any source file symbols we will just have one compile unit for + // the entire object file + if (m_source_indexes.empty()) + { + // The only time we will have a user ID of zero is when we don't have + // and source file symbols and we declare one compile unit for the + // entire object file + if (!m_func_indexes.empty()) + { + + } + + if (!m_code_indexes.empty()) + { +// StreamFile s(stdout); +// symtab->Dump(&s, m_code_indexes); + + uint32_t idx = 0; // Index into the indexes + const uint32_t num_indexes = m_code_indexes.size(); + for (idx = 0; idx < num_indexes; ++idx) + { + uint32_t symbol_idx = m_code_indexes[idx]; + curr_symbol = symtab->SymbolAtIndex(symbol_idx); + if (curr_symbol) + { + // Union of all ranges in the function DIE (if the function is discontiguous) + AddressRange func_range(curr_symbol->GetValue(), 0); + if (func_range.GetBaseAddress().IsSectionOffset()) + { + uint32_t symbol_size = curr_symbol->GetByteSize(); + if (symbol_size != 0 && !curr_symbol->GetSizeIsSibling()) + func_range.SetByteSize(symbol_size); + else if (idx + 1 < num_indexes) + { + next_symbol = symtab->SymbolAtIndex(m_code_indexes[idx + 1]); + if (next_symbol) + { + func_range.SetByteSize(next_symbol->GetValue().GetOffset() - curr_symbol->GetValue().GetOffset()); + } + } + + FunctionSP func_sp(new Function(sc.comp_unit, + symbol_idx, // UserID is the DIE offset + LLDB_INVALID_UID, // We don't have any type info for this function + curr_symbol->GetMangled(), // Linker/mangled name + NULL, // no return type for a code symbol... + func_range)); // first address range + + if (func_sp.get() != NULL) + { + sc.comp_unit->AddFunction(func_sp); + ++num_added; + } + } + } + } + + } + } + else + { + // We assume we + } + return num_added; +} + +bool +SymbolFileSymtab::ParseCompileUnitLineTable (const SymbolContext &sc) +{ + return false; +} + +bool +SymbolFileSymtab::ParseCompileUnitSupportFiles (const SymbolContext& sc, FileSpecList &support_files) +{ + return false; +} + +size_t +SymbolFileSymtab::ParseFunctionBlocks (const SymbolContext &sc) +{ + return 0; +} + + +size_t +SymbolFileSymtab::ParseTypes (const SymbolContext &sc) +{ + return 0; +} + + +size_t +SymbolFileSymtab::ParseVariablesForContext (const SymbolContext& sc) +{ + return 0; +} + +Type* +SymbolFileSymtab::ResolveTypeUID(lldb::user_id_t type_uid) +{ + return NULL; +} + + + +uint32_t +SymbolFileSymtab::ResolveSymbolContext (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc) +{ + if (m_obj_file->GetSymtab() == NULL) + return 0; + + uint32_t resolved_flags = 0; + if (resolve_scope & eSymbolContextSymbol) + { + sc.symbol = m_obj_file->GetSymtab()->FindSymbolContainingFileAddress(so_addr.GetFileAddress()); + if (sc.symbol) + resolved_flags |= eSymbolContextSymbol; + } + return resolved_flags; +} + +uint32_t +SymbolFileSymtab::ResolveSymbolContext (const FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) +{ + return 0; +} + +uint32_t +SymbolFileSymtab::FindGlobalVariables(const ConstString &name, bool append, uint32_t max_matches, VariableList& variables) +{ + return 0; +} + +uint32_t +SymbolFileSymtab::FindGlobalVariables(const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variables) +{ + return 0; +} + +uint32_t +SymbolFileSymtab::FindFunctions(const ConstString &name, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileSymtab::FindFunctions (name = '%s')", + name.GetCString()); + + Symtab *symtab = m_obj_file->GetSymtab(); + if (symtab) + { + const uint32_t start_size = sc_list.GetSize(); + std::vector<uint32_t> symbol_indexes; + symtab->FindAllSymbolsWithNameAndType (name, eSymbolTypeFunction, symbol_indexes); + symtab->FindAllSymbolsWithNameAndType (name, eSymbolTypeCode, symbol_indexes); + const uint32_t num_matches = symbol_indexes.size(); + if (num_matches) + { + SymbolContext sc(m_obj_file->GetModule()); + for (uint32_t i=0; i<num_matches; i++) + { + sc.symbol = symtab->SymbolAtIndex(symbol_indexes[i]); + sc_list.Append(sc); + } + } + return sc_list.GetSize() - start_size; + } + return 0; +} + +uint32_t +SymbolFileSymtab::FindFunctions(const RegularExpression& regex, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileSymtab::FindFunctions (regex = '%s')", + regex.GetText()); + + return 0; +} + +//uint32_t +//SymbolFileSymtab::FindTypes(const SymbolContext& sc, const ConstString &name, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) +//{ +// return 0; +//} +// +//uint32_t +//SymbolFileSymtab::FindTypes(const SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) +//{ +// return 0; +//} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +SymbolFileSymtab::GetPluginName() +{ + return "SymbolFileSymtab"; +} + +const char * +SymbolFileSymtab::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +SymbolFileSymtab::GetPluginVersion() +{ + return 1; +} + +void +SymbolFileSymtab::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +SymbolFileSymtab::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +SymbolFileSymtab::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + diff --git a/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h b/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h new file mode 100644 index 00000000000..ac73f294585 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h @@ -0,0 +1,136 @@ +//===-- SymbolFileSymtab.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_SymbolFileSymtab_h_ +#define liblldb_SymbolFileSymtab_h_ + +#include "lldb/Symbol/SymbolFile.h" +#include <vector> + +class SymbolFileSymtab : public lldb_private::SymbolFile +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile* + CreateInstance (lldb_private::ObjectFile* obj_file); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolFileSymtab(lldb_private::ObjectFile* obj_file); + + virtual + ~SymbolFileSymtab(); + + virtual uint32_t GetAbilities (); + + //------------------------------------------------------------------ + // Compile Unit function calls + //------------------------------------------------------------------ + virtual uint32_t + GetNumCompileUnits(); + + virtual lldb::CompUnitSP + ParseCompileUnitAtIndex(uint32_t index); + + virtual size_t + ParseCompileUnitFunctions (const lldb_private::SymbolContext& sc); + + virtual bool + ParseCompileUnitLineTable (const lldb_private::SymbolContext& sc); + + virtual bool + ParseCompileUnitSupportFiles (const lldb_private::SymbolContext& sc, lldb_private::FileSpecList &support_files); + + virtual size_t + ParseFunctionBlocks (const lldb_private::SymbolContext& sc); + + virtual size_t + ParseTypes (const lldb_private::SymbolContext& sc); + + virtual size_t + ParseVariablesForContext (const lldb_private::SymbolContext& sc); + + virtual lldb_private::Type* + ResolveTypeUID(lldb::user_id_t type_uid); + + virtual uint32_t + ResolveSymbolContext (const lldb_private::Address& so_addr, uint32_t resolve_scope, lldb_private::SymbolContext& sc); + + virtual uint32_t + ResolveSymbolContext (const lldb_private::FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, lldb_private::SymbolContextList& sc_list); + + virtual uint32_t + FindGlobalVariables(const lldb_private::ConstString &name, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + + virtual uint32_t + FindGlobalVariables(const lldb_private::RegularExpression& regex, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + + virtual uint32_t + FindFunctions(const lldb_private::ConstString &name, bool append, lldb_private::SymbolContextList& sc_list); + + virtual uint32_t + FindFunctions(const lldb_private::RegularExpression& regex, bool append, lldb_private::SymbolContextList& sc_list); + +// virtual uint32_t +// FindTypes(const lldb_private::SymbolContext& sc, const lldb_private::ConstString &name, bool append, uint32_t max_matches, lldb_private::Type::Encoding encoding, lldb::user_id_t udt_uid, lldb_private::TypeList& types); + +// virtual uint32_t +// FindTypes(const lldb_private::SymbolContext& sc, const lldb_private::RegularExpression& regex, bool append, uint32_t max_matches, lldb_private::Type::Encoding encoding, lldb::user_id_t udt_uid, lldb_private::TypeList& types); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + + + +protected: + std::vector<uint32_t> m_source_indexes; + std::vector<uint32_t> m_func_indexes; + std::vector<uint32_t> m_code_indexes; + std::vector<uint32_t> m_data_indexes; + std::vector<uint32_t> m_addr_indexes; // Anything that needs to go into an search by address + +private: + DISALLOW_COPY_AND_ASSIGN (SymbolFileSymtab); +}; + + +#endif // liblldb_SymbolFileSymtab_h_ diff --git a/lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp b/lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp new file mode 100644 index 00000000000..270d7ed0f5d --- /dev/null +++ b/lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.cpp @@ -0,0 +1,339 @@ +//===-- SymbolVendorMacOSX.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolVendorMacOSX.h" + +#include <mach/machine.h> // DebugSymbols needs this on Leopard... + +#include <AvailabilityMacros.h> + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Symbol/ObjectFile.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// SymbolVendorMacOSX constructor +//---------------------------------------------------------------------- +SymbolVendorMacOSX::SymbolVendorMacOSX(Module *module) : + SymbolVendor(module) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SymbolVendorMacOSX::~SymbolVendorMacOSX() +{ +} + + +static bool +UUIDsMatch(Module *module, ObjectFile *ofile) +{ + if (module && ofile) + { + // Make sure the UUIDs match + UUID dsym_uuid; + if (ofile->GetUUID(&dsym_uuid)) + return dsym_uuid == module->GetUUID(); + } + return false; +} + + +//ObjectFile * +//LocateDSYMMachFileInDSYMBundle (Module* module, FileSpec& dsym_fspec) +//{ +// ObjectFile *dsym_objfile = NULL; +// +// char path[PATH_MAX]; +// +// if (dsym_fspec.GetPath(path, sizeof(path))) +// { +// size_t path_len = strlen(path); +// const char *bundle_subpath = "/Contents/Resources/DWARF/"; +// if (path_len > 0) +// { +// if (path[path_len-1] == '/') +// ::strncat (path, bundle_subpath + 1, sizeof(path)); +// else +// ::strncat (path, bundle_subpath, sizeof(path)); +// ::strncat (path, dsym_fspec.GetFilename().AsCString(), sizeof(path)); +// +// path_len = strlen(path); +// +// if (::strcasecmp (&path[path_len - strlen(".dSYM")], ".dSYM") == 0) +// { +// path[path_len - ::strlen(".dSYM")] = '\0'; +// dsym_fspec.SetFile(path); +// dsym_objfile = ObjectFile::FindPlugin(module, &dsym_fspec, 0); +// } +// } +// } +// return dsym_objfile; +//} +// +//CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url) __attribute__((weak_import)); + + +//ObjectFile * +//FindDSYMUsingDebugSymbols (Module* module, FileSpec& dsym_fspec) +//{ +// Timer scoped_locate("FindDSYMUsingDebugSymbols"); +// dsym_fspec.Clear(); +// ObjectFile *dsym_objfile = NULL; +// if (module->GetUUID().IsValid()) +// { +// // Try and locate the dSYM file using DebugSymbols first +// const UInt8 *module_uuid = (const UInt8 *)module->GetUUID().GetBytes(); +// if (module_uuid != NULL) +// { +// CFUUIDRef module_uuid_ref; +// module_uuid_ref = ::CFUUIDCreateWithBytes ( NULL, +// module_uuid[0], +// module_uuid[1], +// module_uuid[2], +// module_uuid[3], +// module_uuid[4], +// module_uuid[5], +// module_uuid[6], +// module_uuid[7], +// module_uuid[8], +// module_uuid[9], +// module_uuid[10], +// module_uuid[11], +// module_uuid[12], +// module_uuid[13], +// module_uuid[14], +// module_uuid[15]); +// +// if (module_uuid_ref) +// { +// CFURLRef dsym_url = NULL; +// CFURLRef exec_url = NULL; +// +// // if (DBGCopyFullDSYMURLForUUID) +// { +// char exec_path[PATH_MAX]; +// if (module->GetFileSpec().GetPath(exec_path, sizeof(exec_path))) +// { +// exec_url = CFURLCreateFromFileSystemRepresentation ( NULL, +// (const UInt8 *)exec_path, +// strlen(exec_path), +// FALSE); +// } +// +// dsym_url = DBGCopyFullDSYMURLForUUID(module_uuid_ref, exec_url); +// } +// // else +// // { +// // dsym_url = DBGCopyDSYMURLForUUID(module_uuid_ref); +// // } +// +// if (exec_url) +// { +// ::CFRelease (exec_url); +// exec_url = NULL; +// } +// +// ::CFRelease(module_uuid_ref); +// module_uuid_ref = NULL; +// +// if (dsym_url) +// { +// char dsym_path[PATH_MAX]; +// Boolean success = CFURLGetFileSystemRepresentation (dsym_url, true, (UInt8*)dsym_path, sizeof(dsym_path)-1); +// +// ::CFRelease(dsym_url), dsym_url = NULL; +// +// if (success) +// { +// dsym_fspec.SetFile(dsym_path); +// +// // Some newer versions of DebugSymbols will return a full path into a dSYM bundle +// // that points to the correct mach file within the dSYM bundle (MH_DSYM mach file +// // type). +// dsym_objfile = ObjectFile::FindPlugin(module, &dsym_fspec, 0); +// +// // Olders versions of DebugSymbols will return a path to a dSYM bundle. +// if (dsym_objfile == NULL) +// dsym_objfile = LocateDSYMMachFileInDSYMBundle (module, dsym_fspec); +// } +// } +// } +// } +// } +// return dsym_objfile; +//} + +static void +ReplaceDSYMSectionsWithExecutableSections (ObjectFile *exec_objfile, ObjectFile *dsym_objfile) +{ + // We need both the executable and the dSYM to live off of the + // same section lists. So we take all of the sections from the + // executable, and replace them in the dSYM. This allows section + // offset addresses that come from the dSYM to automatically + // get updated as images (shared libraries) get loaded and + // unloaded. + SectionList *exec_section_list = exec_objfile->GetSectionList(); + SectionList *dsym_section_list = dsym_objfile->GetSectionList(); + if (exec_section_list && dsym_section_list) + { + const uint32_t num_exec_sections = dsym_section_list->GetSize(); + uint32_t exec_sect_idx; + for (exec_sect_idx = 0; exec_sect_idx < num_exec_sections; ++exec_sect_idx) + { + SectionSP exec_sect_sp(exec_section_list->GetSectionAtIndex(exec_sect_idx)); + if (exec_sect_sp.get()) + { + // Try and replace any sections that exist in both the executable + // and in the dSYM with those from the executable. If we fail to + // replace the one in the dSYM, then add the executable section to + // the dSYM. + if (dsym_section_list->ReplaceSection(exec_sect_sp->GetID(), exec_sect_sp, 0) == false) + dsym_section_list->AddSection(exec_sect_sp); + } + } + } +} + +void +SymbolVendorMacOSX::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +SymbolVendorMacOSX::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +SymbolVendorMacOSX::GetPluginNameStatic() +{ + return "symbol-vendor.macosx"; +} + +const char * +SymbolVendorMacOSX::GetPluginDescriptionStatic() +{ + return "Symbol vendor for MacOSX that looks for dSYM files that match executables."; +} + + + +//---------------------------------------------------------------------- +// CreateInstance +// +// Platforms can register a callback to use when creating symbol +// vendors to allow for complex debug information file setups, and to +// also allow for finding separate debug information files. +//---------------------------------------------------------------------- +SymbolVendor* +SymbolVendorMacOSX::CreateInstance(Module* module) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolVendorMacOSX::CreateInstance (module = %s/%s)", + module->GetFileSpec().GetDirectory().AsCString(), + module->GetFileSpec().GetFilename().AsCString()); + SymbolVendorMacOSX* symbol_vendor = new SymbolVendorMacOSX(module); + if (symbol_vendor) + { + char path[PATH_MAX]; + path[0] = '\0'; + + // Try and locate the dSYM file on Mac OS X + ObjectFile * obj_file = module->GetObjectFile(); + if (obj_file) + { + Timer scoped_timer2 ("SymbolVendorMacOSX::CreateInstance () locate dSYM", + "SymbolVendorMacOSX::CreateInstance (module = %s/%s) locate dSYM", + module->GetFileSpec().GetDirectory().AsCString(), + module->GetFileSpec().GetFilename().AsCString()); + + FileSpec dsym_fspec; + std::auto_ptr<ObjectFile> dsym_objfile_ap; + const FileSpec &file_spec = obj_file->GetFileSpec(); + if (file_spec) + { + dsym_fspec = Symbols::LocateExecutableSymbolFile (&file_spec, &module->GetArchitecture(), &module->GetUUID()); + + if (dsym_fspec) + { + dsym_objfile_ap.reset(ObjectFile::FindPlugin(module, &dsym_fspec, 0, dsym_fspec.GetByteSize())); + if (UUIDsMatch(module, dsym_objfile_ap.get())) + { + ReplaceDSYMSectionsWithExecutableSections (obj_file, dsym_objfile_ap.get()); + symbol_vendor->AddSymbolFileRepresendation(dsym_objfile_ap.release()); + return symbol_vendor; + } + } + } + + // Just create our symbol vendor using the current objfile as this is either + // an executable with no dSYM (that we could locate), and executable with + // a dSYM that has a UUID that doesn't match, or it is a dSYM file itself. + symbol_vendor->AddSymbolFileRepresendation(obj_file); + } + } + return symbol_vendor; +} + + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +SymbolVendorMacOSX::GetPluginName() +{ + return "SymbolVendorMacOSX"; +} + +const char * +SymbolVendorMacOSX::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +SymbolVendorMacOSX::GetPluginVersion() +{ + return 1; +} + +void +SymbolVendorMacOSX::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +SymbolVendorMacOSX::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +SymbolVendorMacOSX::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + diff --git a/lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h b/lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h new file mode 100644 index 00000000000..adbf6489e27 --- /dev/null +++ b/lldb/source/Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h @@ -0,0 +1,71 @@ +//===-- SymbolVendorMacOSX.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_SymbolVendorMacOSX_h_ +#define liblldb_SymbolVendorMacOSX_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Symbol/SymbolVendor.h" + +class SymbolVendorMacOSX : public lldb_private::SymbolVendor +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::SymbolVendor* + CreateInstance (lldb_private::Module *module); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolVendorMacOSX (lldb_private::Module *module); + + virtual + ~SymbolVendorMacOSX(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + + +private: + DISALLOW_COPY_AND_ASSIGN (SymbolVendorMacOSX); +}; + +#endif // liblldb_SymbolVendorMacOSX_h_ |