|
1 // -*- mode: c++ -*- |
|
2 |
|
3 // Copyright (c) 2011, Google Inc. |
|
4 // All rights reserved. |
|
5 // |
|
6 // Redistribution and use in source and binary forms, with or without |
|
7 // modification, are permitted provided that the following conditions are |
|
8 // met: |
|
9 // |
|
10 // * Redistributions of source code must retain the above copyright |
|
11 // notice, this list of conditions and the following disclaimer. |
|
12 // * Redistributions in binary form must reproduce the above |
|
13 // copyright notice, this list of conditions and the following disclaimer |
|
14 // in the documentation and/or other materials provided with the |
|
15 // distribution. |
|
16 // * Neither the name of Google Inc. nor the names of its |
|
17 // contributors may be used to endorse or promote products derived from |
|
18 // this software without specific prior written permission. |
|
19 // |
|
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
31 |
|
32 // Author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> |
|
33 |
|
34 // dump_syms.mm: Create a symbol file for use with minidumps |
|
35 |
|
36 #include "common/mac/dump_syms.h" |
|
37 |
|
38 #include <Foundation/Foundation.h> |
|
39 #include <mach-o/arch.h> |
|
40 #include <mach-o/fat.h> |
|
41 #include <stdio.h> |
|
42 |
|
43 #include <ostream> |
|
44 #include <string> |
|
45 #include <vector> |
|
46 |
|
47 #include "common/dwarf/bytereader-inl.h" |
|
48 #include "common/dwarf/dwarf2reader.h" |
|
49 #include "common/dwarf_cfi_to_module.h" |
|
50 #include "common/dwarf_cu_to_module.h" |
|
51 #include "common/dwarf_line_to_module.h" |
|
52 #include "common/mac/file_id.h" |
|
53 #include "common/mac/arch_utilities.h" |
|
54 #include "common/mac/macho_reader.h" |
|
55 #include "common/module.h" |
|
56 #include "common/scoped_ptr.h" |
|
57 #include "common/stabs_reader.h" |
|
58 #include "common/stabs_to_module.h" |
|
59 #include "common/symbol_data.h" |
|
60 |
|
61 #ifndef CPU_TYPE_ARM |
|
62 #define CPU_TYPE_ARM (static_cast<cpu_type_t>(12)) |
|
63 #endif // CPU_TYPE_ARM |
|
64 |
|
65 using dwarf2reader::ByteReader; |
|
66 using google_breakpad::DwarfCUToModule; |
|
67 using google_breakpad::DwarfLineToModule; |
|
68 using google_breakpad::FileID; |
|
69 using google_breakpad::mach_o::FatReader; |
|
70 using google_breakpad::mach_o::Section; |
|
71 using google_breakpad::mach_o::Segment; |
|
72 using google_breakpad::Module; |
|
73 using google_breakpad::StabsReader; |
|
74 using google_breakpad::StabsToModule; |
|
75 using google_breakpad::scoped_ptr; |
|
76 using std::make_pair; |
|
77 using std::pair; |
|
78 using std::string; |
|
79 using std::vector; |
|
80 |
|
81 namespace google_breakpad { |
|
82 |
|
83 bool DumpSymbols::Read(NSString *filename) { |
|
84 if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) { |
|
85 fprintf(stderr, "Object file does not exist: %s\n", |
|
86 [filename fileSystemRepresentation]); |
|
87 return false; |
|
88 } |
|
89 |
|
90 input_pathname_ = [filename retain]; |
|
91 |
|
92 // Does this filename refer to a dSYM bundle? |
|
93 NSBundle *bundle = [NSBundle bundleWithPath:input_pathname_]; |
|
94 |
|
95 if (bundle) { |
|
96 // Filenames referring to bundles usually have names of the form |
|
97 // "<basename>.dSYM"; however, if the user has specified a wrapper |
|
98 // suffix (the WRAPPER_SUFFIX and WRAPPER_EXTENSION build settings), |
|
99 // then the name may have the form "<basename>.<extension>.dSYM". In |
|
100 // either case, the resource name for the file containing the DWARF |
|
101 // info within the bundle is <basename>. |
|
102 // |
|
103 // Since there's no way to tell how much to strip off, remove one |
|
104 // extension at a time, and use the first one that |
|
105 // pathForResource:ofType:inDirectory likes. |
|
106 NSString *base_name = [input_pathname_ lastPathComponent]; |
|
107 NSString *dwarf_resource; |
|
108 |
|
109 do { |
|
110 NSString *new_base_name = [base_name stringByDeletingPathExtension]; |
|
111 |
|
112 // If stringByDeletingPathExtension returned the name unchanged, then |
|
113 // there's nothing more for us to strip off --- lose. |
|
114 if ([new_base_name isEqualToString:base_name]) { |
|
115 fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n", |
|
116 [input_pathname_ fileSystemRepresentation]); |
|
117 return false; |
|
118 } |
|
119 |
|
120 // Take the shortened result as our new base_name. |
|
121 base_name = new_base_name; |
|
122 |
|
123 // Try to find a DWARF resource in the bundle under the new base_name. |
|
124 dwarf_resource = [bundle pathForResource:base_name |
|
125 ofType:nil inDirectory:@"DWARF"]; |
|
126 } while (!dwarf_resource); |
|
127 |
|
128 object_filename_ = [dwarf_resource retain]; |
|
129 } else { |
|
130 object_filename_ = [input_pathname_ retain]; |
|
131 } |
|
132 |
|
133 // Read the file's contents into memory. |
|
134 // |
|
135 // The documentation for dataWithContentsOfMappedFile says: |
|
136 // |
|
137 // Because of file mapping restrictions, this method should only be |
|
138 // used if the file is guaranteed to exist for the duration of the |
|
139 // data object’s existence. It is generally safer to use the |
|
140 // dataWithContentsOfFile: method. |
|
141 // |
|
142 // I gather this means that OS X doesn't have (or at least, that method |
|
143 // doesn't use) a form of mapping like Linux's MAP_PRIVATE, where the |
|
144 // process appears to get its own copy of the data, and changes to the |
|
145 // file don't affect memory and vice versa). |
|
146 NSError *error; |
|
147 contents_ = [NSData dataWithContentsOfFile:object_filename_ |
|
148 options:0 |
|
149 error:&error]; |
|
150 if (!contents_) { |
|
151 fprintf(stderr, "Error reading object file: %s: %s\n", |
|
152 [object_filename_ fileSystemRepresentation], |
|
153 [[error localizedDescription] UTF8String]); |
|
154 return false; |
|
155 } |
|
156 [contents_ retain]; |
|
157 |
|
158 // Get the list of object files present in the file. |
|
159 FatReader::Reporter fat_reporter([object_filename_ |
|
160 fileSystemRepresentation]); |
|
161 FatReader fat_reader(&fat_reporter); |
|
162 if (!fat_reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes]), |
|
163 [contents_ length])) { |
|
164 return false; |
|
165 } |
|
166 |
|
167 // Get our own copy of fat_reader's object file list. |
|
168 size_t object_files_count; |
|
169 const struct fat_arch *object_files = |
|
170 fat_reader.object_files(&object_files_count); |
|
171 if (object_files_count == 0) { |
|
172 fprintf(stderr, "Fat binary file contains *no* architectures: %s\n", |
|
173 [object_filename_ fileSystemRepresentation]); |
|
174 return false; |
|
175 } |
|
176 object_files_.resize(object_files_count); |
|
177 memcpy(&object_files_[0], object_files, |
|
178 sizeof(struct fat_arch) * object_files_count); |
|
179 |
|
180 return true; |
|
181 } |
|
182 |
|
183 bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type, |
|
184 cpu_subtype_t cpu_subtype) { |
|
185 // Find the best match for the architecture the user requested. |
|
186 const struct fat_arch *best_match |
|
187 = NXFindBestFatArch(cpu_type, cpu_subtype, &object_files_[0], |
|
188 static_cast<uint32_t>(object_files_.size())); |
|
189 if (!best_match) return false; |
|
190 |
|
191 // Record the selected object file. |
|
192 selected_object_file_ = best_match; |
|
193 return true; |
|
194 } |
|
195 |
|
196 bool DumpSymbols::SetArchitecture(const std::string &arch_name) { |
|
197 bool arch_set = false; |
|
198 const NXArchInfo *arch_info = |
|
199 google_breakpad::BreakpadGetArchInfoFromName(arch_name.c_str()); |
|
200 if (arch_info) { |
|
201 arch_set = SetArchitecture(arch_info->cputype, arch_info->cpusubtype); |
|
202 } |
|
203 return arch_set; |
|
204 } |
|
205 |
|
206 string DumpSymbols::Identifier() { |
|
207 FileID file_id([object_filename_ fileSystemRepresentation]); |
|
208 unsigned char identifier_bytes[16]; |
|
209 cpu_type_t cpu_type = selected_object_file_->cputype; |
|
210 cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype; |
|
211 if (!file_id.MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) { |
|
212 fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n", |
|
213 [object_filename_ fileSystemRepresentation]); |
|
214 return ""; |
|
215 } |
|
216 |
|
217 char identifier_string[40]; |
|
218 FileID::ConvertIdentifierToString(identifier_bytes, identifier_string, |
|
219 sizeof(identifier_string)); |
|
220 |
|
221 string compacted(identifier_string); |
|
222 for(size_t i = compacted.find('-'); i != string::npos; |
|
223 i = compacted.find('-', i)) |
|
224 compacted.erase(i, 1); |
|
225 |
|
226 return compacted; |
|
227 } |
|
228 |
|
229 // A line-to-module loader that accepts line number info parsed by |
|
230 // dwarf2reader::LineInfo and populates a Module and a line vector |
|
231 // with the results. |
|
232 class DumpSymbols::DumperLineToModule: |
|
233 public DwarfCUToModule::LineToModuleHandler { |
|
234 public: |
|
235 // Create a line-to-module converter using BYTE_READER. |
|
236 DumperLineToModule(dwarf2reader::ByteReader *byte_reader) |
|
237 : byte_reader_(byte_reader) { } |
|
238 |
|
239 void StartCompilationUnit(const string& compilation_dir) { |
|
240 compilation_dir_ = compilation_dir; |
|
241 } |
|
242 |
|
243 void ReadProgram(const char *program, uint64 length, |
|
244 Module *module, vector<Module::Line> *lines) { |
|
245 DwarfLineToModule handler(module, compilation_dir_, lines); |
|
246 dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); |
|
247 parser.Start(); |
|
248 } |
|
249 private: |
|
250 string compilation_dir_; |
|
251 dwarf2reader::ByteReader *byte_reader_; // WEAK |
|
252 }; |
|
253 |
|
254 bool DumpSymbols::ReadDwarf(google_breakpad::Module *module, |
|
255 const mach_o::Reader &macho_reader, |
|
256 const mach_o::SectionMap &dwarf_sections) const { |
|
257 // Build a byte reader of the appropriate endianness. |
|
258 ByteReader byte_reader(macho_reader.big_endian() |
|
259 ? dwarf2reader::ENDIANNESS_BIG |
|
260 : dwarf2reader::ENDIANNESS_LITTLE); |
|
261 |
|
262 // Construct a context for this file. |
|
263 DwarfCUToModule::FileContext file_context(selected_object_name_, |
|
264 module); |
|
265 |
|
266 // Build a dwarf2reader::SectionMap from our mach_o::SectionMap. |
|
267 for (mach_o::SectionMap::const_iterator it = dwarf_sections.begin(); |
|
268 it != dwarf_sections.end(); it++) { |
|
269 file_context.section_map[it->first] = |
|
270 make_pair(reinterpret_cast<const char *>(it->second.contents.start), |
|
271 it->second.contents.Size()); |
|
272 } |
|
273 |
|
274 // Find the __debug_info section. |
|
275 std::pair<const char *, uint64> debug_info_section |
|
276 = file_context.section_map["__debug_info"]; |
|
277 // There had better be a __debug_info section! |
|
278 if (!debug_info_section.first) { |
|
279 fprintf(stderr, "%s: __DWARF segment of file has no __debug_info section\n", |
|
280 selected_object_name_.c_str()); |
|
281 return false; |
|
282 } |
|
283 |
|
284 // Build a line-to-module loader for the root handler to use. |
|
285 DumperLineToModule line_to_module(&byte_reader); |
|
286 |
|
287 // Walk the __debug_info section, one compilation unit at a time. |
|
288 uint64 debug_info_length = debug_info_section.second; |
|
289 for (uint64 offset = 0; offset < debug_info_length;) { |
|
290 // Make a handler for the root DIE that populates MODULE with the |
|
291 // debug info. |
|
292 DwarfCUToModule::WarningReporter reporter(selected_object_name_, |
|
293 offset); |
|
294 DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter); |
|
295 // Make a Dwarf2Handler that drives our DIEHandler. |
|
296 dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); |
|
297 // Make a DWARF parser for the compilation unit at OFFSET. |
|
298 dwarf2reader::CompilationUnit dwarf_reader(file_context.section_map, |
|
299 offset, |
|
300 &byte_reader, |
|
301 &die_dispatcher); |
|
302 // Process the entire compilation unit; get the offset of the next. |
|
303 offset += dwarf_reader.Start(); |
|
304 } |
|
305 |
|
306 return true; |
|
307 } |
|
308 |
|
309 bool DumpSymbols::ReadCFI(google_breakpad::Module *module, |
|
310 const mach_o::Reader &macho_reader, |
|
311 const mach_o::Section §ion, |
|
312 bool eh_frame) const { |
|
313 // Find the appropriate set of register names for this file's |
|
314 // architecture. |
|
315 vector<const UniqueString*> register_names; |
|
316 switch (macho_reader.cpu_type()) { |
|
317 case CPU_TYPE_X86: |
|
318 register_names = DwarfCFIToModule::RegisterNames::I386(); |
|
319 break; |
|
320 case CPU_TYPE_X86_64: |
|
321 register_names = DwarfCFIToModule::RegisterNames::X86_64(); |
|
322 break; |
|
323 case CPU_TYPE_ARM: |
|
324 register_names = DwarfCFIToModule::RegisterNames::ARM(); |
|
325 break; |
|
326 default: { |
|
327 const NXArchInfo *arch = google_breakpad::BreakpadGetArchInfoFromCpuType( |
|
328 macho_reader.cpu_type(), macho_reader.cpu_subtype()); |
|
329 fprintf(stderr, "%s: cannot convert DWARF call frame information for ", |
|
330 selected_object_name_.c_str()); |
|
331 if (arch) |
|
332 fprintf(stderr, "architecture '%s'", arch->name); |
|
333 else |
|
334 fprintf(stderr, "architecture %d,%d", |
|
335 macho_reader.cpu_type(), macho_reader.cpu_subtype()); |
|
336 fprintf(stderr, " to Breakpad symbol file: no register name table\n"); |
|
337 return false; |
|
338 } |
|
339 } |
|
340 |
|
341 // Find the call frame information and its size. |
|
342 const char *cfi = reinterpret_cast<const char *>(section.contents.start); |
|
343 size_t cfi_size = section.contents.Size(); |
|
344 |
|
345 // Plug together the parser, handler, and their entourages. |
|
346 DwarfCFIToModule::Reporter module_reporter(selected_object_name_, |
|
347 section.section_name); |
|
348 DwarfCFIToModule handler(module, register_names, &module_reporter); |
|
349 dwarf2reader::ByteReader byte_reader(macho_reader.big_endian() ? |
|
350 dwarf2reader::ENDIANNESS_BIG : |
|
351 dwarf2reader::ENDIANNESS_LITTLE); |
|
352 byte_reader.SetAddressSize(macho_reader.bits_64() ? 8 : 4); |
|
353 // At the moment, according to folks at Apple and some cursory |
|
354 // investigation, Mac OS X only uses DW_EH_PE_pcrel-based pointers, so |
|
355 // this is the only base address the CFI parser will need. |
|
356 byte_reader.SetCFIDataBase(section.address, cfi); |
|
357 |
|
358 dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(selected_object_name_, |
|
359 section.section_name); |
|
360 dwarf2reader::CallFrameInfo parser(cfi, cfi_size, |
|
361 &byte_reader, &handler, &dwarf_reporter, |
|
362 eh_frame); |
|
363 parser.Start(); |
|
364 return true; |
|
365 } |
|
366 |
|
367 // A LoadCommandHandler that loads whatever debugging data it finds into a |
|
368 // Module. |
|
369 class DumpSymbols::LoadCommandDumper: |
|
370 public mach_o::Reader::LoadCommandHandler { |
|
371 public: |
|
372 // Create a load command dumper handling load commands from READER's |
|
373 // file, and adding data to MODULE. |
|
374 LoadCommandDumper(const DumpSymbols &dumper, |
|
375 google_breakpad::Module *module, |
|
376 const mach_o::Reader &reader, |
|
377 SymbolData symbol_data) |
|
378 : dumper_(dumper), |
|
379 module_(module), |
|
380 reader_(reader), |
|
381 symbol_data_(symbol_data) { } |
|
382 |
|
383 bool SegmentCommand(const mach_o::Segment &segment); |
|
384 bool SymtabCommand(const ByteBuffer &entries, const ByteBuffer &strings); |
|
385 |
|
386 private: |
|
387 const DumpSymbols &dumper_; |
|
388 google_breakpad::Module *module_; // WEAK |
|
389 const mach_o::Reader &reader_; |
|
390 const SymbolData symbol_data_; |
|
391 }; |
|
392 |
|
393 bool DumpSymbols::LoadCommandDumper::SegmentCommand(const Segment &segment) { |
|
394 mach_o::SectionMap section_map; |
|
395 if (!reader_.MapSegmentSections(segment, §ion_map)) |
|
396 return false; |
|
397 |
|
398 if (segment.name == "__TEXT" && symbol_data_ != NO_CFI) { |
|
399 module_->SetLoadAddress(segment.vmaddr); |
|
400 mach_o::SectionMap::const_iterator eh_frame = |
|
401 section_map.find("__eh_frame"); |
|
402 if (eh_frame != section_map.end()) { |
|
403 // If there is a problem reading this, don't treat it as a fatal error. |
|
404 dumper_.ReadCFI(module_, reader_, eh_frame->second, true); |
|
405 } |
|
406 return true; |
|
407 } |
|
408 |
|
409 if (segment.name == "__DWARF") { |
|
410 if (symbol_data_ != ONLY_CFI) { |
|
411 if (!dumper_.ReadDwarf(module_, reader_, section_map)) |
|
412 return false; |
|
413 } |
|
414 if (symbol_data_ != NO_CFI) { |
|
415 mach_o::SectionMap::const_iterator debug_frame |
|
416 = section_map.find("__debug_frame"); |
|
417 if (debug_frame != section_map.end()) { |
|
418 // If there is a problem reading this, don't treat it as a fatal error. |
|
419 dumper_.ReadCFI(module_, reader_, debug_frame->second, false); |
|
420 } |
|
421 } |
|
422 } |
|
423 |
|
424 return true; |
|
425 } |
|
426 |
|
427 bool DumpSymbols::LoadCommandDumper::SymtabCommand(const ByteBuffer &entries, |
|
428 const ByteBuffer &strings) { |
|
429 StabsToModule stabs_to_module(module_); |
|
430 // Mac OS X STABS are never "unitized", and the size of the 'value' field |
|
431 // matches the address size of the executable. |
|
432 StabsReader stabs_reader(entries.start, entries.Size(), |
|
433 strings.start, strings.Size(), |
|
434 reader_.big_endian(), |
|
435 reader_.bits_64() ? 8 : 4, |
|
436 true, |
|
437 &stabs_to_module); |
|
438 if (!stabs_reader.Process()) |
|
439 return false; |
|
440 stabs_to_module.Finalize(); |
|
441 return true; |
|
442 } |
|
443 |
|
444 bool DumpSymbols::ReadSymbolData(Module** out_module) { |
|
445 // Select an object file, if SetArchitecture hasn't been called to set one |
|
446 // explicitly. |
|
447 if (!selected_object_file_) { |
|
448 // If there's only one architecture, that's the one. |
|
449 if (object_files_.size() == 1) |
|
450 selected_object_file_ = &object_files_[0]; |
|
451 else { |
|
452 // Look for an object file whose architecture matches our own. |
|
453 const NXArchInfo *local_arch = NXGetLocalArchInfo(); |
|
454 if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) { |
|
455 fprintf(stderr, "%s: object file contains more than one" |
|
456 " architecture, none of which match the current" |
|
457 " architecture; specify an architecture explicitly" |
|
458 " with '-a ARCH' to resolve the ambiguity\n", |
|
459 [object_filename_ fileSystemRepresentation]); |
|
460 return false; |
|
461 } |
|
462 } |
|
463 } |
|
464 |
|
465 assert(selected_object_file_); |
|
466 |
|
467 // Find the name of the selected file's architecture, to appear in |
|
468 // the MODULE record and in error messages. |
|
469 const NXArchInfo *selected_arch_info = |
|
470 google_breakpad::BreakpadGetArchInfoFromCpuType( |
|
471 selected_object_file_->cputype, selected_object_file_->cpusubtype); |
|
472 |
|
473 const char *selected_arch_name = selected_arch_info->name; |
|
474 if (strcmp(selected_arch_name, "i386") == 0) |
|
475 selected_arch_name = "x86"; |
|
476 |
|
477 // Produce a name to use in error messages that includes the |
|
478 // filename, and the architecture, if there is more than one. |
|
479 selected_object_name_ = [object_filename_ UTF8String]; |
|
480 if (object_files_.size() > 1) { |
|
481 selected_object_name_ += ", architecture "; |
|
482 selected_object_name_ + selected_arch_name; |
|
483 } |
|
484 |
|
485 // Compute a module name, to appear in the MODULE record. |
|
486 NSString *module_name = [object_filename_ lastPathComponent]; |
|
487 |
|
488 // Choose an identifier string, to appear in the MODULE record. |
|
489 string identifier = Identifier(); |
|
490 if (identifier.empty()) |
|
491 return false; |
|
492 identifier += "0"; |
|
493 |
|
494 // Create a module to hold the debugging information. |
|
495 scoped_ptr<Module> module(new Module([module_name UTF8String], |
|
496 "mac", |
|
497 selected_arch_name, |
|
498 identifier)); |
|
499 |
|
500 // Parse the selected object file. |
|
501 mach_o::Reader::Reporter reporter(selected_object_name_); |
|
502 mach_o::Reader reader(&reporter); |
|
503 if (!reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes]) |
|
504 + selected_object_file_->offset, |
|
505 selected_object_file_->size, |
|
506 selected_object_file_->cputype, |
|
507 selected_object_file_->cpusubtype)) |
|
508 return false; |
|
509 |
|
510 // Walk its load commands, and deal with whatever is there. |
|
511 LoadCommandDumper load_command_dumper(*this, module.get(), reader, |
|
512 symbol_data_); |
|
513 if (!reader.WalkLoadCommands(&load_command_dumper)) |
|
514 return false; |
|
515 |
|
516 *out_module = module.release(); |
|
517 |
|
518 return true; |
|
519 } |
|
520 |
|
521 bool DumpSymbols::WriteSymbolFile(std::ostream &stream) { |
|
522 Module* module = NULL; |
|
523 |
|
524 if (ReadSymbolData(&module) && module) { |
|
525 bool res = module->Write(stream, symbol_data_); |
|
526 delete module; |
|
527 return res; |
|
528 } |
|
529 |
|
530 return false; |
|
531 } |
|
532 |
|
533 } // namespace google_breakpad |