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 .
This commit is contained in:
Mark Mentovai 2016-09-23 14:22:42 -04:00
parent 138886803c
commit 7398ce15b7
6 changed files with 130 additions and 33 deletions

View File

@ -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

View File

@ -39,9 +39,6 @@
#include "common/dwarf_cu_to_module.h"
#include <assert.h>
#if !defined(__ANDROID__)
#include <cxxabi.h>
#endif
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
@ -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<void*>(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:

View File

@ -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.

View File

@ -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));
};

View File

@ -34,18 +34,66 @@
#include "common/language.h"
#include <stdlib.h>
#if !defined(__ANDROID__)
#include <cxxabi.h>
#endif
#include <limits>
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<void*>(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

View File

@ -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 its 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;
};