From 7398ce15b79daf038cd07fbae4e37e183e99788d Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Fri, 23 Sep 2016 14:22:42 -0400 Subject: [PATCH] Initial support for dumping DWARF corresponding to Swift code The DWARF data for Swift code has a top-level DW_TAG_module DIE as the child of the DW_TAG_compile_unit DIE and the parent of the DW_TAG_subprogram DIEs that dump_syms uses to locate functions. dump_syms needs to process DW_TAG_module DIEs as introducing nested scopes to make it work with Swift. This also reworks demangling to be language-specific, so that the C++ demangler isn't invoked when processing Swift code. The DWARF data for Swift code presents its mangled names in the same form as used for C++ (DW_AT_MIPS_linkage_name or DW_AT_linkage_name) but the mangling is Swift-specific (beginning with _T instead of _Z). There is no programmatic interface to a Swift name demangler as an analogue to C++'s __cxa_demangle(), so mangled Swift names are exposed as-is. Xcode's "xcrun swift-demangle" can be used to post-process these mangled Swift names on macOS. Support for mangled names presented in a DW_AT_linkage_name attribute, as used by DWARF 4, is added. This supersedes the earlier use of DW_AT_MIPS_linkage_name. BUG=google-breakpad:702,google-breakpad:715 R=ted.mielczarek@gmail.com Review URL: https://codereview.chromium.org/2147523005 . --- src/common/dwarf/dwarf2enums.h | 7 +- src/common/dwarf_cu_to_module.cc | 45 +++++------ src/common/dwarf_cu_to_module.h | 2 +- src/common/dwarf_cu_to_module_unittest.cc | 2 +- src/common/language.cc | 91 +++++++++++++++++++++-- src/common/language.h | 16 ++++ 6 files changed, 130 insertions(+), 33 deletions(-) diff --git a/src/common/dwarf/dwarf2enums.h b/src/common/dwarf/dwarf2enums.h index 6b8a7245..09567555 100644 --- a/src/common/dwarf/dwarf2enums.h +++ b/src/common/dwarf/dwarf2enums.h @@ -232,6 +232,8 @@ enum DwarfAttribute { DW_AT_call_column = 0x57, DW_AT_call_file = 0x58, DW_AT_call_line = 0x59, + // DWARF 4 + DW_AT_linkage_name = 0x6e, // SGI/MIPS extensions. DW_AT_MIPS_fde = 0x2001, DW_AT_MIPS_loop_begin = 0x2002, @@ -499,7 +501,7 @@ enum DwarfOpcode { DW_OP_call_frame_cfa =0x9c, DW_OP_bit_piece =0x9d, DW_OP_lo_user =0xe0, - DW_OP_hi_user =0xff, + DW_OP_hi_user =0xff, // GNU extensions DW_OP_GNU_push_tls_address =0xe0, // Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission. @@ -542,6 +544,7 @@ enum DwarfLanguage DW_LANG_ObjC_plus_plus =0x0011, DW_LANG_UPC =0x0012, DW_LANG_D =0x0013, + DW_LANG_Swift =0x001e, // Implementation-defined language code range. DW_LANG_lo_user = 0x8000, DW_LANG_hi_user = 0xffff, @@ -668,7 +671,7 @@ enum DwarfPointerEncoding // encoding (except DW_EH_PE_aligned), and indicates that the // encoded value represents the address at which the true address // is stored, not the true address itself. - DW_EH_PE_indirect = 0x80 + DW_EH_PE_indirect = 0x80 }; } // namespace dwarf2reader diff --git a/src/common/dwarf_cu_to_module.cc b/src/common/dwarf_cu_to_module.cc index 479e39b2..100f3962 100644 --- a/src/common/dwarf_cu_to_module.cc +++ b/src/common/dwarf_cu_to_module.cc @@ -39,9 +39,6 @@ #include "common/dwarf_cu_to_module.h" #include -#if !defined(__ANDROID__) -#include -#endif #include #include #include @@ -350,20 +347,22 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString( case dwarf2reader::DW_AT_name: name_attribute_ = AddStringToPool(data); break; - case dwarf2reader::DW_AT_MIPS_linkage_name: { - char* demangled = NULL; - int status = -1; -#if !defined(__ANDROID__) // Android NDK doesn't provide abi::__cxa_demangle. - demangled = abi::__cxa_demangle(data.c_str(), NULL, NULL, &status); -#endif - if (status != 0) { - cu_context_->reporter->DemangleError(data, status); - demangled_name_ = ""; - break; - } - if (demangled) { - demangled_name_ = AddStringToPool(demangled); - free(reinterpret_cast(demangled)); + case dwarf2reader::DW_AT_MIPS_linkage_name: + case dwarf2reader::DW_AT_linkage_name: { + string demangled; + Language::DemangleResult result = + cu_context_->language->DemangleName(data, &demangled); + switch (result) { + case Language::kDemangleSuccess: + demangled_name_ = AddStringToPool(demangled); + break; + + case Language::kDemangleFailure: + cu_context_->reporter->DemangleError(data); + // fallthrough + case Language::kDontDemangle: + demangled_name_.clear(); + break; } break; } @@ -676,11 +675,10 @@ void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) { filename_.c_str(), offset); } -void DwarfCUToModule::WarningReporter::DemangleError( - const string &input, int error) { +void DwarfCUToModule::WarningReporter::DemangleError(const string &input) { CUHeading(); - fprintf(stderr, "%s: warning: failed to demangle %s with error %d\n", - filename_.c_str(), input.c_str(), error); + fprintf(stderr, "%s: warning: failed to demangle %s\n", + filename_.c_str(), input.c_str()); } void DwarfCUToModule::WarningReporter::UnhandledInterCUReference( @@ -761,6 +759,7 @@ dwarf2reader::DIEHandler *DwarfCUToModule::FindChildHandler( case dwarf2reader::DW_TAG_class_type: case dwarf2reader::DW_TAG_structure_type: case dwarf2reader::DW_TAG_union_type: + case dwarf2reader::DW_TAG_module: return new NamedScopeHandler(cu_context_.get(), child_context_.get(), offset); default: @@ -774,6 +773,10 @@ void DwarfCUToModule::SetLanguage(DwarfLanguage language) { cu_context_->language = Language::Java; break; + case dwarf2reader::DW_LANG_Swift: + cu_context_->language = Language::Swift; + break; + // DWARF has no generic language code for assembly language; this is // what the GNU toolchain uses. case dwarf2reader::DW_LANG_Mips_Assembler: diff --git a/src/common/dwarf_cu_to_module.h b/src/common/dwarf_cu_to_module.h index fca92710..a36b82b0 100644 --- a/src/common/dwarf_cu_to_module.h +++ b/src/common/dwarf_cu_to_module.h @@ -202,7 +202,7 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler { virtual void UnnamedFunction(uint64 offset); // __cxa_demangle() failed to demangle INPUT. - virtual void DemangleError(const string &input, int error); + virtual void DemangleError(const string &input); // The DW_FORM_ref_addr at OFFSET to TARGET was not handled because // FilePrivate did not retain the inter-CU specification data. diff --git a/src/common/dwarf_cu_to_module_unittest.cc b/src/common/dwarf_cu_to_module_unittest.cc index 619e90a2..e789f9da 100644 --- a/src/common/dwarf_cu_to_module_unittest.cc +++ b/src/common/dwarf_cu_to_module_unittest.cc @@ -83,7 +83,7 @@ class MockWarningReporter: public DwarfCUToModule::WarningReporter { MOCK_METHOD1(UncoveredFunction, void(const Module::Function &function)); MOCK_METHOD1(UncoveredLine, void(const Module::Line &line)); MOCK_METHOD1(UnnamedFunction, void(uint64 offset)); - MOCK_METHOD2(DemangleError, void(const string &input, int error)); + MOCK_METHOD1(DemangleError, void(const string &input)); MOCK_METHOD2(UnhandledInterCUReference, void(uint64 offset, uint64 target)); }; diff --git a/src/common/language.cc b/src/common/language.cc index c2fd81f6..3a598f69 100644 --- a/src/common/language.cc +++ b/src/common/language.cc @@ -34,18 +34,66 @@ #include "common/language.h" +#include + +#if !defined(__ANDROID__) +#include +#endif + +#include + +namespace { + +string MakeQualifiedNameWithSeparator(const string& parent_name, + const char* separator, + const string& name) { + if (parent_name.empty()) { + return name; + } + + return parent_name + separator + name; +} + +} // namespace + namespace google_breakpad { // C++ language-specific operations. class CPPLanguage: public Language { public: CPPLanguage() {} + string MakeQualifiedName(const string &parent_name, const string &name) const { - if (parent_name.empty()) - return name; - else - return parent_name + "::" + name; + return MakeQualifiedNameWithSeparator(parent_name, "::", name); + } + + virtual DemangleResult DemangleName(const string& mangled, + std::string* demangled) const { +#if defined(__ANDROID__) + // Android NDK doesn't provide abi::__cxa_demangle. + demangled->clear(); + return kDontDemangle; +#else + int status; + char* demangled_c = + abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status); + + DemangleResult result; + if (status == 0) { + result = kDemangleSuccess; + demangled->clear(); + } else { + result = kDemangleFailure; + demangled->assign(demangled_c); + } + + if (demangled_c) { + free(reinterpret_cast(demangled_c)); + } + + return result; +#endif } }; @@ -54,19 +102,45 @@ CPPLanguage CPPLanguageSingleton; // Java language-specific operations. class JavaLanguage: public Language { public: + JavaLanguage() {} + string MakeQualifiedName(const string &parent_name, const string &name) const { - if (parent_name.empty()) - return name; - else - return parent_name + "." + name; + return MakeQualifiedNameWithSeparator(parent_name, ".", name); } }; JavaLanguage JavaLanguageSingleton; +// Swift language-specific operations. +class SwiftLanguage: public Language { + public: + SwiftLanguage() {} + + string MakeQualifiedName(const string &parent_name, + const string &name) const { + return MakeQualifiedNameWithSeparator(parent_name, ".", name); + } + + virtual DemangleResult DemangleName(const string& mangled, + std::string* demangled) const { + // There is no programmatic interface to a Swift demangler. Pass through the + // mangled form because it encodes more information than the qualified name + // that would have been built by MakeQualifiedName(). The output can be + // post-processed by xcrun swift-demangle to transform mangled Swift names + // into something more readable. + demangled->assign(mangled); + return kDemangleSuccess; + } +}; + +SwiftLanguage SwiftLanguageSingleton; + // Assembler language-specific operations. class AssemblerLanguage: public Language { + public: + AssemblerLanguage() {} + bool HasFunctions() const { return false; } string MakeQualifiedName(const string &parent_name, const string &name) const { @@ -78,6 +152,7 @@ AssemblerLanguage AssemblerLanguageSingleton; const Language * const Language::CPlusPlus = &CPPLanguageSingleton; const Language * const Language::Java = &JavaLanguageSingleton; +const Language * const Language::Swift = &SwiftLanguageSingleton; const Language * const Language::Assembler = &AssemblerLanguageSingleton; } // namespace google_breakpad diff --git a/src/common/language.h b/src/common/language.h index bbe30334..05786199 100644 --- a/src/common/language.h +++ b/src/common/language.h @@ -77,9 +77,25 @@ class Language { virtual string MakeQualifiedName (const string &parent_name, const string &name) const = 0; + enum DemangleResult { + // Demangling was not performed because it’s not appropriate to attempt. + kDontDemangle = -1, + + kDemangleSuccess, + kDemangleFailure, + }; + + // Wraps abi::__cxa_demangle() or similar for languages where appropriate. + virtual DemangleResult DemangleName(const string& mangled, + std::string* demangled) const { + demangled->clear(); + return kDontDemangle; + } + // Instances for specific languages. static const Language * const CPlusPlus, * const Java, + * const Swift, * const Assembler; };