diff options
author | ivanpe@google.com <ivanpe@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-05 10:16:03 +0000 |
---|---|---|
committer | ivanpe@google.com <ivanpe@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-05 10:16:03 +0000 |
commit | db024825de7b10be6382b216c11cda7c8aa16af4 (patch) | |
tree | 2486262b7faeda9630d6aca48ef396d2ca6406a6 /base/third_party | |
parent | 704a900dae24b39386940a88978a3b31fdd0a805 (diff) | |
download | chromium_src-db024825de7b10be6382b216c11cda7c8aa16af4.zip chromium_src-db024825de7b10be6382b216c11cda7c8aa16af4.tar.gz chromium_src-db024825de7b10be6382b216c11cda7c8aa16af4.tar.bz2 |
Copying the following files AS-IS from google.glog r141:
- demangle.cc
- demangle.h
- symbolize.cc
- symbolize.h
The new functionality is a callback mechanism to allow users of
google.glog to provide their own implementation of
OpenObjectFileContainingPcAndGetStartAddress.
BUG=
Review URL: https://codereview.chromium.org/180163013
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@254996 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/third_party')
-rw-r--r-- | base/third_party/symbolize/README.chromium | 5 | ||||
-rw-r--r-- | base/third_party/symbolize/demangle.cc | 379 | ||||
-rw-r--r-- | base/third_party/symbolize/symbolize.cc | 197 | ||||
-rw-r--r-- | base/third_party/symbolize/symbolize.h | 22 |
4 files changed, 430 insertions, 173 deletions
diff --git a/base/third_party/symbolize/README.chromium b/base/third_party/symbolize/README.chromium index 520a3db..de92794 100644 --- a/base/third_party/symbolize/README.chromium +++ b/base/third_party/symbolize/README.chromium @@ -3,16 +3,13 @@ URL: http://code.google.com/p/google-glog/ License: BSD The following files are copied AS-IS from: -http://code.google.com/p/google-glog/source/browse/#svn/trunk/src (r76) +http://code.google.com/p/google-glog/source/browse/#svn/trunk/src (r141) - demangle.cc - demangle.h - symbolize.cc - symbolize.h -r137 (https://code.google.com/p/google-glog/source/detail?r=137) was merged -after that. - The following files are minimal stubs created for use in Chromium: - config.h diff --git a/base/third_party/symbolize/demangle.cc b/base/third_party/symbolize/demangle.cc index 46556bf..e858181 100644 --- a/base/third_party/symbolize/demangle.cc +++ b/base/third_party/symbolize/demangle.cc @@ -28,6 +28,11 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Author: Satoru Takabayashi +// +// For reference check out: +// http://www.codesourcery.com/public/cxx-abi/abi.html#mangling +// +// Note that we only have partial C++0x support yet. #include <stdio.h> // for NULL #include "demangle.h" @@ -138,14 +143,12 @@ static const AbbrevPair kSubstitutionList[] = { // State needed for demangling. typedef struct { const char *mangled_cur; // Cursor of mangled name. - const char *mangled_end; // End of mangled name. char *out_cur; // Cursor of output string. const char *out_begin; // Beginning of output string. const char *out_end; // End of output string. const char *prev_name; // For constructors/destructors. int prev_name_length; // For constructors/destructors. - int nest_level; // For nested names. - int number; // Remember the previous number. + short nest_level; // For nested names. bool append; // Append flag. bool overflowed; // True if output gets overflowed. } State; @@ -161,6 +164,16 @@ static size_t StrLen(const char *str) { return len; } +// Returns true if "str" has at least "n" characters remaining. +static bool AtLeastNumCharsRemaining(const char *str, int n) { + for (int i = 0; i < n; ++i) { + if (str[i] == '\0') { + return false; + } + } + return true; +} + // Returns true if "str" has "prefix" as a prefix. static bool StrPrefix(const char *str, const char *prefix) { size_t i = 0; @@ -174,39 +187,33 @@ static bool StrPrefix(const char *str, const char *prefix) { static void InitState(State *state, const char *mangled, char *out, int out_size) { state->mangled_cur = mangled; - state->mangled_end = mangled + StrLen(mangled); state->out_cur = out; state->out_begin = out; state->out_end = out + out_size; state->prev_name = NULL; state->prev_name_length = -1; state->nest_level = -1; - state->number = -1; state->append = true; state->overflowed = false; } -// Calculates the remaining length of the mangled name. -static int RemainingLength(State *state) { - return state->mangled_end - state->mangled_cur; -} - -// Returns true and advances "mangled_cur" if we find "c" at -// "mangled_cur" position. -static bool ParseChar(State *state, const char c) { - if (RemainingLength(state) >= 1 && *state->mangled_cur == c) { +// Returns true and advances "mangled_cur" if we find "one_char_token" +// at "mangled_cur" position. It is assumed that "one_char_token" does +// not contain '\0'. +static bool ParseOneCharToken(State *state, const char one_char_token) { + if (state->mangled_cur[0] == one_char_token) { ++state->mangled_cur; return true; } return false; } -// Returns true and advances "mangled_cur" if we find "two_chars" at -// "mangled_cur" position. -static bool ParseTwoChar(State *state, const char *two_chars) { - if (RemainingLength(state) >= 2 && - state->mangled_cur[0] == two_chars[0] && - state->mangled_cur[1] == two_chars[1]) { +// Returns true and advances "mangled_cur" if we find "two_char_token" +// at "mangled_cur" position. It is assumed that "two_char_token" does +// not contain '\0'. +static bool ParseTwoCharToken(State *state, const char *two_char_token) { + if (state->mangled_cur[0] == two_char_token[0] && + state->mangled_cur[1] == two_char_token[1]) { state->mangled_cur += 2; return true; } @@ -216,13 +223,10 @@ static bool ParseTwoChar(State *state, const char *two_chars) { // Returns true and advances "mangled_cur" if we find any character in // "char_class" at "mangled_cur" position. static bool ParseCharClass(State *state, const char *char_class) { - if (state->mangled_cur == state->mangled_end) { - return false; - } const char *p = char_class; for (; *p != '\0'; ++p) { - if (*state->mangled_cur == *p) { - state->mangled_cur += 1; + if (state->mangled_cur[0] == *p) { + ++state->mangled_cur; return true; } } @@ -230,7 +234,7 @@ static bool ParseCharClass(State *state, const char *char_class) { } // This function is used for handling an optional non-terminal. -static bool Optional(bool status) { +static bool Optional(bool) { return true; } @@ -245,6 +249,16 @@ static bool OneOrMore(ParseFunc parse_func, State *state) { return false; } +// This function is used for handling <non-terminal>* syntax. The function +// always returns true and must be followed by a termination token or a +// terminating sequence not handled by parse_func (e.g. +// ParseOneCharToken(state, 'E')). +static bool ZeroOrMore(ParseFunc parse_func, State *state) { + while (parse_func(state)) { + } + return true; +} + // Append "str" at "out_cur". If there is an overflow, "overflowed" // is set to true for later use. The output string is ensured to // always terminate with '\0' as long as there is no overflow. @@ -270,7 +284,37 @@ static bool IsLower(char c) { } static bool IsAlpha(char c) { - return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +static bool IsDigit(char c) { + return c >= '0' && c <= '9'; +} + +// Returns true if "str" is a function clone suffix. These suffixes are used +// by GCC 4.5.x and later versions to indicate functions which have been +// cloned during optimization. We treat any sequence (.<alpha>+.<digit>+)+ as +// a function clone suffix. +static bool IsFunctionCloneSuffix(const char *str) { + size_t i = 0; + while (str[i] != '\0') { + // Consume a single .<alpha>+.<digit>+ sequence. + if (str[i] != '.' || !IsAlpha(str[i + 1])) { + return false; + } + i += 2; + while (IsAlpha(str[i])) { + ++i; + } + if (str[i] != '.' || !IsDigit(str[i + 1])) { + return false; + } + i += 2; + while (IsDigit(str[i])) { + ++i; + } + } + return true; // Consumed everything in "str". } // Append "str" with some tweaks, iff "append" state is true. @@ -309,7 +353,7 @@ static bool EnterNestedName(State *state) { } // This function is used for handling nested names. -static bool LeaveNestedName(State *state, int prev_value) { +static bool LeaveNestedName(State *state, short prev_value) { state->nest_level = prev_value; return true; } @@ -349,11 +393,11 @@ static void MaybeCancelLastSeparator(State *state) { } } -// Returns true if identifier pointed by "mangled_cur" is anonymous -// namespace. -static bool IdentifierIsAnonymousNamespace(State *state) { - const char anon_prefix[] = "_GLOBAL__N_"; - return (state->number > sizeof(anon_prefix) - 1 && // Should be longer. +// Returns true if the identifier of the given length pointed to by +// "mangled_cur" is anonymous namespace. +static bool IdentifierIsAnonymousNamespace(State *state, int length) { + static const char anon_prefix[] = "_GLOBAL__N_"; + return (length > (int)sizeof(anon_prefix) - 1 && // Should be longer. StrPrefix(state->mangled_cur, anon_prefix)); } @@ -368,10 +412,10 @@ static bool ParsePrefix(State *state); static bool ParseUnqualifiedName(State *state); static bool ParseSourceName(State *state); static bool ParseLocalSourceName(State *state); -static bool ParseNumber(State *state); +static bool ParseNumber(State *state, int *number_out); static bool ParseFloatNumber(State *state); static bool ParseSeqId(State *state); -static bool ParseIdentifier(State *state); +static bool ParseIdentifier(State *state, int length); static bool ParseOperatorName(State *state); static bool ParseSpecialName(State *state); static bool ParseCallOffset(State *state); @@ -428,17 +472,7 @@ static bool ParseSubstitution(State *state); // <mangled-name> ::= _Z <encoding> static bool ParseMangledName(State *state) { - if (ParseTwoChar(state, "_Z") && ParseEncoding(state)) { - // Append trailing version suffix if any. - // ex. _Z3foo@@GLIBCXX_3.4 - if (state->mangled_cur < state->mangled_end && - state->mangled_cur[0] == '@') { - MaybeAppend(state, state->mangled_cur); - state->mangled_cur = state->mangled_end; - } - return true; - } - return false; + return ParseTwoCharToken(state, "_Z") && ParseEncoding(state); } // <encoding> ::= <(function) name> <bare-function-type> @@ -488,7 +522,7 @@ static bool ParseUnscopedName(State *state) { } State copy = *state; - if (ParseTwoChar(state, "St") && + if (ParseTwoCharToken(state, "St") && MaybeAppend(state, "std::") && ParseUnqualifiedName(state)) { return true; @@ -507,12 +541,12 @@ static bool ParseUnscopedTemplateName(State *state) { // ::= N [<CV-qualifiers>] <template-prefix> <template-args> E static bool ParseNestedName(State *state) { State copy = *state; - if (ParseChar(state, 'N') && + if (ParseOneCharToken(state, 'N') && EnterNestedName(state) && Optional(ParseCVQualifiers(state)) && ParsePrefix(state) && LeaveNestedName(state, copy.nest_level) && - ParseChar(state, 'E')) { + ParseOneCharToken(state, 'E')) { return true; } *state = copy; @@ -565,7 +599,8 @@ static bool ParseUnqualifiedName(State *state) { // <source-name> ::= <positive length number> <identifier> static bool ParseSourceName(State *state) { State copy = *state; - if (ParseNumber(state) && ParseIdentifier(state)) { + int length = -1; + if (ParseNumber(state, &length) && ParseIdentifier(state, length)) { return true; } *state = copy; @@ -579,7 +614,7 @@ static bool ParseSourceName(State *state) { // http://gcc.gnu.org/viewcvs?view=rev&revision=124467 static bool ParseLocalSourceName(State *state) { State copy = *state; - if (ParseChar(state, 'L') && ParseSourceName(state) && + if (ParseOneCharToken(state, 'L') && ParseSourceName(state) && Optional(ParseDiscriminator(state))) { return true; } @@ -588,15 +623,17 @@ static bool ParseLocalSourceName(State *state) { } // <number> ::= [n] <non-negative decimal integer> -static bool ParseNumber(State *state) { +// If "number_out" is non-null, then *number_out is set to the value of the +// parsed number on success. +static bool ParseNumber(State *state, int *number_out) { int sign = 1; - if (ParseChar(state, 'n')) { + if (ParseOneCharToken(state, 'n')) { sign = -1; } const char *p = state->mangled_cur; int number = 0; - for (;p < state->mangled_end; ++p) { - if ((*p >= '0' && *p <= '9')) { + for (;*p != '\0'; ++p) { + if (IsDigit(*p)) { number = number * 10 + (*p - '0'); } else { break; @@ -604,7 +641,9 @@ static bool ParseNumber(State *state) { } if (p != state->mangled_cur) { // Conversion succeeded. state->mangled_cur = p; - state->number = number * sign; + if (number_out != NULL) { + *number_out = number * sign; + } return true; } return false; @@ -614,19 +653,13 @@ static bool ParseNumber(State *state) { // hexadecimal string. static bool ParseFloatNumber(State *state) { const char *p = state->mangled_cur; - int number = 0; - for (;p < state->mangled_end; ++p) { - if ((*p >= '0' && *p <= '9')) { - number = number * 16 + (*p - '0'); - } else if (*p >= 'a' && *p <= 'f') { - number = number * 16 + (*p - 'a' + 10); - } else { + for (;*p != '\0'; ++p) { + if (!IsDigit(*p) && !(*p >= 'a' && *p <= 'f')) { break; } } if (p != state->mangled_cur) { // Conversion succeeded. state->mangled_cur = p; - state->number = number; return true; } return false; @@ -636,37 +669,30 @@ static bool ParseFloatNumber(State *state) { // using digits and upper case letters static bool ParseSeqId(State *state) { const char *p = state->mangled_cur; - int number = 0; - for (;p < state->mangled_end; ++p) { - if ((*p >= '0' && *p <= '9')) { - number = number * 36 + (*p - '0'); - } else if (*p >= 'A' && *p <= 'Z') { - number = number * 36 + (*p - 'A' + 10); - } else { + for (;*p != '\0'; ++p) { + if (!IsDigit(*p) && !(*p >= 'A' && *p <= 'Z')) { break; } } if (p != state->mangled_cur) { // Conversion succeeded. state->mangled_cur = p; - state->number = number; return true; } return false; } -// <identifier> ::= <unqualified source code identifier> -static bool ParseIdentifier(State *state) { - if (state->number == -1 || - RemainingLength(state) < state->number) { +// <identifier> ::= <unqualified source code identifier> (of given length) +static bool ParseIdentifier(State *state, int length) { + if (length == -1 || + !AtLeastNumCharsRemaining(state->mangled_cur, length)) { return false; } - if (IdentifierIsAnonymousNamespace(state)) { + if (IdentifierIsAnonymousNamespace(state, length)) { MaybeAppend(state, "(anonymous namespace)"); } else { - MaybeAppendWithLength(state, state->mangled_cur, state->number); + MaybeAppendWithLength(state, state->mangled_cur, length); } - state->mangled_cur += state->number; - state->number = -1; // Reset the number. + state->mangled_cur += length; return true; } @@ -674,12 +700,12 @@ static bool ParseIdentifier(State *state) { // ::= cv <type> # (cast) // ::= v <digit> <source-name> # vendor extended operator static bool ParseOperatorName(State *state) { - if (RemainingLength(state) < 2) { + if (!AtLeastNumCharsRemaining(state->mangled_cur, 2)) { return false; } // First check with "cv" (cast) case. State copy = *state; - if (ParseTwoChar(state, "cv") && + if (ParseTwoCharToken(state, "cv") && MaybeAppend(state, "operator ") && EnterNestedName(state) && ParseType(state) && @@ -689,7 +715,7 @@ static bool ParseOperatorName(State *state) { *state = copy; // Then vendor extended operators. - if (ParseChar(state, 'v') && ParseCharClass(state, "0123456789") && + if (ParseOneCharToken(state, 'v') && ParseCharClass(state, "0123456789") && ParseSourceName(state)) { return true; } @@ -738,34 +764,34 @@ static bool ParseOperatorName(State *state) { // stack traces. The are special data. static bool ParseSpecialName(State *state) { State copy = *state; - if (ParseChar(state, 'T') && + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "VTIS") && ParseType(state)) { return true; } *state = copy; - if (ParseTwoChar(state, "Tc") && ParseCallOffset(state) && + if (ParseTwoCharToken(state, "Tc") && ParseCallOffset(state) && ParseCallOffset(state) && ParseEncoding(state)) { return true; } *state = copy; - if (ParseTwoChar(state, "GV") && + if (ParseTwoCharToken(state, "GV") && ParseName(state)) { return true; } *state = copy; - if (ParseChar(state, 'T') && ParseCallOffset(state) && + if (ParseOneCharToken(state, 'T') && ParseCallOffset(state) && ParseEncoding(state)) { return true; } *state = copy; // G++ extensions - if (ParseTwoChar(state, "TC") && ParseType(state) && - ParseNumber(state) && ParseChar(state, '_') && + if (ParseTwoCharToken(state, "TC") && ParseType(state) && + ParseNumber(state, NULL) && ParseOneCharToken(state, '_') && DisableAppend(state) && ParseType(state)) { RestoreAppend(state, copy.append); @@ -773,23 +799,23 @@ static bool ParseSpecialName(State *state) { } *state = copy; - if (ParseChar(state, 'T') && ParseCharClass(state, "FJ") && + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "FJ") && ParseType(state)) { return true; } *state = copy; - if (ParseTwoChar(state, "GR") && ParseName(state)) { + if (ParseTwoCharToken(state, "GR") && ParseName(state)) { return true; } *state = copy; - if (ParseTwoChar(state, "GA") && ParseEncoding(state)) { + if (ParseTwoCharToken(state, "GA") && ParseEncoding(state)) { return true; } *state = copy; - if (ParseChar(state, 'T') && ParseCharClass(state, "hv") && + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "hv") && ParseCallOffset(state) && ParseEncoding(state)) { return true; } @@ -801,14 +827,14 @@ static bool ParseSpecialName(State *state) { // ::= v <v-offset> _ static bool ParseCallOffset(State *state) { State copy = *state; - if (ParseChar(state, 'h') && - ParseNVOffset(state) && ParseChar(state, '_')) { + if (ParseOneCharToken(state, 'h') && + ParseNVOffset(state) && ParseOneCharToken(state, '_')) { return true; } *state = copy; - if (ParseChar(state, 'v') && - ParseVOffset(state) && ParseChar(state, '_')) { + if (ParseOneCharToken(state, 'v') && + ParseVOffset(state) && ParseOneCharToken(state, '_')) { return true; } *state = copy; @@ -818,14 +844,14 @@ static bool ParseCallOffset(State *state) { // <nv-offset> ::= <(offset) number> static bool ParseNVOffset(State *state) { - return ParseNumber(state); + return ParseNumber(state, NULL); } // <v-offset> ::= <(offset) number> _ <(virtual offset) number> static bool ParseVOffset(State *state) { State copy = *state; - if (ParseNumber(state) && ParseChar(state, '_') && - ParseNumber(state)) { + if (ParseNumber(state, NULL) && ParseOneCharToken(state, '_') && + ParseNumber(state, NULL)) { return true; } *state = copy; @@ -836,7 +862,7 @@ static bool ParseVOffset(State *state) { // ::= D0 | D1 | D2 static bool ParseCtorDtorName(State *state) { State copy = *state; - if (ParseChar(state, 'C') && + if (ParseOneCharToken(state, 'C') && ParseCharClass(state, "123")) { const char * const prev_name = state->prev_name; const int prev_name_length = state->prev_name_length; @@ -845,7 +871,7 @@ static bool ParseCtorDtorName(State *state) { } *state = copy; - if (ParseChar(state, 'D') && + if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "012")) { const char * const prev_name = state->prev_name; const int prev_name_length = state->prev_name_length; @@ -858,11 +884,12 @@ static bool ParseCtorDtorName(State *state) { } // <type> ::= <CV-qualifiers> <type> -// ::= P <type> -// ::= R <type> -// ::= C <type> -// ::= G <type> -// ::= U <source-name> <type> +// ::= P <type> # pointer-to +// ::= R <type> # reference-to +// ::= O <type> # rvalue reference-to (C++0x) +// ::= C <type> # complex pair (C 2000) +// ::= G <type> # imaginary (C 2000) +// ::= U <source-name> <type> # vendor extended type qualifier // ::= <builtin-type> // ::= <function-type> // ::= <class-enum-type> @@ -871,6 +898,11 @@ static bool ParseCtorDtorName(State *state) { // ::= <template-template-param> <template-args> // ::= <template-param> // ::= <substitution> +// ::= Dp <type> # pack expansion of (C++0x) +// ::= Dt <expression> E # decltype of an id-expression or class +// # member access (C++0x) +// ::= DT <expression> E # decltype of an expression (C++0x) +// static bool ParseType(State *state) { // We should check CV-qualifers, and PRGC things first. State copy = *state; @@ -879,12 +911,23 @@ static bool ParseType(State *state) { } *state = copy; - if (ParseCharClass(state, "PRCG") && ParseType(state)) { + if (ParseCharClass(state, "OPRCG") && ParseType(state)) { return true; } *state = copy; - if (ParseChar(state, 'U') && ParseSourceName(state) && + if (ParseTwoCharToken(state, "Dp") && ParseType(state)) { + return true; + } + *state = copy; + + if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") && + ParseExpression(state) && ParseOneCharToken(state, 'E')) { + return true; + } + *state = copy; + + if (ParseOneCharToken(state, 'U') && ParseSourceName(state) && ParseType(state)) { return true; } @@ -918,9 +961,9 @@ static bool ParseType(State *state) { // ParseType(). static bool ParseCVQualifiers(State *state) { int num_cv_qualifiers = 0; - num_cv_qualifiers += ParseChar(state, 'r'); - num_cv_qualifiers += ParseChar(state, 'V'); - num_cv_qualifiers += ParseChar(state, 'K'); + num_cv_qualifiers += ParseOneCharToken(state, 'r'); + num_cv_qualifiers += ParseOneCharToken(state, 'V'); + num_cv_qualifiers += ParseOneCharToken(state, 'K'); return num_cv_qualifiers > 0; } @@ -937,7 +980,7 @@ static bool ParseBuiltinType(State *state) { } State copy = *state; - if (ParseChar(state, 'u') && ParseSourceName(state)) { + if (ParseOneCharToken(state, 'u') && ParseSourceName(state)) { return true; } *state = copy; @@ -947,8 +990,9 @@ static bool ParseBuiltinType(State *state) { // <function-type> ::= F [Y] <bare-function-type> E static bool ParseFunctionType(State *state) { State copy = *state; - if (ParseChar(state, 'F') && Optional(ParseChar(state, 'Y')) && - ParseBareFunctionType(state) && ParseChar(state, 'E')) { + if (ParseOneCharToken(state, 'F') && + Optional(ParseOneCharToken(state, 'Y')) && + ParseBareFunctionType(state) && ParseOneCharToken(state, 'E')) { return true; } *state = copy; @@ -977,14 +1021,14 @@ static bool ParseClassEnumType(State *state) { // ::= A [<(dimension) expression>] _ <(element) type> static bool ParseArrayType(State *state) { State copy = *state; - if (ParseChar(state, 'A') && ParseNumber(state) && - ParseChar(state, '_') && ParseType(state)) { + if (ParseOneCharToken(state, 'A') && ParseNumber(state, NULL) && + ParseOneCharToken(state, '_') && ParseType(state)) { return true; } *state = copy; - if (ParseChar(state, 'A') && Optional(ParseExpression(state)) && - ParseChar(state, '_') && ParseType(state)) { + if (ParseOneCharToken(state, 'A') && Optional(ParseExpression(state)) && + ParseOneCharToken(state, '_') && ParseType(state)) { return true; } *state = copy; @@ -994,7 +1038,7 @@ static bool ParseArrayType(State *state) { // <pointer-to-member-type> ::= M <(class) type> <(member) type> static bool ParsePointerToMemberType(State *state) { State copy = *state; - if (ParseChar(state, 'M') && ParseType(state) && + if (ParseOneCharToken(state, 'M') && ParseType(state) && ParseType(state)) { return true; } @@ -1005,14 +1049,14 @@ static bool ParsePointerToMemberType(State *state) { // <template-param> ::= T_ // ::= T <parameter-2 non-negative number> _ static bool ParseTemplateParam(State *state) { - if (ParseTwoChar(state, "T_")) { + if (ParseTwoCharToken(state, "T_")) { MaybeAppend(state, "?"); // We don't support template substitutions. return true; } State copy = *state; - if (ParseChar(state, 'T') && ParseNumber(state) && - ParseChar(state, '_')) { + if (ParseOneCharToken(state, 'T') && ParseNumber(state, NULL) && + ParseOneCharToken(state, '_')) { MaybeAppend(state, "?"); // We don't support template substitutions. return true; } @@ -1032,9 +1076,9 @@ static bool ParseTemplateTemplateParam(State *state) { static bool ParseTemplateArgs(State *state) { State copy = *state; DisableAppend(state); - if (ParseChar(state, 'I') && + if (ParseOneCharToken(state, 'I') && OneOrMore(ParseTemplateArg, state) && - ParseChar(state, 'E')) { + ParseOneCharToken(state, 'E')) { RestoreAppend(state, copy.append); MaybeAppend(state, "<>"); return true; @@ -1045,16 +1089,25 @@ static bool ParseTemplateArgs(State *state) { // <template-arg> ::= <type> // ::= <expr-primary> +// ::= I <template-arg>* E # argument pack // ::= X <expression> E static bool ParseTemplateArg(State *state) { + State copy = *state; + if (ParseOneCharToken(state, 'I') && + ZeroOrMore(ParseTemplateArg, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + *state = copy; + if (ParseType(state) || ParseExprPrimary(state)) { return true; } + *state = copy; - State copy = *state; - if (ParseChar(state, 'X') && ParseExpression(state) && - ParseChar(state, 'E')) { + if (ParseOneCharToken(state, 'X') && ParseExpression(state) && + ParseOneCharToken(state, 'E')) { return true; } *state = copy; @@ -1097,19 +1150,19 @@ static bool ParseExpression(State *state) { } *state = copy; - if (ParseTwoChar(state, "st") && ParseType(state)) { + if (ParseTwoCharToken(state, "st") && ParseType(state)) { return true; } *state = copy; - if (ParseTwoChar(state, "sr") && ParseType(state) && + if (ParseTwoCharToken(state, "sr") && ParseType(state) && ParseUnqualifiedName(state) && ParseTemplateArgs(state)) { return true; } *state = copy; - if (ParseTwoChar(state, "sr") && ParseType(state) && + if (ParseTwoCharToken(state, "sr") && ParseType(state) && ParseUnqualifiedName(state)) { return true; } @@ -1124,28 +1177,28 @@ static bool ParseExpression(State *state) { // ::= LZ <encoding> E static bool ParseExprPrimary(State *state) { State copy = *state; - if (ParseChar(state, 'L') && ParseType(state) && - ParseNumber(state) && - ParseChar(state, 'E')) { + if (ParseOneCharToken(state, 'L') && ParseType(state) && + ParseNumber(state, NULL) && + ParseOneCharToken(state, 'E')) { return true; } *state = copy; - if (ParseChar(state, 'L') && ParseType(state) && + if (ParseOneCharToken(state, 'L') && ParseType(state) && ParseFloatNumber(state) && - ParseChar(state, 'E')) { + ParseOneCharToken(state, 'E')) { return true; } *state = copy; - if (ParseChar(state, 'L') && ParseMangledName(state) && - ParseChar(state, 'E')) { + if (ParseOneCharToken(state, 'L') && ParseMangledName(state) && + ParseOneCharToken(state, 'E')) { return true; } *state = copy; - if (ParseTwoChar(state, "LZ") && ParseEncoding(state) && - ParseChar(state, 'E')) { + if (ParseTwoCharToken(state, "LZ") && ParseEncoding(state) && + ParseOneCharToken(state, 'E')) { return true; } *state = copy; @@ -1158,15 +1211,15 @@ static bool ParseExprPrimary(State *state) { // := Z <(function) encoding> E s [<discriminator>] static bool ParseLocalName(State *state) { State copy = *state; - if (ParseChar(state, 'Z') && ParseEncoding(state) && - ParseChar(state, 'E') && MaybeAppend(state, "::") && + if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && + ParseOneCharToken(state, 'E') && MaybeAppend(state, "::") && ParseName(state) && Optional(ParseDiscriminator(state))) { return true; } *state = copy; - if (ParseChar(state, 'Z') && ParseEncoding(state) && - ParseTwoChar(state, "Es") && Optional(ParseDiscriminator(state))) { + if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && + ParseTwoCharToken(state, "Es") && Optional(ParseDiscriminator(state))) { return true; } *state = copy; @@ -1176,7 +1229,7 @@ static bool ParseLocalName(State *state) { // <discriminator> := _ <(non-negative) number> static bool ParseDiscriminator(State *state) { State copy = *state; - if (ParseChar(state, '_') && ParseNumber(state)) { + if (ParseOneCharToken(state, '_') && ParseNumber(state, NULL)) { return true; } *state = copy; @@ -1187,21 +1240,21 @@ static bool ParseDiscriminator(State *state) { // ::= S <seq-id> _ // ::= St, etc. static bool ParseSubstitution(State *state) { - if (ParseTwoChar(state, "S_")) { + if (ParseTwoCharToken(state, "S_")) { MaybeAppend(state, "?"); // We don't support substitutions. return true; } State copy = *state; - if (ParseChar(state, 'S') && ParseSeqId(state) && - ParseChar(state, '_')) { + if (ParseOneCharToken(state, 'S') && ParseSeqId(state) && + ParseOneCharToken(state, '_')) { MaybeAppend(state, "?"); // We don't support substitutions. return true; } *state = copy; // Expand abbreviations like "St" => "std". - if (ParseChar(state, 'S')) { + if (ParseOneCharToken(state, 'S')) { const AbbrevPair *p; for (p = kSubstitutionList; p->abbrev != NULL; ++p) { if (state->mangled_cur[0] == p->abbrev[1]) { @@ -1210,7 +1263,7 @@ static bool ParseSubstitution(State *state) { MaybeAppend(state, "::"); MaybeAppend(state, p->real_name); } - state->mangled_cur += 1; + ++state->mangled_cur; return true; } } @@ -1219,13 +1272,33 @@ static bool ParseSubstitution(State *state) { return false; } +// Parse <mangled-name>, optionally followed by either a function-clone suffix +// or version suffix. Returns true only if all of "mangled_cur" was consumed. +static bool ParseTopLevelMangledName(State *state) { + if (ParseMangledName(state)) { + if (state->mangled_cur[0] != '\0') { + // Drop trailing function clone suffix, if any. + if (IsFunctionCloneSuffix(state->mangled_cur)) { + return true; + } + // Append trailing version suffix if any. + // ex. _Z3foo@@GLIBCXX_3.4 + if (state->mangled_cur[0] == '@') { + MaybeAppend(state, state->mangled_cur); + return true; + } + return false; // Unconsumed suffix. + } + return true; + } + return false; +} + // The demangler entry point. bool Demangle(const char *mangled, char *out, int out_size) { State state; InitState(&state, mangled, out, out_size); - return (ParseMangledName(&state) && - state.overflowed == false && - RemainingLength(&state) == 0); + return ParseTopLevelMangledName(&state) && !state.overflowed; } _END_GOOGLE_NAMESPACE_ diff --git a/base/third_party/symbolize/symbolize.cc b/base/third_party/symbolize/symbolize.cc index 85976bb..b25f747 100644 --- a/base/third_party/symbolize/symbolize.cc +++ b/base/third_party/symbolize/symbolize.cc @@ -45,8 +45,13 @@ // some functions which are not guaranteed to be so, such as memchr() // and memmove(). We assume they are async-signal-safe. // +// Additional header can be specified by the GLOG_BUILD_CONFIG_INCLUDE +// macro to add platform specific defines (e.g. OS_OPENBSD). + +#ifdef GLOG_BUILD_CONFIG_INCLUDE +#include GLOG_BUILD_CONFIG_INCLUDE +#endif // GLOG_BUILD_CONFIG_INCLUDE -#include "build/build_config.h" #include "utilities.h" #if defined(HAVE_SYMBOLIZE) @@ -75,6 +80,13 @@ void InstallSymbolizeCallback(SymbolizeCallback callback) { g_symbolize_callback = callback; } +static SymbolizeOpenObjectFileCallback g_symbolize_open_object_file_callback = + NULL; +void InstallSymbolizeOpenObjectFileCallback( + SymbolizeOpenObjectFileCallback callback) { + g_symbolize_open_object_file_callback = callback; +} + // This function wraps the Demangle function to provide an interface // where the input symbol is demangled in-place. // To keep stack consumption low, we would like this function to not @@ -83,8 +95,8 @@ static ATTRIBUTE_NOINLINE void DemangleInplace(char *out, int out_size) { char demangled[256]; // Big enough for sane demangled symbols. if (Demangle(out, demangled, sizeof(demangled))) { // Demangling succeeded. Copy to out if the space allows. - int len = strlen(demangled); - if (len + 1 <= out_size) { // +1 for '\0'. + size_t len = strlen(demangled); + if (len + 1 <= (size_t)out_size) { // +1 for '\0'. SAFE_ASSERT(len < sizeof(demangled)); memmove(out, demangled, len + 1); } @@ -483,13 +495,20 @@ static char *GetHex(const char *start, const char *end, uint64_t *hex) { return const_cast<char *>(p); } -// Search for the object file (from /proc/self/maps) that contains -// the specified pc. If found, open this file and return the file handle, -// and also set start_address to the start address of where this object -// file is mapped to in memory. Otherwise, return -1. +// Searches for the object file (from /proc/self/maps) that contains +// the specified pc. If found, sets |start_address| to the start address +// of where this object file is mapped in memory, sets the module base +// address into |base_address|, copies the object file name into +// |out_file_name|, and attempts to open the object file. If the object +// file is opened successfully, returns the file descriptor. Otherwise, +// returns -1. |out_file_name_size| is the size of the file name buffer +// (including the null-terminator). static ATTRIBUTE_NOINLINE int OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, - uint64_t &start_address) { + uint64_t &start_address, + uint64_t &base_address, + char *out_file_name, + int out_file_name_size) { int object_fd; // Open /proc/self/maps. @@ -503,8 +522,10 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, // Iterate over maps and look for the map containing the pc. Then // look into the symbol tables inside. char buf[1024]; // Big enough for line of sane /proc/self/maps + int num_maps = 0; LineReader reader(wrapped_maps_fd.get(), buf, sizeof(buf)); while (true) { + num_maps++; const char *cursor; const char *eol; if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line. @@ -554,14 +575,35 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, } ++cursor; // Skip ' '. - // Skip to file name. "cursor" now points to file offset. We need to - // skip at least three spaces for file offset, dev, and inode. + // Read file offset. + uint64_t file_offset; + cursor = GetHex(cursor, eol, &file_offset); + if (cursor == eol || *cursor != ' ') { + return -1; // Malformed line. + } + ++cursor; // Skip ' '. + + // Don't subtract 'start_address' from the first entry: + // * If a binary is compiled w/o -pie, then the first entry in + // process maps is likely the binary itself (all dynamic libs + // are mapped higher in address space). For such a binary, + // instruction offset in binary coincides with the actual + // instruction address in virtual memory (as code section + // is mapped to a fixed memory range). + // * If a binary is compiled with -pie, all the modules are + // mapped high at address space (in particular, higher than + // shadow memory of the tool), so the module can't be the + // first entry. + base_address = ((num_maps == 1) ? 0U : start_address) - file_offset; + + // Skip to file name. "cursor" now points to dev. We need to + // skip at least two spaces for dev and inode. int num_spaces = 0; while (cursor < eol) { if (*cursor == ' ') { ++num_spaces; - } else if (num_spaces >= 3) { - // The first non-space character after skipping three spaces + } else if (num_spaces >= 2) { + // The first non-space character after skipping two spaces // is the beginning of the file name. break; } @@ -574,12 +616,105 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, // Finally, "cursor" now points to file name of our interest. NO_INTR(object_fd = open(cursor, O_RDONLY)); if (object_fd < 0) { + // Failed to open object file. Copy the object file name to + // |out_file_name|. + strncpy(out_file_name, cursor, out_file_name_size); + // Making sure |out_file_name| is always null-terminated. + out_file_name[out_file_name_size - 1] = '\0'; return -1; } return object_fd; } } +// POSIX doesn't define any async-signal safe function for converting +// an integer to ASCII. We'll have to define our own version. +// itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the +// conversion was successful or NULL otherwise. It never writes more than "sz" +// bytes. Output will be truncated as needed, and a NUL character is always +// appended. +// NOTE: code from sandbox/linux/seccomp-bpf/demo.cc. +char *itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding) { + // Make sure we can write at least one NUL byte. + size_t n = 1; + if (n > sz) + return NULL; + + if (base < 2 || base > 16) { + buf[0] = '\000'; + return NULL; + } + + char *start = buf; + + uintptr_t j = i; + + // Handle negative numbers (only for base 10). + if (i < 0 && base == 10) { + j = -i; + + // Make sure we can write the '-' character. + if (++n > sz) { + buf[0] = '\000'; + return NULL; + } + *start++ = '-'; + } + + // Loop until we have converted the entire number. Output at least one + // character (i.e. '0'). + char *ptr = start; + do { + // Make sure there is still enough space left in our output buffer. + if (++n > sz) { + buf[0] = '\000'; + return NULL; + } + + // Output the next digit. + *ptr++ = "0123456789abcdef"[j % base]; + j /= base; + + if (padding > 0) + padding--; + } while (j > 0 || padding > 0); + + // Terminate the output with a NUL character. + *ptr = '\000'; + + // Conversion to ASCII actually resulted in the digits being in reverse + // order. We can't easily generate them in forward order, as we can't tell + // the number of characters needed until we are done converting. + // So, now, we reverse the string (except for the possible "-" sign). + while (--ptr > start) { + char ch = *ptr; + *ptr = *start; + *start++ = ch; + } + return buf; +} + +// Safely appends string |source| to string |dest|. Never writes past the +// buffer size |dest_size| and guarantees that |dest| is null-terminated. +void SafeAppendString(const char* source, char* dest, int dest_size) { + int dest_string_length = strlen(dest); + SAFE_ASSERT(dest_string_length < dest_size); + dest += dest_string_length; + dest_size -= dest_string_length; + strncpy(dest, source, dest_size); + // Making sure |dest| is always null-terminated. + dest[dest_size - 1] = '\0'; +} + +// Converts a 64-bit value into a hex string, and safely appends it to |dest|. +// Never writes past the buffer size |dest_size| and guarantees that |dest| is +// null-terminated. +void SafeAppendHexNumber(uint64_t value, char* dest, int dest_size) { + // 64-bit numbers in hex can have up to 16 digits. + char buf[17] = {'\0'}; + SafeAppendString(itoa_r(value, buf, sizeof(buf), 16, 0), dest, dest_size); +} + // The implementation of our symbolization routine. If it // successfully finds the symbol containing "pc" and obtains the // symbol name, returns true and write the symbol name to "out". @@ -592,10 +727,40 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, int out_size) { uint64_t pc0 = reinterpret_cast<uintptr_t>(pc); uint64_t start_address = 0; + uint64_t base_address = 0; + int object_fd = -1; - int object_fd = OpenObjectFileContainingPcAndGetStartAddress(pc0, - start_address); - if (object_fd == -1) { + if (out_size < 1) { + return false; + } + out[0] = '\0'; + SafeAppendString("(", out, out_size); + + if (g_symbolize_open_object_file_callback) { + object_fd = g_symbolize_open_object_file_callback(pc0, start_address, + base_address, out + 1, + out_size - 1); + } else { + object_fd = OpenObjectFileContainingPcAndGetStartAddress(pc0, start_address, + base_address, + out + 1, + out_size - 1); + } + + // Check whether a file name was returned. + if (object_fd < 0) { + if (out[1]) { + // The object file containing PC was determined successfully however the + // object file was not opened successfully. This is still considered + // success because the object file name and offset are known and tools + // like asan_symbolize.py can be used for the symbolization. + out[out_size - 1] = '\0'; // Making sure |out| is always null-terminated. + SafeAppendString("+0x", out, out_size); + SafeAppendHexNumber(pc0 - base_address, out, out_size); + SafeAppendString(")", out, out_size); + return true; + } + // Failed to determine the object file containing PC. Bail out. return false; } FileDescriptor wrapped_object_fd(object_fd); @@ -639,7 +804,7 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, int out_size) { Dl_info info; if (dladdr(pc, &info)) { - if (strlen(info.dli_sname) < out_size) { + if ((int)strlen(info.dli_sname) < out_size) { strcpy(out, info.dli_sname); // Symbolization succeeded. Now we try to demangle the symbol. DemangleInplace(out, out_size); diff --git a/base/third_party/symbolize/symbolize.h b/base/third_party/symbolize/symbolize.h index 441e543..f617184 100644 --- a/base/third_party/symbolize/symbolize.h +++ b/base/third_party/symbolize/symbolize.h @@ -105,6 +105,10 @@ _END_GOOGLE_NAMESPACE_ _START_GOOGLE_NAMESPACE_ +// Restrictions on the callbacks that follow: +// - The callbacks must not use heaps but only use stacks. +// - The callbacks must be async-signal-safe. + // Installs a callback function, which will be called right before a symbol name // is printed. The callback is intended to be used for showing a file name and a // line number preceding a symbol name. @@ -116,6 +120,24 @@ typedef int (*SymbolizeCallback)(int fd, void *pc, char *out, size_t out_size, uint64 relocation); void InstallSymbolizeCallback(SymbolizeCallback callback); +// Installs a callback function, which will be called instead of +// OpenObjectFileContainingPcAndGetStartAddress. The callback is expected +// to searches for the object file (from /proc/self/maps) that contains +// the specified pc. If found, sets |start_address| to the start address +// of where this object file is mapped in memory, sets the module base +// address into |base_address|, copies the object file name into +// |out_file_name|, and attempts to open the object file. If the object +// file is opened successfully, returns the file descriptor. Otherwise, +// returns -1. |out_file_name_size| is the size of the file name buffer +// (including the null-terminator). +typedef int (*SymbolizeOpenObjectFileCallback)(uint64_t pc, + uint64_t &start_address, + uint64_t &base_address, + char *out_file_name, + int out_file_name_size); +void InstallSymbolizeOpenObjectFileCallback( + SymbolizeOpenObjectFileCallback callback); + _END_GOOGLE_NAMESPACE_ #endif |