|  | // Copyright (c) 2010 Google Inc. All Rights Reserved. | 
|  | // | 
|  | // Redistribution and use in source and binary forms, with or without | 
|  | // modification, are permitted provided that the following conditions are | 
|  | // met: | 
|  | // | 
|  | //     * Redistributions of source code must retain the above copyright | 
|  | // notice, this list of conditions and the following disclaimer. | 
|  | //     * Redistributions in binary form must reproduce the above | 
|  | // copyright notice, this list of conditions and the following disclaimer | 
|  | // in the documentation and/or other materials provided with the | 
|  | // distribution. | 
|  | //     * Neither the name of Google Inc. nor the names of its | 
|  | // contributors may be used to endorse or promote products derived from | 
|  | // this software without specific prior written permission. | 
|  | // | 
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  |  | 
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | 
|  |  | 
|  | // This file implements the google_breakpad::StabsReader class. | 
|  | // See stabs_reader.h. | 
|  |  | 
|  | #include "common/stabs_reader.h" | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <stab.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include <string> | 
|  |  | 
|  | #include "common/using_std_string.h" | 
|  |  | 
|  | using std::vector; | 
|  |  | 
|  | namespace google_breakpad { | 
|  |  | 
|  | StabsReader::EntryIterator::EntryIterator(const ByteBuffer *buffer, | 
|  | bool big_endian, size_t value_size) | 
|  | : value_size_(value_size), cursor_(buffer, big_endian) { | 
|  | // Actually, we could handle weird sizes just fine, but they're | 
|  | // probably mistakes --- expressed in bits, say. | 
|  | assert(value_size == 4 || value_size == 8); | 
|  | entry_.index = 0; | 
|  | Fetch(); | 
|  | } | 
|  |  | 
|  | void StabsReader::EntryIterator::Fetch() { | 
|  | cursor_ | 
|  | .Read(4, false, &entry_.name_offset) | 
|  | .Read(1, false, &entry_.type) | 
|  | .Read(1, false, &entry_.other) | 
|  | .Read(2, false, &entry_.descriptor) | 
|  | .Read(value_size_, false, &entry_.value); | 
|  | entry_.at_end = !cursor_; | 
|  | } | 
|  |  | 
|  | StabsReader::StabsReader(const uint8_t *stab,    size_t stab_size, | 
|  | const uint8_t *stabstr, size_t stabstr_size, | 
|  | bool big_endian, size_t value_size, bool unitized, | 
|  | StabsHandler *handler) | 
|  | : entries_(stab, stab_size), | 
|  | strings_(stabstr, stabstr_size), | 
|  | iterator_(&entries_, big_endian, value_size), | 
|  | unitized_(unitized), | 
|  | handler_(handler), | 
|  | string_offset_(0), | 
|  | next_cu_string_offset_(0), | 
|  | current_source_file_(NULL) { } | 
|  |  | 
|  | const char *StabsReader::SymbolString() { | 
|  | ptrdiff_t offset = string_offset_ + iterator_->name_offset; | 
|  | if (offset < 0 || (size_t) offset >= strings_.Size()) { | 
|  | handler_->Warning("symbol %d: name offset outside the string section\n", | 
|  | iterator_->index); | 
|  | // Return our null string, to keep our promise about all names being | 
|  | // taken from the string section. | 
|  | offset = 0; | 
|  | } | 
|  | return reinterpret_cast<const char *>(strings_.start + offset); | 
|  | } | 
|  |  | 
|  | bool StabsReader::Process() { | 
|  | while (!iterator_->at_end) { | 
|  | if (iterator_->type == N_SO) { | 
|  | if (! ProcessCompilationUnit()) | 
|  | return false; | 
|  | } else if (iterator_->type == N_UNDF && unitized_) { | 
|  | // In unitized STABS (including Linux STABS, and pretty much anything | 
|  | // else that puts STABS data in sections), at the head of each | 
|  | // compilation unit's entries there is an N_UNDF stab giving the | 
|  | // number of symbols in the compilation unit, and the number of bytes | 
|  | // that compilation unit's strings take up in the .stabstr section. | 
|  | // Each CU's strings are separate; the n_strx values are offsets | 
|  | // within the current CU's portion of the .stabstr section. | 
|  | // | 
|  | // As an optimization, the GNU linker combines all the | 
|  | // compilation units into one, with a single N_UNDF at the | 
|  | // beginning. However, other linkers, like Gold, do not perform | 
|  | // this optimization. | 
|  | string_offset_ = next_cu_string_offset_; | 
|  | next_cu_string_offset_ = iterator_->value; | 
|  | ++iterator_; | 
|  | } | 
|  | #if defined(HAVE_MACH_O_NLIST_H) | 
|  | // Export symbols in Mach-O binaries look like this. | 
|  | // This is necessary in order to be able to dump symbols | 
|  | // from OS X system libraries. | 
|  | else if ((iterator_->type & N_STAB) == 0 && | 
|  | (iterator_->type & N_TYPE) == N_SECT) { | 
|  | ProcessExtern(); | 
|  | } | 
|  | #endif | 
|  | else { | 
|  | ++iterator_; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool StabsReader::ProcessCompilationUnit() { | 
|  | assert(!iterator_->at_end && iterator_->type == N_SO); | 
|  |  | 
|  | // There may be an N_SO entry whose name ends with a slash, | 
|  | // indicating the directory in which the compilation occurred. | 
|  | // The build directory defaults to NULL. | 
|  | const char *build_directory = NULL; | 
|  | { | 
|  | const char *name = SymbolString(); | 
|  | if (name[0] && name[strlen(name) - 1] == '/') { | 
|  | build_directory = name; | 
|  | ++iterator_; | 
|  | } | 
|  | } | 
|  |  | 
|  | // We expect to see an N_SO entry with a filename next, indicating | 
|  | // the start of the compilation unit. | 
|  | { | 
|  | if (iterator_->at_end || iterator_->type != N_SO) | 
|  | return true; | 
|  | const char *name = SymbolString(); | 
|  | if (name[0] == '\0') { | 
|  | // This seems to be a stray end-of-compilation-unit marker; | 
|  | // consume it, but don't report the end, since we didn't see a | 
|  | // beginning. | 
|  | ++iterator_; | 
|  | return true; | 
|  | } | 
|  | current_source_file_ = name; | 
|  | } | 
|  |  | 
|  | if (! handler_->StartCompilationUnit(current_source_file_, | 
|  | iterator_->value, | 
|  | build_directory)) | 
|  | return false; | 
|  |  | 
|  | ++iterator_; | 
|  |  | 
|  | // The STABS documentation says that some compilers may emit | 
|  | // additional N_SO entries with names immediately following the | 
|  | // first, and that they should be ignored.  However, the original | 
|  | // Breakpad STABS reader doesn't ignore them, so we won't either. | 
|  |  | 
|  | // Process the body of the compilation unit, up to the next N_SO. | 
|  | while (!iterator_->at_end && iterator_->type != N_SO) { | 
|  | if (iterator_->type == N_FUN) { | 
|  | if (! ProcessFunction()) | 
|  | return false; | 
|  | } else if (iterator_->type == N_SLINE) { | 
|  | // Mac OS X STABS place SLINE records before functions. | 
|  | Line line; | 
|  | // The value of an N_SLINE entry that appears outside a function is | 
|  | // the absolute address of the line. | 
|  | line.address = iterator_->value; | 
|  | line.filename = current_source_file_; | 
|  | // The n_desc of a N_SLINE entry is the line number.  It's a | 
|  | // signed 16-bit field; line numbers from 32768 to 65535 are | 
|  | // stored as n-65536. | 
|  | line.number = (uint16_t) iterator_->descriptor; | 
|  | queued_lines_.push_back(line); | 
|  | ++iterator_; | 
|  | } else if (iterator_->type == N_SOL) { | 
|  | current_source_file_ = SymbolString(); | 
|  | ++iterator_; | 
|  | } else { | 
|  | // Ignore anything else. | 
|  | ++iterator_; | 
|  | } | 
|  | } | 
|  |  | 
|  | // An N_SO with an empty name indicates the end of the compilation | 
|  | // unit.  Default to zero. | 
|  | uint64_t ending_address = 0; | 
|  | if (!iterator_->at_end) { | 
|  | assert(iterator_->type == N_SO); | 
|  | const char *name = SymbolString(); | 
|  | if (name[0] == '\0') { | 
|  | ending_address = iterator_->value; | 
|  | ++iterator_; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (! handler_->EndCompilationUnit(ending_address)) | 
|  | return false; | 
|  |  | 
|  | queued_lines_.clear(); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool StabsReader::ProcessFunction() { | 
|  | assert(!iterator_->at_end && iterator_->type == N_FUN); | 
|  |  | 
|  | uint64_t function_address = iterator_->value; | 
|  | // The STABS string for an N_FUN entry is the name of the function, | 
|  | // followed by a colon, followed by type information for the | 
|  | // function.  We want to pass the name alone to StartFunction. | 
|  | const char *stab_string = SymbolString(); | 
|  | const char *name_end = strchr(stab_string, ':'); | 
|  | if (! name_end) | 
|  | name_end = stab_string + strlen(stab_string); | 
|  | string name(stab_string, name_end - stab_string); | 
|  | if (! handler_->StartFunction(name, function_address)) | 
|  | return false; | 
|  | ++iterator_; | 
|  |  | 
|  | // If there were any SLINE records given before the function, report them now. | 
|  | for (vector<Line>::const_iterator it = queued_lines_.begin(); | 
|  | it != queued_lines_.end(); it++) { | 
|  | if (!handler_->Line(it->address, it->filename, it->number)) | 
|  | return false; | 
|  | } | 
|  | queued_lines_.clear(); | 
|  |  | 
|  | while (!iterator_->at_end) { | 
|  | if (iterator_->type == N_SO || iterator_->type == N_FUN) | 
|  | break; | 
|  | else if (iterator_->type == N_SLINE) { | 
|  | // The value of an N_SLINE entry is the offset of the line from | 
|  | // the function's start address. | 
|  | uint64_t line_address = function_address + iterator_->value; | 
|  | // The n_desc of a N_SLINE entry is the line number.  It's a | 
|  | // signed 16-bit field; line numbers from 32768 to 65535 are | 
|  | // stored as n-65536. | 
|  | uint16_t line_number = iterator_->descriptor; | 
|  | if (! handler_->Line(line_address, current_source_file_, line_number)) | 
|  | return false; | 
|  | ++iterator_; | 
|  | } else if (iterator_->type == N_SOL) { | 
|  | current_source_file_ = SymbolString(); | 
|  | ++iterator_; | 
|  | } else | 
|  | // Ignore anything else. | 
|  | ++iterator_; | 
|  | } | 
|  |  | 
|  | // We've reached the end of the function. See if we can figure out its | 
|  | // ending address. | 
|  | uint64_t ending_address = 0; | 
|  | if (!iterator_->at_end) { | 
|  | assert(iterator_->type == N_SO || iterator_->type == N_FUN); | 
|  | if (iterator_->type == N_FUN) { | 
|  | const char *symbol_name = SymbolString(); | 
|  | if (symbol_name[0] == '\0') { | 
|  | // An N_FUN entry with no name is a terminator for this function; | 
|  | // its value is the function's size. | 
|  | ending_address = function_address + iterator_->value; | 
|  | ++iterator_; | 
|  | } else { | 
|  | // An N_FUN entry with a name is the next function, and we can take | 
|  | // its value as our ending address. Don't advance the iterator, as | 
|  | // we'll use this symbol to start the next function as well. | 
|  | ending_address = iterator_->value; | 
|  | } | 
|  | } else { | 
|  | // An N_SO entry could be an end-of-compilation-unit marker, or the | 
|  | // start of the next compilation unit, but in either case, its value | 
|  | // is our ending address. We don't advance the iterator; | 
|  | // ProcessCompilationUnit will decide what to do with this symbol. | 
|  | ending_address = iterator_->value; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (! handler_->EndFunction(ending_address)) | 
|  | return false; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool StabsReader::ProcessExtern() { | 
|  | #if defined(HAVE_MACH_O_NLIST_H) | 
|  | assert(!iterator_->at_end && | 
|  | (iterator_->type & N_STAB) == 0 && | 
|  | (iterator_->type & N_TYPE) == N_SECT); | 
|  | #endif | 
|  |  | 
|  | // TODO(mark): only do symbols in the text section? | 
|  | if (!handler_->Extern(SymbolString(), iterator_->value)) | 
|  | return false; | 
|  |  | 
|  | ++iterator_; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | } // namespace google_breakpad |