• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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