module.cc revision 4ac61acb3a7dad6ce722fe07564be8ec92713228
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// module.cc: Implement google_breakpad::Module.  See module.h.
33
34#include "common/module.h"
35
36#include <errno.h>
37#include <string.h>
38
39namespace google_breakpad {
40
41Module::Module(const string &name, const string &os,
42               const string &architecture, const string &id) :
43    name_(name),
44    os_(os),
45    architecture_(architecture),
46    id_(id),
47    load_address_(0) { }
48
49Module::~Module() {
50  for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); it++)
51    delete it->second;
52  for (FunctionSet::iterator it = functions_.begin();
53       it != functions_.end(); it++)
54    delete *it;
55  for (vector<StackFrameEntry *>::iterator it = stack_frame_entries_.begin();
56       it != stack_frame_entries_.end(); it++)
57    delete *it;
58}
59
60void Module::SetLoadAddress(Address address) {
61  load_address_ = address;
62}
63
64void Module::AddFunction(Function *function) {
65  std::pair<FunctionSet::iterator,bool> ret = functions_.insert(function);
66  if (!ret.second) {
67    // Free the duplicate we failed to insert because we own it.
68    delete function;
69  }
70}
71
72void Module::AddFunctions(vector<Function *>::iterator begin,
73                          vector<Function *>::iterator end) {
74  for (vector<Function *>::iterator it = begin; it != end; it++)
75    AddFunction(*it);
76}
77
78void Module::AddStackFrameEntry(StackFrameEntry *stack_frame_entry) {
79  stack_frame_entries_.push_back(stack_frame_entry);
80}
81
82void Module::GetFunctions(vector<Function *> *vec,
83                          vector<Function *>::iterator i) {
84  vec->insert(i, functions_.begin(), functions_.end());
85}
86
87Module::File *Module::FindFile(const string &name) {
88  // A tricky bit here.  The key of each map entry needs to be a
89  // pointer to the entry's File's name string.  This means that we
90  // can't do the initial lookup with any operation that would create
91  // an empty entry for us if the name isn't found (like, say,
92  // operator[] or insert do), because such a created entry's key will
93  // be a pointer the string passed as our argument.  Since the key of
94  // a map's value type is const, we can't fix it up once we've
95  // created our file.  lower_bound does the lookup without doing an
96  // insertion, and returns a good hint iterator to pass to insert.
97  // Our "destiny" is where we belong, whether we're there or not now.
98  FileByNameMap::iterator destiny = files_.lower_bound(&name);
99  if (destiny == files_.end()
100      || *destiny->first != name) {  // Repeated string comparison, boo hoo.
101    File *file = new File;
102    file->name = name;
103    file->source_id = -1;
104    destiny = files_.insert(destiny,
105                            FileByNameMap::value_type(&file->name, file));
106  }
107  return destiny->second;
108}
109
110Module::File *Module::FindFile(const char *name) {
111  string name_string = name;
112  return FindFile(name_string);
113}
114
115Module::File *Module::FindExistingFile(const string &name) {
116  FileByNameMap::iterator it = files_.find(&name);
117  return (it == files_.end()) ? NULL : it->second;
118}
119
120void Module::GetFiles(vector<File *> *vec) {
121  vec->clear();
122  for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); it++)
123    vec->push_back(it->second);
124}
125
126void Module::GetStackFrameEntries(vector<StackFrameEntry *> *vec) {
127  *vec = stack_frame_entries_;
128}
129
130void Module::AssignSourceIds() {
131  // First, give every source file an id of -1.
132  for (FileByNameMap::iterator file_it = files_.begin();
133       file_it != files_.end(); file_it++)
134    file_it->second->source_id = -1;
135
136  // Next, mark all files actually cited by our functions' line number
137  // info, by setting each one's source id to zero.
138  for (FunctionSet::const_iterator func_it = functions_.begin();
139       func_it != functions_.end(); func_it++) {
140    Function *func = *func_it;
141    for (vector<Line>::iterator line_it = func->lines.begin();
142         line_it != func->lines.end(); line_it++)
143      line_it->file->source_id = 0;
144  }
145
146  // Finally, assign source ids to those files that have been marked.
147  // We could have just assigned source id numbers while traversing
148  // the line numbers, but doing it this way numbers the files in
149  // lexicographical order by name, which is neat.
150  int next_source_id = 0;
151  for (FileByNameMap::iterator file_it = files_.begin();
152       file_it != files_.end(); file_it++)
153    if (!file_it->second->source_id)
154      file_it->second->source_id = next_source_id++;
155}
156
157bool Module::ReportError() {
158  fprintf(stderr, "error writing symbol file: %s\n",
159          strerror(errno));
160  return false;
161}
162
163bool Module::WriteRuleMap(const RuleMap &rule_map, FILE *stream) {
164  for (RuleMap::const_iterator it = rule_map.begin();
165       it != rule_map.end(); it++) {
166    if (it != rule_map.begin() &&
167        0 > putc(' ', stream))
168      return false;
169    if (0 > fprintf(stream, "%s: %s", it->first.c_str(), it->second.c_str()))
170      return false;
171  }
172  return true;
173}
174
175bool Module::Write(FILE *stream) {
176  if (0 > fprintf(stream, "MODULE %s %s %s %s\n",
177                  os_.c_str(), architecture_.c_str(), id_.c_str(),
178                  name_.c_str()))
179    return ReportError();
180
181  AssignSourceIds();
182
183  // Write out files.
184  for (FileByNameMap::iterator file_it = files_.begin();
185       file_it != files_.end(); file_it++) {
186    File *file = file_it->second;
187    if (file->source_id >= 0) {
188      if (0 > fprintf(stream, "FILE %d %s\n",
189                      file->source_id, file->name.c_str()))
190        return ReportError();
191    }
192  }
193
194  // Write out functions and their lines.
195  for (FunctionSet::const_iterator func_it = functions_.begin();
196       func_it != functions_.end(); func_it++) {
197    Function *func = *func_it;
198    if (0 > fprintf(stream, "FUNC %llx %llx %llx %s\n",
199                    (unsigned long long) (func->address - load_address_),
200                    (unsigned long long) func->size,
201                    (unsigned long long) func->parameter_size,
202                    func->name.c_str()))
203      return ReportError();
204    for (vector<Line>::iterator line_it = func->lines.begin();
205         line_it != func->lines.end(); line_it++)
206      if (0 > fprintf(stream, "%llx %llx %d %d\n",
207                      (unsigned long long) (line_it->address - load_address_),
208                      (unsigned long long) line_it->size,
209                      line_it->number,
210                      line_it->file->source_id))
211        return ReportError();
212  }
213
214  // Write out 'STACK CFI INIT' and 'STACK CFI' records.
215  vector<StackFrameEntry *>::const_iterator frame_it;
216  for (frame_it = stack_frame_entries_.begin();
217       frame_it != stack_frame_entries_.end(); frame_it++) {
218    StackFrameEntry *entry = *frame_it;
219    if (0 > fprintf(stream, "STACK CFI INIT %llx %llx ",
220                    (unsigned long long) entry->address - load_address_,
221                    (unsigned long long) entry->size)
222        || !WriteRuleMap(entry->initial_rules, stream)
223        || 0 > putc('\n', stream))
224      return ReportError();
225
226    // Write out this entry's delta rules as 'STACK CFI' records.
227    for (RuleChangeMap::const_iterator delta_it = entry->rule_changes.begin();
228         delta_it != entry->rule_changes.end(); delta_it++) {
229      if (0 > fprintf(stream, "STACK CFI %llx ",
230                      (unsigned long long) delta_it->first - load_address_)
231          || !WriteRuleMap(delta_it->second, stream)
232          || 0 > putc('\n', stream))
233        return ReportError();
234    }
235  }
236
237  return true;
238}
239
240} // namespace google_breakpad
241