diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/common/linux/stabs_reader.cc | 195 | ||||
-rw-r--r-- | src/common/linux/stabs_reader.h | 188 |
2 files changed, 383 insertions, 0 deletions
diff --git a/src/common/linux/stabs_reader.cc b/src/common/linux/stabs_reader.cc new file mode 100644 index 00000000..57073361 --- /dev/null +++ b/src/common/linux/stabs_reader.cc @@ -0,0 +1,195 @@ +// Copyright 2009 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. + +// This file implements the google_breakpad::StabsReader class. + +#include <a.out.h> +#include <stab.h> +#include <cstring> +#include <cassert> + +#include "common/linux/stabs_reader.h" + +namespace google_breakpad { + +StabsReader::StabsReader(const uint8_t *stab, size_t stab_size, + const uint8_t *stabstr, size_t stabstr_size, + StabsHandler *handler) : + stabstr_(stabstr), + stabstr_size_(stabstr_size), + handler_(handler), + symbol_(NULL), + current_source_file_(NULL) { + symbols_ = reinterpret_cast<const struct nlist *>(stab); + symbols_end_ = symbols_ + (stab_size / sizeof (*symbols_)); +} + +const char *StabsReader::SymbolString() { + ptrdiff_t offset = symbol_->n_un.n_strx; + if (offset < 0 || (size_t) offset >= stabstr_size_) { + handler_->Warning("symbol %d: name offset outside the string section", + symbol_ - symbols_); + // Return our null string, to keep our promise about all names being + // taken from the string section. + offset = 0; + } + return reinterpret_cast<const char *>(stabstr_ + offset); +} + +bool StabsReader::Process() { + symbol_ = symbols_; + while (symbol_ < symbols_end_) { + if (symbol_->n_type == N_SO) { + if (! ProcessCompilationUnit()) + return false; + } else + symbol_++; + } + return true; +} + +bool StabsReader::ProcessCompilationUnit() { + assert(symbol_ < symbols_end_ && symbol_->n_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; + symbol_++; + } + } + + // We expect to see an N_SO entry with a filename next, indicating + // the start of the compilation unit. + { + if (symbol_ >= symbols_end_ || symbol_->n_type != N_SO) + return true; + const char *name = SymbolString(); + if (name[0] == '\0') + return true; + current_source_file_ = name; + } + + if (! handler_->StartCompilationUnit(current_source_file_, + SymbolValue(), + build_directory)) + return false; + + symbol_++; + + // The STABS documentation says that some compilers may emit + // additional N_SO units 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 (symbol_ < symbols_end_ && symbol_->n_type != N_SO) { + if (symbol_->n_type == N_FUN) { + if (! ProcessFunction()) + return false; + } else + // Ignore anything else. + symbol_++; + } + + // An N_SO with an empty name indicates the end of the compilation + // unit. Default to zero. + uint64_t ending_address = 0; + if (symbol_ < symbols_end_) { + assert(symbol_->n_type == N_SO); + const char *name = SymbolString(); + if (name[0] == '\0') { + ending_address = SymbolValue(); + symbol_++; + } + } + + if (! handler_->EndCompilationUnit(ending_address)) + return false; + + return true; +} + +bool StabsReader::ProcessFunction() { + assert(symbol_ < symbols_end_ && symbol_->n_type == N_FUN); + + uint64_t function_address = SymbolValue(); + // 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); + std::string name(stab_string, name_end - stab_string); + if (! handler_->StartFunction(name, function_address)) + return false; + symbol_++; + + while (symbol_ < symbols_end_) { + if (symbol_->n_type == N_SO || symbol_->n_type == N_FUN) + break; + else if (symbol_->n_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 + SymbolValue(); + // 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 = symbol_->n_desc; + if (! handler_->Line(line_address, current_source_file_, line_number)) + return false; + symbol_++; + } else if (symbol_->n_type == N_SOL) { + current_source_file_ = SymbolString(); + symbol_++; + } else + // Ignore anything else. + symbol_++; + } + + // If there is a subsequent N_SO or N_FUN entry, its address is our + // end address. + uint64_t ending_address = 0; + if (symbol_ < symbols_end_) { + assert(symbol_->n_type == N_SO || symbol_->n_type == N_FUN); + ending_address = SymbolValue(); + // Note: we do not increment symbol_ here, since we haven't consumed it. + } + + if (! handler_->EndFunction(ending_address)) + return false; + + return true; +} + +} // namespace google_breakpad diff --git a/src/common/linux/stabs_reader.h b/src/common/linux/stabs_reader.h new file mode 100644 index 00000000..7ebc30b1 --- /dev/null +++ b/src/common/linux/stabs_reader.h @@ -0,0 +1,188 @@ +// Copyright 2009 Google Inc. All Rights Reserved. -*- mode: c++ -*- +// +// 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. + +// This file contains definitions related to the STABS reader and +// its handler interfaces. +// A description of the STABS debugging format can be found at +// http://sourceware.org/gdb/current/onlinedocs/stabs_toc.html +// The comments here assume you understand the format. +// +// This reader assumes that the system's <a.out.h> and <stab.h> +// headers accurately describe the layout of the STABS data; this code +// is not cross-platform safe. + +#ifndef COMMON_LINUX_STABS_READER_H__ +#define COMMON_LINUX_STABS_READER_H__ + +#include <stdint.h> +#include <cstddef> +#include <a.out.h> + +#include <string> + +namespace google_breakpad { + +class StabsHandler; + +class StabsReader { + public: + // Create a reader for the STABS debug information whose .stab + // section is the STAB_SIZE bytes at STAB, and whose .stabstr + // section is the STABSTR_SIZE bytes at STABSTR. The reader will + // call the methods of HANDLER to report the information it finds, + // when the reader's 'process' method is called. + // + // Note that, in ELF, the .stabstr section should be found using the + // 'sh_link' field of the .stab section header, not by name. + StabsReader(const uint8_t *stab, size_t stab_size, + const uint8_t *stabstr, size_t stabstr_size, + StabsHandler *handler); + + // Process the STAB data, calling the handler's methods to report + // what we find. While the handler functions return true, continue + // to process until we reach the end of the section. If we + // processed the entire section and all handlers returned true, + // return true. If any handler returned false, return false. + bool Process(); + + private: + // Return the name of the current symbol. + const char *SymbolString(); + + // Return the value of the current symbol. + const uint64_t SymbolValue() { + return symbol_->n_value; + } + + // Process a compilation unit starting at symbol_. Return true + // to continue processing, or false to abort. + bool ProcessCompilationUnit(); + + // Process a function in current_source_file_ starting at symbol_. + // Return true to continue processing, or false to abort. + bool ProcessFunction(); + + // The debugging information we're reading. + const struct nlist *symbols_, *symbols_end_; + const uint8_t *stabstr_; + size_t stabstr_size_; + + StabsHandler *handler_; + + // The current symbol we're processing. + const struct nlist *symbol_; + + // The current source file name. + const char *current_source_file_; +}; + +// Consumer-provided callback structure for the STABS reader. +// Clients of the STABS reader provide an instance of this structure. +// The reader then invokes the methods of that instance to report the +// information it finds. +// +// The default definitions of the methods do nothing. +class StabsHandler { + public: + StabsHandler() { } + virtual ~StabsHandler() { } + + // Some general notes about the handler callback functions: + + // Processing proceeds until the end of the .stabs section, or until + // one of these functions returns false. + + // The addresses given are as reported in the STABS info, without + // regard for whether the module may be loaded at different + // addresses at different times (a shared library, say). When + // processing STABS from an ELF shared library, the addresses given + // all assume the library is loaded at its nominal load address. + // They are *not* offsets from the nominal load address. If you + // want offsets, you must subtract off the library's nominal load + // address. + + // The arguments to these functions named FILENAME are all + // references to strings stored in the .stabstr section. Because + // both the Linux and Solaris linkers factor out duplicate strings + // from the .stabstr section, the consumer can assume that if two + // FILENAME values are different addresses, they represent different + // file names. + // + // Thus, it's safe to use (say) std::map<char *, ...>, which does + // address comparisons. Since all the pointers are into the array + // holding the .stabstr section's contents, comparing them produces + // predictable results. + + // Begin processing a compilation unit whose main source file is + // named FILENAME, and whose base address is ADDRESS. If + // BUILD_DIRECTORY is non-NULL, it is the name of the build + // directory in which the compilation occurred. + virtual bool StartCompilationUnit(const char *filename, uint64_t address, + const char *build_directory) { + return true; + } + + // Finish processing the compilation unit. If END_ADDRESS is + // non-zero, it is the ending address of the compilation unit. This + // information may not be available, in which case the consumer must + // infer it by other means. + virtual bool EndCompilationUnit(uint64_t address) { return true; } + + // Begin processing a function named NAME, whose starting address is + // ADDRESS. This function belongs to the compilation unit that was + // most recently started but not ended. + // + // Note that, unlike filenames, NAME is not a pointer into the + // .stabstr section; this is because the name as it appears in the + // STABS data is followed by type information. The value passed to + // StartFunction is the function name alone. + virtual bool StartFunction(const std::string &name, uint64_t address) { + return true; + } + + // Finishing processing the function. If END_ADDRESS is non-zero, + // it is the ending address for the function. This information may + // not be available, in which case the consumer must infer it by + // other means. + virtual bool EndFunction(uint64_t address) { return true; } + + // Report that the code at ADDRESS is attributable to line NUMBER of + // the source file named FILENAME. The caller must infer the ending + // address of the line. + virtual bool Line(uint64_t address, const char *filename, int number) { + return true; + } + + // Report a warning. FORMAT is a printf-like format string, + // specifying how to format the subsequent arguments. + virtual void Warning(const char *format, ...) { } +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_STABS_READER_H__ |