michael@0: // Copyright (c) 2011 Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: // Original author: Jim Blandy michael@0: michael@0: // module.cc: Implement google_breakpad::Module. See module.h. michael@0: michael@0: #include "common/module.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: namespace google_breakpad { michael@0: michael@0: using std::dec; michael@0: using std::endl; michael@0: using std::hex; michael@0: michael@0: michael@0: Module::Module(const string &name, const string &os, michael@0: const string &architecture, const string &id) : michael@0: name_(name), michael@0: os_(os), michael@0: architecture_(architecture), michael@0: id_(id), michael@0: load_address_(0) { } michael@0: michael@0: Module::~Module() { michael@0: for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); ++it) michael@0: delete it->second; michael@0: for (FunctionSet::iterator it = functions_.begin(); michael@0: it != functions_.end(); ++it) { michael@0: delete *it; michael@0: } michael@0: for (StackFrameEntrySet::iterator it = stack_frame_entries_.begin(); michael@0: it != stack_frame_entries_.end(); ++it) { michael@0: delete *it; michael@0: } michael@0: for (ExternSet::iterator it = externs_.begin(); it != externs_.end(); ++it) michael@0: delete *it; michael@0: } michael@0: michael@0: void Module::SetLoadAddress(Address address) { michael@0: load_address_ = address; michael@0: } michael@0: michael@0: void Module::AddFunction(Function *function) { michael@0: // FUNC lines must not hold an empty name, so catch the problem early if michael@0: // callers try to add one. michael@0: assert(!function->name.empty()); michael@0: std::pair ret = functions_.insert(function); michael@0: if (!ret.second) { michael@0: // Free the duplicate that was not inserted because this Module michael@0: // now owns it. michael@0: delete function; michael@0: } michael@0: } michael@0: michael@0: void Module::AddFunctions(vector::iterator begin, michael@0: vector::iterator end) { michael@0: for (vector::iterator it = begin; it != end; ++it) michael@0: AddFunction(*it); michael@0: } michael@0: michael@0: void Module::AddStackFrameEntry(StackFrameEntry* stack_frame_entry) { michael@0: std::pair ret = michael@0: stack_frame_entries_.insert(stack_frame_entry); michael@0: if (!ret.second) { michael@0: // Free the duplicate that was not inserted because this Module michael@0: // now owns it. michael@0: delete stack_frame_entry; michael@0: } michael@0: } michael@0: michael@0: void Module::AddExtern(Extern *ext) { michael@0: std::pair ret = externs_.insert(ext); michael@0: if (!ret.second) { michael@0: // Free the duplicate that was not inserted because this Module michael@0: // now owns it. michael@0: delete ext; michael@0: } michael@0: } michael@0: michael@0: void Module::GetFunctions(vector *vec, michael@0: vector::iterator i) { michael@0: vec->insert(i, functions_.begin(), functions_.end()); michael@0: } michael@0: michael@0: template michael@0: bool EntryContainsAddress(T entry, Module::Address address) { michael@0: return entry->address <= address && address < entry->address + entry->size; michael@0: } michael@0: michael@0: Module::Function* Module::FindFunctionByAddress(Address address) { michael@0: Function search; michael@0: search.address = address; michael@0: // Ensure that name always sorts higher than the function name, michael@0: // so that upper_bound always returns the function just after michael@0: // the function containing this address. michael@0: search.name = "\xFF"; michael@0: FunctionSet::iterator it = functions_.upper_bound(&search); michael@0: if (it == functions_.begin()) michael@0: return NULL; michael@0: michael@0: it--; michael@0: michael@0: if (EntryContainsAddress(*it, address)) michael@0: return *it; michael@0: michael@0: return NULL; michael@0: } michael@0: michael@0: void Module::GetExterns(vector *vec, michael@0: vector::iterator i) { michael@0: vec->insert(i, externs_.begin(), externs_.end()); michael@0: } michael@0: michael@0: Module::Extern* Module::FindExternByAddress(Address address) { michael@0: Extern search; michael@0: search.address = address; michael@0: ExternSet::iterator it = externs_.upper_bound(&search); michael@0: michael@0: if (it == externs_.begin()) michael@0: return NULL; michael@0: michael@0: it--; michael@0: if ((*it)->address > address) michael@0: return NULL; michael@0: michael@0: return *it; michael@0: } michael@0: michael@0: Module::File *Module::FindFile(const string &name) { michael@0: // A tricky bit here. The key of each map entry needs to be a michael@0: // pointer to the entry's File's name string. This means that we michael@0: // can't do the initial lookup with any operation that would create michael@0: // an empty entry for us if the name isn't found (like, say, michael@0: // operator[] or insert do), because such a created entry's key will michael@0: // be a pointer the string passed as our argument. Since the key of michael@0: // a map's value type is const, we can't fix it up once we've michael@0: // created our file. lower_bound does the lookup without doing an michael@0: // insertion, and returns a good hint iterator to pass to insert. michael@0: // Our "destiny" is where we belong, whether we're there or not now. michael@0: FileByNameMap::iterator destiny = files_.lower_bound(&name); michael@0: if (destiny == files_.end() michael@0: || *destiny->first != name) { // Repeated string comparison, boo hoo. michael@0: File *file = new File; michael@0: file->name = name; michael@0: file->source_id = -1; michael@0: destiny = files_.insert(destiny, michael@0: FileByNameMap::value_type(&file->name, file)); michael@0: } michael@0: return destiny->second; michael@0: } michael@0: michael@0: Module::File *Module::FindFile(const char *name) { michael@0: string name_string = name; michael@0: return FindFile(name_string); michael@0: } michael@0: michael@0: Module::File *Module::FindExistingFile(const string &name) { michael@0: FileByNameMap::iterator it = files_.find(&name); michael@0: return (it == files_.end()) ? NULL : it->second; michael@0: } michael@0: michael@0: void Module::GetFiles(vector *vec) { michael@0: vec->clear(); michael@0: for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); ++it) michael@0: vec->push_back(it->second); michael@0: } michael@0: michael@0: void Module::GetStackFrameEntries(vector* vec) { michael@0: vec->clear(); michael@0: vec->insert(vec->begin(), stack_frame_entries_.begin(), michael@0: stack_frame_entries_.end()); michael@0: } michael@0: michael@0: Module::StackFrameEntry* Module::FindStackFrameEntryByAddress(Address address) { michael@0: StackFrameEntry search; michael@0: search.address = address; michael@0: StackFrameEntrySet::iterator it = stack_frame_entries_.upper_bound(&search); michael@0: michael@0: if (it == stack_frame_entries_.begin()) michael@0: return NULL; michael@0: michael@0: it--; michael@0: if (EntryContainsAddress(*it, address)) michael@0: return *it; michael@0: michael@0: return NULL; michael@0: } michael@0: michael@0: void Module::AssignSourceIds() { michael@0: // First, give every source file an id of -1. michael@0: for (FileByNameMap::iterator file_it = files_.begin(); michael@0: file_it != files_.end(); ++file_it) { michael@0: file_it->second->source_id = -1; michael@0: } michael@0: michael@0: // Next, mark all files actually cited by our functions' line number michael@0: // info, by setting each one's source id to zero. michael@0: for (FunctionSet::const_iterator func_it = functions_.begin(); michael@0: func_it != functions_.end(); ++func_it) { michael@0: Function *func = *func_it; michael@0: for (vector::iterator line_it = func->lines.begin(); michael@0: line_it != func->lines.end(); ++line_it) michael@0: line_it->file->source_id = 0; michael@0: } michael@0: michael@0: // Finally, assign source ids to those files that have been marked. michael@0: // We could have just assigned source id numbers while traversing michael@0: // the line numbers, but doing it this way numbers the files in michael@0: // lexicographical order by name, which is neat. michael@0: int next_source_id = 0; michael@0: for (FileByNameMap::iterator file_it = files_.begin(); michael@0: file_it != files_.end(); ++file_it) { michael@0: if (!file_it->second->source_id) michael@0: file_it->second->source_id = next_source_id++; michael@0: } michael@0: } michael@0: michael@0: bool Module::ReportError() { michael@0: fprintf(stderr, "error writing symbol file: %s\n", michael@0: strerror(errno)); michael@0: return false; michael@0: } michael@0: michael@0: std::ostream& operator<<(std::ostream& stream, const Module::Expr& expr) { michael@0: assert(!expr.isExprInvalid()); michael@0: switch (expr.how_) { michael@0: case Module::kExprSimple: michael@0: stream << FromUniqueString(expr.ident_) << " " << expr.offset_ << " +"; michael@0: break; michael@0: case Module::kExprSimpleMem: michael@0: stream << FromUniqueString(expr.ident_) << " " << expr.offset_ << " + ^"; michael@0: break; michael@0: case Module::kExprPostfix: michael@0: stream << expr.postfix_; break; michael@0: case Module::kExprInvalid: michael@0: default: michael@0: break; michael@0: } michael@0: return stream; michael@0: } michael@0: michael@0: bool Module::WriteRuleMap(const RuleMap &rule_map, std::ostream &stream) { michael@0: // Visit the register rules in alphabetical order. Because michael@0: // rule_map has the elements in some arbitrary order, michael@0: // get the names out into a vector, sort them, and visit in michael@0: // sorted order. michael@0: std::vector rr_names; michael@0: for (RuleMap::const_iterator it = rule_map.begin(); michael@0: it != rule_map.end(); ++it) { michael@0: rr_names.push_back(it->first); michael@0: } michael@0: michael@0: std::sort(rr_names.begin(), rr_names.end(), LessThan_UniqueString); michael@0: michael@0: // Now visit the register rules in alphabetical order. michael@0: for (std::vector::const_iterator name = rr_names.begin(); michael@0: name != rr_names.end(); michael@0: ++name) { michael@0: if (name != rr_names.begin()) michael@0: stream << " "; michael@0: stream << FromUniqueString(*name) << ": " << rule_map.find(*name)->second; michael@0: } michael@0: return stream.good(); michael@0: } michael@0: michael@0: bool Module::Write(std::ostream &stream, SymbolData symbol_data) { michael@0: stream << "MODULE " << os_ << " " << architecture_ << " " michael@0: << id_ << " " << name_ << endl; michael@0: if (!stream.good()) michael@0: return ReportError(); michael@0: michael@0: if (symbol_data != ONLY_CFI) { michael@0: AssignSourceIds(); michael@0: michael@0: // Write out files. michael@0: for (FileByNameMap::iterator file_it = files_.begin(); michael@0: file_it != files_.end(); ++file_it) { michael@0: File *file = file_it->second; michael@0: if (file->source_id >= 0) { michael@0: stream << "FILE " << file->source_id << " " << file->name << endl; michael@0: if (!stream.good()) michael@0: return ReportError(); michael@0: } michael@0: } michael@0: michael@0: // Write out functions and their lines. michael@0: for (FunctionSet::const_iterator func_it = functions_.begin(); michael@0: func_it != functions_.end(); ++func_it) { michael@0: Function *func = *func_it; michael@0: stream << "FUNC " << hex michael@0: << (func->address - load_address_) << " " michael@0: << func->size << " " michael@0: << func->parameter_size << " " michael@0: << func->name << dec << endl; michael@0: if (!stream.good()) michael@0: return ReportError(); michael@0: michael@0: for (vector::iterator line_it = func->lines.begin(); michael@0: line_it != func->lines.end(); ++line_it) { michael@0: stream << hex michael@0: << (line_it->address - load_address_) << " " michael@0: << line_it->size << " " michael@0: << dec michael@0: << line_it->number << " " michael@0: << line_it->file->source_id << endl; michael@0: if (!stream.good()) michael@0: return ReportError(); michael@0: } michael@0: } michael@0: michael@0: // Write out 'PUBLIC' records. michael@0: for (ExternSet::const_iterator extern_it = externs_.begin(); michael@0: extern_it != externs_.end(); ++extern_it) { michael@0: Extern *ext = *extern_it; michael@0: stream << "PUBLIC " << hex michael@0: << (ext->address - load_address_) << " 0 " michael@0: << ext->name << dec << endl; michael@0: } michael@0: } michael@0: michael@0: if (symbol_data != NO_CFI) { michael@0: // Write out 'STACK CFI INIT' and 'STACK CFI' records. michael@0: StackFrameEntrySet::const_iterator frame_it; michael@0: for (frame_it = stack_frame_entries_.begin(); michael@0: frame_it != stack_frame_entries_.end(); ++frame_it) { michael@0: StackFrameEntry *entry = *frame_it; michael@0: stream << "STACK CFI INIT " << hex michael@0: << (entry->address - load_address_) << " " michael@0: << entry->size << " " << dec; michael@0: if (!stream.good() michael@0: || !WriteRuleMap(entry->initial_rules, stream)) michael@0: return ReportError(); michael@0: michael@0: stream << endl; michael@0: michael@0: // Write out this entry's delta rules as 'STACK CFI' records. michael@0: for (RuleChangeMap::const_iterator delta_it = entry->rule_changes.begin(); michael@0: delta_it != entry->rule_changes.end(); ++delta_it) { michael@0: stream << "STACK CFI " << hex michael@0: << (delta_it->first - load_address_) << " " << dec; michael@0: if (!stream.good() michael@0: || !WriteRuleMap(delta_it->second, stream)) michael@0: return ReportError(); michael@0: michael@0: stream << endl; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: } // namespace google_breakpad