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 // basic_source_line_resolver.cc: BasicSourceLineResolver implementation.
31 //
32 // See basic_source_line_resolver.h and basic_source_line_resolver_types.h
33 // for documentation.
34
35 #include <assert.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40
41 #include <limits>
42 #include <map>
43 #include <utility>
44 #include <vector>
45
46 #include "google_breakpad/processor/basic_source_line_resolver.h"
47 #include "processor/basic_source_line_resolver_types.h"
48 #include "processor/module_factory.h"
49
50 #include "processor/tokenize.h"
51
52 using std::map;
53 using std::vector;
54 using std::make_pair;
55
56 namespace google_breakpad {
57
58 #ifdef _WIN32
59 #ifdef _MSC_VER
60 #define strtok_r strtok_s
61 #endif
62 #define strtoull _strtoui64
63 #endif
64
65 namespace {
66
67 // Utility function to tokenize given the presence of an optional initial
68 // field. In this case, optional_field is the expected string for the optional
69 // field, and max_tokens is the maximum number of tokens including the optional
70 // field. Refer to the documentation for Tokenize for descriptions of the other
71 // arguments.
TokenizeWithOptionalField(char * line,const char * optional_field,const char * separators,int max_tokens,vector<char * > * tokens)72 bool TokenizeWithOptionalField(char *line,
73 const char *optional_field,
74 const char *separators,
75 int max_tokens,
76 vector<char*> *tokens) {
77 // First tokenize assuming the optional field is not present. If we then see
78 // the optional field, additionally tokenize the last token into two tokens.
79 if (!Tokenize(line, separators, max_tokens - 1, tokens)) {
80 return false;
81 }
82
83 if (strcmp(tokens->front(), optional_field) == 0) {
84 // The optional field is present. Split the last token in two to recover the
85 // field prior to the last.
86 vector<char*> last_tokens;
87 if (!Tokenize(tokens->back(), separators, 2, &last_tokens)) {
88 return false;
89 }
90 // Replace the previous last token with the two new tokens.
91 tokens->pop_back();
92 tokens->push_back(last_tokens[0]);
93 tokens->push_back(last_tokens[1]);
94 }
95
96 return true;
97 }
98
99 } // namespace
100
101 static const char *kWhitespace = " \r\n";
102 static const int kMaxErrorsPrinted = 5;
103 static const int kMaxErrorsBeforeBailing = 100;
104
BasicSourceLineResolver()105 BasicSourceLineResolver::BasicSourceLineResolver() :
106 SourceLineResolverBase(new BasicModuleFactory) { }
107
108 // static
LogParseError(const string & message,int line_number,int * num_errors)109 void BasicSourceLineResolver::Module::LogParseError(
110 const string &message,
111 int line_number,
112 int *num_errors) {
113 if (++(*num_errors) <= kMaxErrorsPrinted) {
114 if (line_number > 0) {
115 BPLOG(ERROR) << "Line " << line_number << ": " << message;
116 } else {
117 BPLOG(ERROR) << message;
118 }
119 }
120 }
121
LoadMapFromMemory(char * memory_buffer,size_t memory_buffer_size)122 bool BasicSourceLineResolver::Module::LoadMapFromMemory(
123 char *memory_buffer,
124 size_t memory_buffer_size) {
125 linked_ptr<Function> cur_func;
126 int line_number = 0;
127 int num_errors = 0;
128 char *save_ptr;
129
130 // If the length is 0, we can still pretend we have a symbol file. This is
131 // for scenarios that want to test symbol lookup, but don't necessarily care
132 // if certain modules do not have any information, like system libraries.
133 if (memory_buffer_size == 0) {
134 return true;
135 }
136
137 // Make sure the last character is null terminator.
138 size_t last_null_terminator = memory_buffer_size - 1;
139 if (memory_buffer[last_null_terminator] != '\0') {
140 memory_buffer[last_null_terminator] = '\0';
141 }
142
143 // Skip any null terminators at the end of the memory buffer, and make sure
144 // there are no other null terminators in the middle of the memory buffer.
145 bool has_null_terminator_in_the_middle = false;
146 while (last_null_terminator > 0 &&
147 memory_buffer[last_null_terminator - 1] == '\0') {
148 last_null_terminator--;
149 }
150 for (size_t i = 0; i < last_null_terminator; i++) {
151 if (memory_buffer[i] == '\0') {
152 memory_buffer[i] = '_';
153 has_null_terminator_in_the_middle = true;
154 }
155 }
156 if (has_null_terminator_in_the_middle) {
157 LogParseError(
158 "Null terminator is not expected in the middle of the symbol data",
159 line_number,
160 &num_errors);
161 }
162
163 char *buffer;
164 buffer = strtok_r(memory_buffer, "\r\n", &save_ptr);
165
166 while (buffer != NULL) {
167 ++line_number;
168
169 if (strncmp(buffer, "FILE ", 5) == 0) {
170 if (!ParseFile(buffer)) {
171 LogParseError("ParseFile on buffer failed", line_number, &num_errors);
172 }
173 } else if (strncmp(buffer, "STACK ", 6) == 0) {
174 if (!ParseStackInfo(buffer)) {
175 LogParseError("ParseStackInfo failed", line_number, &num_errors);
176 }
177 } else if (strncmp(buffer, "FUNC ", 5) == 0) {
178 cur_func.reset(ParseFunction(buffer));
179 if (!cur_func.get()) {
180 LogParseError("ParseFunction failed", line_number, &num_errors);
181 } else {
182 // StoreRange will fail if the function has an invalid address or size.
183 // We'll silently ignore this, the function and any corresponding lines
184 // will be destroyed when cur_func is released.
185 functions_.StoreRange(cur_func->address, cur_func->size, cur_func);
186 }
187 } else if (strncmp(buffer, "PUBLIC ", 7) == 0) {
188 // Clear cur_func: public symbols don't contain line number information.
189 cur_func.reset();
190
191 if (!ParsePublicSymbol(buffer)) {
192 LogParseError("ParsePublicSymbol failed", line_number, &num_errors);
193 }
194 } else if (strncmp(buffer, "MODULE ", 7) == 0) {
195 // Ignore these. They're not of any use to BasicSourceLineResolver,
196 // which is fed modules by a SymbolSupplier. These lines are present to
197 // aid other tools in properly placing symbol files so that they can
198 // be accessed by a SymbolSupplier.
199 //
200 // MODULE <guid> <age> <filename>
201 } else if (strncmp(buffer, "INFO ", 5) == 0) {
202 // Ignore these as well, they're similarly just for housekeeping.
203 //
204 // INFO CODE_ID <code id> <filename>
205 } else {
206 if (!cur_func.get()) {
207 LogParseError("Found source line data without a function",
208 line_number, &num_errors);
209 } else {
210 Line *line = ParseLine(buffer);
211 if (!line) {
212 LogParseError("ParseLine failed", line_number, &num_errors);
213 } else {
214 cur_func->lines.StoreRange(line->address, line->size,
215 linked_ptr<Line>(line));
216 }
217 }
218 }
219 if (num_errors > kMaxErrorsBeforeBailing) {
220 break;
221 }
222 buffer = strtok_r(NULL, "\r\n", &save_ptr);
223 }
224 is_corrupt_ = num_errors > 0;
225 return true;
226 }
227
LookupAddress(StackFrame * frame) const228 void BasicSourceLineResolver::Module::LookupAddress(StackFrame *frame) const {
229 MemAddr address = frame->instruction - frame->module->base_address();
230
231 // First, look for a FUNC record that covers address. Use
232 // RetrieveNearestRange instead of RetrieveRange so that, if there
233 // is no such function, we can use the next function to bound the
234 // extent of the PUBLIC symbol we find, below. This does mean we
235 // need to check that address indeed falls within the function we
236 // find; do the range comparison in an overflow-friendly way.
237 linked_ptr<Function> func;
238 linked_ptr<PublicSymbol> public_symbol;
239 MemAddr function_base;
240 MemAddr function_size;
241 MemAddr public_address;
242 if (functions_.RetrieveNearestRange(address, &func, &function_base,
243 NULL /* delta */, &function_size) &&
244 address >= function_base && address - function_base < function_size) {
245 frame->function_name = func->name;
246 frame->function_base = frame->module->base_address() + function_base;
247
248 linked_ptr<Line> line;
249 MemAddr line_base;
250 if (func->lines.RetrieveRange(address, &line, &line_base, NULL /* delta */,
251 NULL /* size */)) {
252 FileMap::const_iterator it = files_.find(line->source_file_id);
253 if (it != files_.end()) {
254 frame->source_file_name = files_.find(line->source_file_id)->second;
255 }
256 frame->source_line = line->line;
257 frame->source_line_base = frame->module->base_address() + line_base;
258 }
259 } else if (public_symbols_.Retrieve(address,
260 &public_symbol, &public_address) &&
261 (!func.get() || public_address > function_base)) {
262 frame->function_name = public_symbol->name;
263 frame->function_base = frame->module->base_address() + public_address;
264 }
265 }
266
FindWindowsFrameInfo(const StackFrame * frame) const267 WindowsFrameInfo *BasicSourceLineResolver::Module::FindWindowsFrameInfo(
268 const StackFrame *frame) const {
269 MemAddr address = frame->instruction - frame->module->base_address();
270 scoped_ptr<WindowsFrameInfo> result(new WindowsFrameInfo());
271
272 // We only know about WindowsFrameInfo::STACK_INFO_FRAME_DATA and
273 // WindowsFrameInfo::STACK_INFO_FPO. Prefer them in this order.
274 // WindowsFrameInfo::STACK_INFO_FRAME_DATA is the newer type that
275 // includes its own program string.
276 // WindowsFrameInfo::STACK_INFO_FPO is the older type
277 // corresponding to the FPO_DATA struct. See stackwalker_x86.cc.
278 linked_ptr<WindowsFrameInfo> frame_info;
279 if ((windows_frame_info_[WindowsFrameInfo::STACK_INFO_FRAME_DATA]
280 .RetrieveRange(address, &frame_info))
281 || (windows_frame_info_[WindowsFrameInfo::STACK_INFO_FPO]
282 .RetrieveRange(address, &frame_info))) {
283 result->CopyFrom(*frame_info.get());
284 return result.release();
285 }
286
287 // Even without a relevant STACK line, many functions contain
288 // information about how much space their parameters consume on the
289 // stack. Use RetrieveNearestRange instead of RetrieveRange, so that
290 // we can use the function to bound the extent of the PUBLIC symbol,
291 // below. However, this does mean we need to check that ADDRESS
292 // falls within the retrieved function's range; do the range
293 // comparison in an overflow-friendly way.
294 linked_ptr<Function> function;
295 MemAddr function_base, function_size;
296 if (functions_.RetrieveNearestRange(address, &function, &function_base,
297 NULL /* delta */, &function_size) &&
298 address >= function_base && address - function_base < function_size) {
299 result->parameter_size = function->parameter_size;
300 result->valid |= WindowsFrameInfo::VALID_PARAMETER_SIZE;
301 return result.release();
302 }
303
304 // PUBLIC symbols might have a parameter size. Use the function we
305 // found above to limit the range the public symbol covers.
306 linked_ptr<PublicSymbol> public_symbol;
307 MemAddr public_address;
308 if (public_symbols_.Retrieve(address, &public_symbol, &public_address) &&
309 (!function.get() || public_address > function_base)) {
310 result->parameter_size = public_symbol->parameter_size;
311 }
312
313 return NULL;
314 }
315
FindCFIFrameInfo(const StackFrame * frame) const316 CFIFrameInfo *BasicSourceLineResolver::Module::FindCFIFrameInfo(
317 const StackFrame *frame) const {
318 MemAddr address = frame->instruction - frame->module->base_address();
319 MemAddr initial_base, initial_size;
320 string initial_rules;
321
322 // Find the initial rule whose range covers this address. That
323 // provides an initial set of register recovery rules. Then, walk
324 // forward from the initial rule's starting address to frame's
325 // instruction address, applying delta rules.
326 if (!cfi_initial_rules_.RetrieveRange(address, &initial_rules, &initial_base,
327 NULL /* delta */, &initial_size)) {
328 return NULL;
329 }
330
331 // Create a frame info structure, and populate it with the rules from
332 // the STACK CFI INIT record.
333 scoped_ptr<CFIFrameInfo> rules(new CFIFrameInfo());
334 if (!ParseCFIRuleSet(initial_rules, rules.get()))
335 return NULL;
336
337 // Find the first delta rule that falls within the initial rule's range.
338 map<MemAddr, string>::const_iterator delta =
339 cfi_delta_rules_.lower_bound(initial_base);
340
341 // Apply delta rules up to and including the frame's address.
342 while (delta != cfi_delta_rules_.end() && delta->first <= address) {
343 ParseCFIRuleSet(delta->second, rules.get());
344 delta++;
345 }
346
347 return rules.release();
348 }
349
ParseFile(char * file_line)350 bool BasicSourceLineResolver::Module::ParseFile(char *file_line) {
351 long index;
352 char *filename;
353 if (SymbolParseHelper::ParseFile(file_line, &index, &filename)) {
354 files_.insert(make_pair(index, string(filename)));
355 return true;
356 }
357 return false;
358 }
359
360 BasicSourceLineResolver::Function*
ParseFunction(char * function_line)361 BasicSourceLineResolver::Module::ParseFunction(char *function_line) {
362 bool is_multiple;
363 uint64_t address;
364 uint64_t size;
365 long stack_param_size;
366 char *name;
367 if (SymbolParseHelper::ParseFunction(function_line, &is_multiple, &address,
368 &size, &stack_param_size, &name)) {
369 return new Function(name, address, size, stack_param_size, is_multiple);
370 }
371 return NULL;
372 }
373
ParseLine(char * line_line)374 BasicSourceLineResolver::Line* BasicSourceLineResolver::Module::ParseLine(
375 char *line_line) {
376 uint64_t address;
377 uint64_t size;
378 long line_number;
379 long source_file;
380
381 if (SymbolParseHelper::ParseLine(line_line, &address, &size, &line_number,
382 &source_file)) {
383 return new Line(address, size, source_file, line_number);
384 }
385 return NULL;
386 }
387
ParsePublicSymbol(char * public_line)388 bool BasicSourceLineResolver::Module::ParsePublicSymbol(char *public_line) {
389 bool is_multiple;
390 uint64_t address;
391 long stack_param_size;
392 char *name;
393
394 if (SymbolParseHelper::ParsePublicSymbol(public_line, &is_multiple, &address,
395 &stack_param_size, &name)) {
396 // A few public symbols show up with an address of 0. This has been seen
397 // in the dumped output of ntdll.pdb for symbols such as _CIlog, _CIpow,
398 // RtlDescribeChunkLZNT1, and RtlReserveChunkLZNT1. They would conflict
399 // with one another if they were allowed into the public_symbols_ map,
400 // but since the address is obviously invalid, gracefully accept them
401 // as input without putting them into the map.
402 if (address == 0) {
403 return true;
404 }
405
406 linked_ptr<PublicSymbol> symbol(new PublicSymbol(name, address,
407 stack_param_size,
408 is_multiple));
409 return public_symbols_.Store(address, symbol);
410 }
411 return false;
412 }
413
ParseStackInfo(char * stack_info_line)414 bool BasicSourceLineResolver::Module::ParseStackInfo(char *stack_info_line) {
415 // Skip "STACK " prefix.
416 stack_info_line += 6;
417
418 // Find the token indicating what sort of stack frame walking
419 // information this is.
420 while (*stack_info_line == ' ')
421 stack_info_line++;
422 const char *platform = stack_info_line;
423 while (!strchr(kWhitespace, *stack_info_line))
424 stack_info_line++;
425 *stack_info_line++ = '\0';
426
427 // MSVC stack frame info.
428 if (strcmp(platform, "WIN") == 0) {
429 int type = 0;
430 uint64_t rva, code_size;
431 linked_ptr<WindowsFrameInfo>
432 stack_frame_info(WindowsFrameInfo::ParseFromString(stack_info_line,
433 type,
434 rva,
435 code_size));
436 if (stack_frame_info == NULL)
437 return false;
438
439 // TODO(mmentovai): I wanted to use StoreRange's return value as this
440 // method's return value, but MSVC infrequently outputs stack info that
441 // violates the containment rules. This happens with a section of code
442 // in strncpy_s in test_app.cc (testdata/minidump2). There, problem looks
443 // like this:
444 // STACK WIN 4 4242 1a a 0 ... (STACK WIN 4 base size prolog 0 ...)
445 // STACK WIN 4 4243 2e 9 0 ...
446 // ContainedRangeMap treats these two blocks as conflicting. In reality,
447 // when the prolog lengths are taken into account, the actual code of
448 // these blocks doesn't conflict. However, we can't take the prolog lengths
449 // into account directly here because we'd wind up with a different set
450 // of range conflicts when MSVC outputs stack info like this:
451 // STACK WIN 4 1040 73 33 0 ...
452 // STACK WIN 4 105a 59 19 0 ...
453 // because in both of these entries, the beginning of the code after the
454 // prolog is at 0x1073, and the last byte of contained code is at 0x10b2.
455 // Perhaps we could get away with storing ranges by rva + prolog_size
456 // if ContainedRangeMap were modified to allow replacement of
457 // already-stored values.
458
459 windows_frame_info_[type].StoreRange(rva, code_size, stack_frame_info);
460 return true;
461 } else if (strcmp(platform, "CFI") == 0) {
462 // DWARF CFI stack frame info
463 return ParseCFIFrameInfo(stack_info_line);
464 } else {
465 // Something unrecognized.
466 return false;
467 }
468 }
469
ParseCFIFrameInfo(char * stack_info_line)470 bool BasicSourceLineResolver::Module::ParseCFIFrameInfo(
471 char *stack_info_line) {
472 char *cursor;
473
474 // Is this an INIT record or a delta record?
475 char *init_or_address = strtok_r(stack_info_line, " \r\n", &cursor);
476 if (!init_or_address)
477 return false;
478
479 if (strcmp(init_or_address, "INIT") == 0) {
480 // This record has the form "STACK INIT <address> <size> <rules...>".
481 char *address_field = strtok_r(NULL, " \r\n", &cursor);
482 if (!address_field) return false;
483
484 char *size_field = strtok_r(NULL, " \r\n", &cursor);
485 if (!size_field) return false;
486
487 char *initial_rules = strtok_r(NULL, "\r\n", &cursor);
488 if (!initial_rules) return false;
489
490 MemAddr address = strtoul(address_field, NULL, 16);
491 MemAddr size = strtoul(size_field, NULL, 16);
492 cfi_initial_rules_.StoreRange(address, size, initial_rules);
493 return true;
494 }
495
496 // This record has the form "STACK <address> <rules...>".
497 char *address_field = init_or_address;
498 char *delta_rules = strtok_r(NULL, "\r\n", &cursor);
499 if (!delta_rules) return false;
500 MemAddr address = strtoul(address_field, NULL, 16);
501 cfi_delta_rules_[address] = delta_rules;
502 return true;
503 }
504
505 // static
ParseFile(char * file_line,long * index,char ** filename)506 bool SymbolParseHelper::ParseFile(char *file_line, long *index,
507 char **filename) {
508 // FILE <id> <filename>
509 assert(strncmp(file_line, "FILE ", 5) == 0);
510 file_line += 5; // skip prefix
511
512 vector<char*> tokens;
513 if (!Tokenize(file_line, kWhitespace, 2, &tokens)) {
514 return false;
515 }
516
517 char *after_number;
518 *index = strtol(tokens[0], &after_number, 10);
519 if (!IsValidAfterNumber(after_number) || *index < 0 ||
520 *index == std::numeric_limits<long>::max()) {
521 return false;
522 }
523
524 *filename = tokens[1];
525 if (!*filename) {
526 return false;
527 }
528
529 return true;
530 }
531
532 // static
ParseFunction(char * function_line,bool * is_multiple,uint64_t * address,uint64_t * size,long * stack_param_size,char ** name)533 bool SymbolParseHelper::ParseFunction(char *function_line, bool *is_multiple,
534 uint64_t *address, uint64_t *size,
535 long *stack_param_size, char **name) {
536 // FUNC [<multiple>] <address> <size> <stack_param_size> <name>
537 assert(strncmp(function_line, "FUNC ", 5) == 0);
538 function_line += 5; // skip prefix
539
540 vector<char*> tokens;
541 if (!TokenizeWithOptionalField(function_line, "m", kWhitespace, 5, &tokens)) {
542 return false;
543 }
544
545 *is_multiple = strcmp(tokens[0], "m") == 0;
546 int next_token = *is_multiple ? 1 : 0;
547
548 char *after_number;
549 *address = strtoull(tokens[next_token++], &after_number, 16);
550 if (!IsValidAfterNumber(after_number) ||
551 *address == std::numeric_limits<unsigned long long>::max()) {
552 return false;
553 }
554 *size = strtoull(tokens[next_token++], &after_number, 16);
555 if (!IsValidAfterNumber(after_number) ||
556 *size == std::numeric_limits<unsigned long long>::max()) {
557 return false;
558 }
559 *stack_param_size = strtol(tokens[next_token++], &after_number, 16);
560 if (!IsValidAfterNumber(after_number) ||
561 *stack_param_size == std::numeric_limits<long>::max() ||
562 *stack_param_size < 0) {
563 return false;
564 }
565 *name = tokens[next_token++];
566
567 return true;
568 }
569
570 // static
ParseLine(char * line_line,uint64_t * address,uint64_t * size,long * line_number,long * source_file)571 bool SymbolParseHelper::ParseLine(char *line_line, uint64_t *address,
572 uint64_t *size, long *line_number,
573 long *source_file) {
574 // <address> <size> <line number> <source file id>
575 vector<char*> tokens;
576 if (!Tokenize(line_line, kWhitespace, 4, &tokens)) {
577 return false;
578 }
579
580 char *after_number;
581 *address = strtoull(tokens[0], &after_number, 16);
582 if (!IsValidAfterNumber(after_number) ||
583 *address == std::numeric_limits<unsigned long long>::max()) {
584 return false;
585 }
586 *size = strtoull(tokens[1], &after_number, 16);
587 if (!IsValidAfterNumber(after_number) ||
588 *size == std::numeric_limits<unsigned long long>::max()) {
589 return false;
590 }
591 *line_number = strtol(tokens[2], &after_number, 10);
592 if (!IsValidAfterNumber(after_number) ||
593 *line_number == std::numeric_limits<long>::max()) {
594 return false;
595 }
596 *source_file = strtol(tokens[3], &after_number, 10);
597 if (!IsValidAfterNumber(after_number) || *source_file < 0 ||
598 *source_file == std::numeric_limits<long>::max()) {
599 return false;
600 }
601
602 // Valid line numbers normally start from 1, however there are functions that
603 // are associated with a source file but not associated with any line number
604 // (block helper function) and for such functions the symbol file contains 0
605 // for the line numbers. Hence, 0 should be treated as a valid line number.
606 // For more information on block helper functions, please, take a look at:
607 // http://clang.llvm.org/docs/Block-ABI-Apple.html
608 if (*line_number < 0) {
609 return false;
610 }
611
612 return true;
613 }
614
615 // static
ParsePublicSymbol(char * public_line,bool * is_multiple,uint64_t * address,long * stack_param_size,char ** name)616 bool SymbolParseHelper::ParsePublicSymbol(char *public_line, bool *is_multiple,
617 uint64_t *address,
618 long *stack_param_size,
619 char **name) {
620 // PUBLIC [<multiple>] <address> <stack_param_size> <name>
621 assert(strncmp(public_line, "PUBLIC ", 7) == 0);
622 public_line += 7; // skip prefix
623
624 vector<char*> tokens;
625 if (!TokenizeWithOptionalField(public_line, "m", kWhitespace, 4, &tokens)) {
626 return false;
627 }
628
629 *is_multiple = strcmp(tokens[0], "m") == 0;
630 int next_token = *is_multiple ? 1 : 0;
631
632 char *after_number;
633 *address = strtoull(tokens[next_token++], &after_number, 16);
634 if (!IsValidAfterNumber(after_number) ||
635 *address == std::numeric_limits<unsigned long long>::max()) {
636 return false;
637 }
638 *stack_param_size = strtol(tokens[next_token++], &after_number, 16);
639 if (!IsValidAfterNumber(after_number) ||
640 *stack_param_size == std::numeric_limits<long>::max() ||
641 *stack_param_size < 0) {
642 return false;
643 }
644 *name = tokens[next_token++];
645
646 return true;
647 }
648
649 // static
IsValidAfterNumber(char * after_number)650 bool SymbolParseHelper::IsValidAfterNumber(char *after_number) {
651 if (after_number != NULL && strchr(kWhitespace, *after_number) != NULL) {
652 return true;
653 }
654 return false;
655 }
656
657 } // namespace google_breakpad
658