1 : // Copyright (c) 2010 Google Inc. All Rights Reserved.
2 : //
3 : // Redistribution and use in source and binary forms, with or without
4 : // modification, are permitted provided that the following conditions are
5 : // met:
6 : //
7 : // * Redistributions of source code must retain the above copyright
8 : // notice, this list of conditions and the following disclaimer.
9 : // * Redistributions in binary form must reproduce the above
10 : // copyright notice, this list of conditions and the following disclaimer
11 : // in the documentation and/or other materials provided with the
12 : // distribution.
13 : // * Neither the name of Google Inc. nor the names of its
14 : // contributors may be used to endorse or promote products derived from
15 : // this software without specific prior written permission.
16 : //
17 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 :
29 : // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
30 :
31 : // This file implements the google_breakpad::StabsReader class.
32 : // See stabs_reader.h.
33 :
34 : #include "common/stabs_reader.h"
35 :
36 : #include <assert.h>
37 : #include <stab.h>
38 : #include <string.h>
39 :
40 : using std::vector;
41 :
42 : namespace google_breakpad {
43 :
44 0 : StabsReader::EntryIterator::EntryIterator(const ByteBuffer *buffer,
45 : bool big_endian, size_t value_size)
46 0 : : value_size_(value_size), cursor_(buffer, big_endian) {
47 : // Actually, we could handle weird sizes just fine, but they're
48 : // probably mistakes --- expressed in bits, say.
49 0 : assert(value_size == 4 || value_size == 8);
50 0 : entry_.index = 0;
51 0 : Fetch();
52 0 : }
53 :
54 0 : void StabsReader::EntryIterator::Fetch() {
55 : cursor_
56 0 : .Read(4, false, &entry_.name_offset)
57 0 : .Read(1, false, &entry_.type)
58 0 : .Read(1, false, &entry_.other)
59 0 : .Read(2, false, &entry_.descriptor)
60 0 : .Read(value_size_, false, &entry_.value);
61 0 : entry_.at_end = !cursor_;
62 0 : }
63 :
64 0 : StabsReader::StabsReader(const uint8_t *stab, size_t stab_size,
65 : const uint8_t *stabstr, size_t stabstr_size,
66 : bool big_endian, size_t value_size, bool unitized,
67 : StabsHandler *handler)
68 : : entries_(stab, stab_size),
69 : strings_(stabstr, stabstr_size),
70 : iterator_(&entries_, big_endian, value_size),
71 : unitized_(unitized),
72 : handler_(handler),
73 : string_offset_(0),
74 : next_cu_string_offset_(0),
75 0 : current_source_file_(NULL) { }
76 :
77 0 : const char *StabsReader::SymbolString() {
78 0 : ptrdiff_t offset = string_offset_ + iterator_->name_offset;
79 0 : if (offset < 0 || (size_t) offset >= strings_.Size()) {
80 : handler_->Warning("symbol %d: name offset outside the string section\n",
81 0 : iterator_->index);
82 : // Return our null string, to keep our promise about all names being
83 : // taken from the string section.
84 0 : offset = 0;
85 : }
86 0 : return reinterpret_cast<const char *>(strings_.start + offset);
87 : }
88 :
89 0 : bool StabsReader::Process() {
90 0 : while (!iterator_->at_end) {
91 0 : if (iterator_->type == N_SO) {
92 0 : if (! ProcessCompilationUnit())
93 0 : return false;
94 0 : } else if (iterator_->type == N_UNDF && unitized_) {
95 : // In unitized STABS (including Linux STABS, and pretty much anything
96 : // else that puts STABS data in sections), at the head of each
97 : // compilation unit's entries there is an N_UNDF stab giving the
98 : // number of symbols in the compilation unit, and the number of bytes
99 : // that compilation unit's strings take up in the .stabstr section.
100 : // Each CU's strings are separate; the n_strx values are offsets
101 : // within the current CU's portion of the .stabstr section.
102 : //
103 : // As an optimization, the GNU linker combines all the
104 : // compilation units into one, with a single N_UNDF at the
105 : // beginning. However, other linkers, like Gold, do not perform
106 : // this optimization.
107 0 : string_offset_ = next_cu_string_offset_;
108 0 : next_cu_string_offset_ = iterator_->value;
109 0 : ++iterator_;
110 : } else
111 0 : ++iterator_;
112 : }
113 0 : return true;
114 : }
115 :
116 0 : bool StabsReader::ProcessCompilationUnit() {
117 0 : assert(!iterator_->at_end && iterator_->type == N_SO);
118 :
119 : // There may be an N_SO entry whose name ends with a slash,
120 : // indicating the directory in which the compilation occurred.
121 : // The build directory defaults to NULL.
122 0 : const char *build_directory = NULL;
123 : {
124 0 : const char *name = SymbolString();
125 0 : if (name[0] && name[strlen(name) - 1] == '/') {
126 0 : build_directory = name;
127 0 : ++iterator_;
128 : }
129 : }
130 :
131 : // We expect to see an N_SO entry with a filename next, indicating
132 : // the start of the compilation unit.
133 : {
134 0 : if (iterator_->at_end || iterator_->type != N_SO)
135 0 : return true;
136 0 : const char *name = SymbolString();
137 0 : if (name[0] == '\0') {
138 : // This seems to be a stray end-of-compilation-unit marker;
139 : // consume it, but don't report the end, since we didn't see a
140 : // beginning.
141 0 : ++iterator_;
142 0 : return true;
143 : }
144 0 : current_source_file_ = name;
145 : }
146 :
147 0 : if (! handler_->StartCompilationUnit(current_source_file_,
148 0 : iterator_->value,
149 0 : build_directory))
150 0 : return false;
151 :
152 0 : ++iterator_;
153 :
154 : // The STABS documentation says that some compilers may emit
155 : // additional N_SO entries with names immediately following the
156 : // first, and that they should be ignored. However, the original
157 : // Breakpad STABS reader doesn't ignore them, so we won't either.
158 :
159 : // Process the body of the compilation unit, up to the next N_SO.
160 0 : while (!iterator_->at_end && iterator_->type != N_SO) {
161 0 : if (iterator_->type == N_FUN) {
162 0 : if (! ProcessFunction())
163 0 : return false;
164 0 : } else if (iterator_->type == N_SLINE) {
165 : // Mac OS X STABS place SLINE records before functions.
166 : Line line;
167 : // The value of an N_SLINE entry that appears outside a function is
168 : // the absolute address of the line.
169 0 : line.address = iterator_->value;
170 0 : line.filename = current_source_file_;
171 : // The n_desc of a N_SLINE entry is the line number. It's a
172 : // signed 16-bit field; line numbers from 32768 to 65535 are
173 : // stored as n-65536.
174 0 : line.number = (uint16_t) iterator_->descriptor;
175 0 : queued_lines_.push_back(line);
176 0 : ++iterator_;
177 0 : } else if (iterator_->type == N_SOL) {
178 0 : current_source_file_ = SymbolString();
179 0 : ++iterator_;
180 : } else {
181 : // Ignore anything else.
182 0 : ++iterator_;
183 : }
184 : }
185 :
186 : // An N_SO with an empty name indicates the end of the compilation
187 : // unit. Default to zero.
188 0 : uint64_t ending_address = 0;
189 0 : if (!iterator_->at_end) {
190 0 : assert(iterator_->type == N_SO);
191 0 : const char *name = SymbolString();
192 0 : if (name[0] == '\0') {
193 0 : ending_address = iterator_->value;
194 0 : ++iterator_;
195 : }
196 : }
197 :
198 0 : if (! handler_->EndCompilationUnit(ending_address))
199 0 : return false;
200 :
201 0 : queued_lines_.clear();
202 :
203 0 : return true;
204 : }
205 :
206 0 : bool StabsReader::ProcessFunction() {
207 0 : assert(!iterator_->at_end && iterator_->type == N_FUN);
208 :
209 0 : uint64_t function_address = iterator_->value;
210 : // The STABS string for an N_FUN entry is the name of the function,
211 : // followed by a colon, followed by type information for the
212 : // function. We want to pass the name alone to StartFunction.
213 0 : const char *stab_string = SymbolString();
214 0 : const char *name_end = strchr(stab_string, ':');
215 0 : if (! name_end)
216 0 : name_end = stab_string + strlen(stab_string);
217 0 : std::string name(stab_string, name_end - stab_string);
218 0 : if (! handler_->StartFunction(name, function_address))
219 0 : return false;
220 0 : ++iterator_;
221 :
222 : // If there were any SLINE records given before the function, report them now.
223 0 : for (vector<Line>::const_iterator it = queued_lines_.begin();
224 0 : it != queued_lines_.end(); it++) {
225 0 : if (!handler_->Line(it->address, it->filename, it->number))
226 0 : return false;
227 : }
228 0 : queued_lines_.clear();
229 :
230 0 : while (!iterator_->at_end) {
231 0 : if (iterator_->type == N_SO || iterator_->type == N_FUN)
232 0 : break;
233 0 : else if (iterator_->type == N_SLINE) {
234 : // The value of an N_SLINE entry is the offset of the line from
235 : // the function's start address.
236 0 : uint64_t line_address = function_address + iterator_->value;
237 : // The n_desc of a N_SLINE entry is the line number. It's a
238 : // signed 16-bit field; line numbers from 32768 to 65535 are
239 : // stored as n-65536.
240 0 : uint16_t line_number = iterator_->descriptor;
241 0 : if (! handler_->Line(line_address, current_source_file_, line_number))
242 0 : return false;
243 0 : ++iterator_;
244 0 : } else if (iterator_->type == N_SOL) {
245 0 : current_source_file_ = SymbolString();
246 0 : ++iterator_;
247 : } else
248 : // Ignore anything else.
249 0 : ++iterator_;
250 : }
251 :
252 : // We've reached the end of the function. See if we can figure out its
253 : // ending address.
254 0 : uint64_t ending_address = 0;
255 0 : if (!iterator_->at_end) {
256 0 : assert(iterator_->type == N_SO || iterator_->type == N_FUN);
257 0 : if (iterator_->type == N_FUN) {
258 0 : const char *name = SymbolString();
259 0 : if (name[0] == '\0') {
260 : // An N_FUN entry with no name is a terminator for this function;
261 : // its value is the function's size.
262 0 : ending_address = function_address + iterator_->value;
263 0 : ++iterator_;
264 : } else {
265 : // An N_FUN entry with a name is the next function, and we can take
266 : // its value as our ending address. Don't advance the iterator, as
267 : // we'll use this symbol to start the next function as well.
268 0 : ending_address = iterator_->value;
269 : }
270 : } else {
271 : // An N_SO entry could be an end-of-compilation-unit marker, or the
272 : // start of the next compilation unit, but in either case, its value
273 : // is our ending address. We don't advance the iterator;
274 : // ProcessCompilationUnit will decide what to do with this symbol.
275 0 : ending_address = iterator_->value;
276 : }
277 : }
278 :
279 0 : if (! handler_->EndFunction(ending_address))
280 0 : return false;
281 :
282 0 : return true;
283 : }
284 :
285 : } // namespace google_breakpad
|