1// Copyright (c) 2010 Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
31
32// language.cc: Subclasses and singletons for google_breakpad::Language.
33// See language.h for details.
34
35#include "common/language.h"
36
37#include <stdlib.h>
38#include <array>
39
40#if !defined(__ANDROID__)
41#include <cxxabi.h>
42#endif
43
44#if defined(HAVE_RUSTC_DEMANGLE)
45#include <rustc_demangle.h>
46#endif
47
48#include <limits>
49
50namespace {
51
52string MakeQualifiedNameWithSeparator(const string& parent_name,
53 const char* separator,
54 const string& name) {
55 if (parent_name.empty()) {
56 return name;
57 }
58
59 return parent_name + separator + name;
60}
61
62} // namespace
63
64namespace google_breakpad {
65
66// C++ language-specific operations.
67class CPPLanguage: public Language {
68 public:
69 CPPLanguage() {}
70
71 string MakeQualifiedName(const string& parent_name,
72 const string& name) const {
73 return MakeQualifiedNameWithSeparator(parent_name, "::", name);
74 }
75
76 virtual DemangleResult DemangleName(const string& mangled,
77 string* demangled) const {
78#if defined(__ANDROID__)
79 // Android NDK doesn't provide abi::__cxa_demangle.
80 demangled->clear();
81 return kDontDemangle;
82#else
83 // Attempting to demangle non-C++ symbols with the C++ demangler would print
84 // warnings and fail, so return kDontDemangle for these.
85 if (!IsMangledName(mangled)) {
86 demangled->clear();
87 return kDontDemangle;
88 }
89
90 int status;
91 char* demangled_c =
92 abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status);
93
94 DemangleResult result;
95 if (status == 0) {
96 result = kDemangleSuccess;
97 demangled->assign(demangled_c);
98 } else {
99 result = kDemangleFailure;
100 demangled->clear();
101 }
102
103 if (demangled_c) {
104 free(reinterpret_cast<void*>(demangled_c));
105 }
106
107 return result;
108#endif
109 }
110
111 private:
112 static bool IsMangledName(const string& name) {
113 // NOTE: For proper cross-compilation support, this should depend on target
114 // binary's platform, not current build platform.
115#if defined(__APPLE__)
116 // Mac C++ symbols can have up to 4 underscores, followed by a "Z".
117 // Non-C++ symbols are not coded that way, but may have leading underscores.
118 size_t i = name.find_first_not_of('_');
119 return i > 0 && i != string::npos && i <= 4 && name[i] == 'Z';
120#else
121 // Linux C++ symbols always start with "_Z".
122 return name.size() > 2 && name[0] == '_' && name[1] == 'Z';
123#endif
124 }
125};
126
127CPPLanguage CPPLanguageSingleton;
128
129// Java language-specific operations.
130class JavaLanguage: public Language {
131 public:
132 JavaLanguage() {}
133
134 string MakeQualifiedName(const string& parent_name,
135 const string& name) const {
136 return MakeQualifiedNameWithSeparator(parent_name, ".", name);
137 }
138};
139
140JavaLanguage JavaLanguageSingleton;
141
142// Swift language-specific operations.
143class SwiftLanguage: public Language {
144 public:
145 SwiftLanguage() {}
146
147 string MakeQualifiedName(const string& parent_name,
148 const string& name) const {
149 return MakeQualifiedNameWithSeparator(parent_name, ".", name);
150 }
151
152 virtual DemangleResult DemangleName(const string& mangled,
153 string* demangled) const {
154 // There is no programmatic interface to a Swift demangler. Pass through the
155 // mangled form because it encodes more information than the qualified name
156 // that would have been built by MakeQualifiedName(). The output can be
157 // post-processed by xcrun swift-demangle to transform mangled Swift names
158 // into something more readable.
159 demangled->assign(mangled);
160 return kDemangleSuccess;
161 }
162};
163
164SwiftLanguage SwiftLanguageSingleton;
165
166// Rust language-specific operations.
167class RustLanguage: public Language {
168 public:
169 RustLanguage() {}
170
171 string MakeQualifiedName(const string& parent_name,
172 const string& name) const {
173 return MakeQualifiedNameWithSeparator(parent_name, ".", name);
174 }
175
176 virtual DemangleResult DemangleName(const string& mangled,
177 string* demangled) const {
178 // Rust names use GCC C++ name mangling, but demangling them with
179 // abi_demangle doesn't produce stellar results due to them having
180 // another layer of encoding.
181 // If callers provide rustc-demangle, use that.
182#if defined(HAVE_RUSTC_DEMANGLE)
183 std::array<char, 1 * 1024 * 1024> rustc_demangled;
184 if (rustc_demangle(mangled.c_str(), rustc_demangled.data(),
185 rustc_demangled.size()) == 0) {
186 return kDemangleFailure;
187 }
188 demangled->assign(rustc_demangled.data());
189#else
190 // Otherwise, pass through the mangled name so callers can demangle
191 // after the fact.
192 demangled->assign(mangled);
193#endif
194 return kDemangleSuccess;
195 }
196};
197
198RustLanguage RustLanguageSingleton;
199
200// Assembler language-specific operations.
201class AssemblerLanguage: public Language {
202 public:
203 AssemblerLanguage() {}
204
205 bool HasFunctions() const { return false; }
206 string MakeQualifiedName(const string& parent_name,
207 const string& name) const {
208 return name;
209 }
210};
211
212AssemblerLanguage AssemblerLanguageSingleton;
213
214const Language * const Language::CPlusPlus = &CPPLanguageSingleton;
215const Language * const Language::Java = &JavaLanguageSingleton;
216const Language * const Language::Swift = &SwiftLanguageSingleton;
217const Language * const Language::Rust = &RustLanguageSingleton;
218const Language * const Language::Assembler = &AssemblerLanguageSingleton;
219
220} // namespace google_breakpad
221