OLD | NEW |
| (Empty) |
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 <assert.h> | |
39 #include <Foundation/Foundation.h> | |
40 #include <mach-o/arch.h> | |
41 #include <mach-o/fat.h> | |
42 #include <stdio.h> | |
43 | |
44 #include <ostream> | |
45 #include <string> | |
46 #include <vector> | |
47 | |
48 #include "common/dwarf/bytereader-inl.h" | |
49 #include "common/dwarf/dwarf2reader.h" | |
50 #include "common/dwarf_cfi_to_module.h" | |
51 #include "common/dwarf_cu_to_module.h" | |
52 #include "common/dwarf_line_to_module.h" | |
53 #include "common/mac/file_id.h" | |
54 #include "common/mac/arch_utilities.h" | |
55 #include "common/mac/macho_reader.h" | |
56 #include "common/module.h" | |
57 #include "common/scoped_ptr.h" | |
58 #include "common/stabs_reader.h" | |
59 #include "common/stabs_to_module.h" | |
60 #include "common/symbol_data.h" | |
61 | |
62 #ifndef CPU_TYPE_ARM | |
63 #define CPU_TYPE_ARM (static_cast<cpu_type_t>(12)) | |
64 #endif // CPU_TYPE_ARM | |
65 | |
66 #ifndef CPU_TYPE_ARM64 | |
67 #define CPU_TYPE_ARM64 (static_cast<cpu_type_t>(16777228)) | |
68 #endif // CPU_TYPE_ARM64 | |
69 | |
70 using dwarf2reader::ByteReader; | |
71 using google_breakpad::DwarfCUToModule; | |
72 using google_breakpad::DwarfLineToModule; | |
73 using google_breakpad::FileID; | |
74 using google_breakpad::mach_o::FatReader; | |
75 using google_breakpad::mach_o::Section; | |
76 using google_breakpad::mach_o::Segment; | |
77 using google_breakpad::Module; | |
78 using google_breakpad::StabsReader; | |
79 using google_breakpad::StabsToModule; | |
80 using google_breakpad::scoped_ptr; | |
81 using std::make_pair; | |
82 using std::pair; | |
83 using std::string; | |
84 using std::vector; | |
85 | |
86 namespace google_breakpad { | |
87 | |
88 bool DumpSymbols::Read(NSString *filename) { | |
89 if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) { | |
90 fprintf(stderr, "Object file does not exist: %s\n", | |
91 [filename fileSystemRepresentation]); | |
92 return false; | |
93 } | |
94 | |
95 input_pathname_ = [filename retain]; | |
96 | |
97 // Does this filename refer to a dSYM bundle? | |
98 NSBundle *bundle = [NSBundle bundleWithPath:input_pathname_]; | |
99 | |
100 if (bundle) { | |
101 // Filenames referring to bundles usually have names of the form | |
102 // "<basename>.dSYM"; however, if the user has specified a wrapper | |
103 // suffix (the WRAPPER_SUFFIX and WRAPPER_EXTENSION build settings), | |
104 // then the name may have the form "<basename>.<extension>.dSYM". In | |
105 // either case, the resource name for the file containing the DWARF | |
106 // info within the bundle is <basename>. | |
107 // | |
108 // Since there's no way to tell how much to strip off, remove one | |
109 // extension at a time, and use the first one that | |
110 // pathForResource:ofType:inDirectory likes. | |
111 NSString *base_name = [input_pathname_ lastPathComponent]; | |
112 NSString *dwarf_resource; | |
113 | |
114 do { | |
115 NSString *new_base_name = [base_name stringByDeletingPathExtension]; | |
116 | |
117 // If stringByDeletingPathExtension returned the name unchanged, then | |
118 // there's nothing more for us to strip off --- lose. | |
119 if ([new_base_name isEqualToString:base_name]) { | |
120 fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n", | |
121 [input_pathname_ fileSystemRepresentation]); | |
122 return false; | |
123 } | |
124 | |
125 // Take the shortened result as our new base_name. | |
126 base_name = new_base_name; | |
127 | |
128 // Try to find a DWARF resource in the bundle under the new base_name. | |
129 dwarf_resource = [bundle pathForResource:base_name | |
130 ofType:nil inDirectory:@"DWARF"]; | |
131 } while (!dwarf_resource); | |
132 | |
133 object_filename_ = [dwarf_resource retain]; | |
134 } else { | |
135 object_filename_ = [input_pathname_ retain]; | |
136 } | |
137 | |
138 // Read the file's contents into memory. | |
139 // | |
140 // The documentation for dataWithContentsOfMappedFile says: | |
141 // | |
142 // Because of file mapping restrictions, this method should only be | |
143 // used if the file is guaranteed to exist for the duration of the | |
144 // data object’s existence. It is generally safer to use the | |
145 // dataWithContentsOfFile: method. | |
146 // | |
147 // I gather this means that OS X doesn't have (or at least, that method | |
148 // doesn't use) a form of mapping like Linux's MAP_PRIVATE, where the | |
149 // process appears to get its own copy of the data, and changes to the | |
150 // file don't affect memory and vice versa). | |
151 NSError *error; | |
152 contents_ = [NSData dataWithContentsOfFile:object_filename_ | |
153 options:0 | |
154 error:&error]; | |
155 if (!contents_) { | |
156 fprintf(stderr, "Error reading object file: %s: %s\n", | |
157 [object_filename_ fileSystemRepresentation], | |
158 [[error localizedDescription] UTF8String]); | |
159 return false; | |
160 } | |
161 [contents_ retain]; | |
162 | |
163 // Get the list of object files present in the file. | |
164 FatReader::Reporter fat_reporter([object_filename_ | |
165 fileSystemRepresentation]); | |
166 FatReader fat_reader(&fat_reporter); | |
167 if (!fat_reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes]), | |
168 [contents_ length])) { | |
169 return false; | |
170 } | |
171 | |
172 // Get our own copy of fat_reader's object file list. | |
173 size_t object_files_count; | |
174 const SuperFatArch *object_files = | |
175 fat_reader.object_files(&object_files_count); | |
176 if (object_files_count == 0) { | |
177 fprintf(stderr, "Fat binary file contains *no* architectures: %s\n", | |
178 [object_filename_ fileSystemRepresentation]); | |
179 return false; | |
180 } | |
181 object_files_.resize(object_files_count); | |
182 memcpy(&object_files_[0], object_files, | |
183 sizeof(SuperFatArch) * object_files_count); | |
184 | |
185 return true; | |
186 } | |
187 | |
188 bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type, | |
189 cpu_subtype_t cpu_subtype) { | |
190 // Find the best match for the architecture the user requested. | |
191 const SuperFatArch *best_match = FindBestMatchForArchitecture( | |
192 cpu_type, cpu_subtype); | |
193 if (!best_match) return false; | |
194 | |
195 // Record the selected object file. | |
196 selected_object_file_ = best_match; | |
197 return true; | |
198 } | |
199 | |
200 bool DumpSymbols::SetArchitecture(const std::string &arch_name) { | |
201 bool arch_set = false; | |
202 const NXArchInfo *arch_info = | |
203 google_breakpad::BreakpadGetArchInfoFromName(arch_name.c_str()); | |
204 if (arch_info) { | |
205 arch_set = SetArchitecture(arch_info->cputype, arch_info->cpusubtype); | |
206 } | |
207 return arch_set; | |
208 } | |
209 | |
210 SuperFatArch* DumpSymbols::FindBestMatchForArchitecture( | |
211 cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) { | |
212 // Check if all the object files can be converted to struct fat_arch. | |
213 bool can_convert_to_fat_arch = true; | |
214 vector<struct fat_arch> fat_arch_vector; | |
215 for (vector<SuperFatArch>::const_iterator it = object_files_.begin(); | |
216 it != object_files_.end(); | |
217 ++it) { | |
218 struct fat_arch arch; | |
219 bool success = it->ConvertToFatArch(&arch); | |
220 if (!success) { | |
221 can_convert_to_fat_arch = false; | |
222 break; | |
223 } | |
224 fat_arch_vector.push_back(arch); | |
225 } | |
226 | |
227 // If all the object files can be converted to struct fat_arch, use | |
228 // NXFindBestFatArch. | |
229 if (can_convert_to_fat_arch) { | |
230 const struct fat_arch *best_match | |
231 = NXFindBestFatArch(cpu_type, cpu_subtype, &fat_arch_vector[0], | |
232 static_cast<uint32_t>(fat_arch_vector.size())); | |
233 | |
234 for (size_t i = 0; i < fat_arch_vector.size(); ++i) { | |
235 if (best_match == &fat_arch_vector[i]) | |
236 return &object_files_[i]; | |
237 } | |
238 assert(best_match == NULL); | |
239 return NULL; | |
240 } | |
241 | |
242 // Check for an exact match with cpu_type and cpu_subtype. | |
243 for (vector<SuperFatArch>::iterator it = object_files_.begin(); | |
244 it != object_files_.end(); | |
245 ++it) { | |
246 if (it->cputype == cpu_type && it->cpusubtype == cpu_subtype) | |
247 return &*it; | |
248 } | |
249 | |
250 // No exact match found. | |
251 // TODO(erikchen): If it becomes necessary, we can copy the implementation of | |
252 // NXFindBestFatArch, located at | |
253 // http://web.mit.edu/darwin/src/modules/cctools/libmacho/arch.c. | |
254 fprintf(stderr, "Failed to find an exact match for an object file with cpu " | |
255 "type: %d and cpu subtype: %d. Furthermore, at least one object file is " | |
256 "larger than 2**32.\n", cpu_type, cpu_subtype); | |
257 return NULL; | |
258 } | |
259 | |
260 string DumpSymbols::Identifier() { | |
261 FileID file_id([object_filename_ fileSystemRepresentation]); | |
262 unsigned char identifier_bytes[16]; | |
263 cpu_type_t cpu_type = selected_object_file_->cputype; | |
264 cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype; | |
265 if (!file_id.MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) { | |
266 fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n", | |
267 [object_filename_ fileSystemRepresentation]); | |
268 return ""; | |
269 } | |
270 | |
271 char identifier_string[40]; | |
272 FileID::ConvertIdentifierToString(identifier_bytes, identifier_string, | |
273 sizeof(identifier_string)); | |
274 | |
275 string compacted(identifier_string); | |
276 for(size_t i = compacted.find('-'); i != string::npos; | |
277 i = compacted.find('-', i)) | |
278 compacted.erase(i, 1); | |
279 | |
280 return compacted; | |
281 } | |
282 | |
283 // A line-to-module loader that accepts line number info parsed by | |
284 // dwarf2reader::LineInfo and populates a Module and a line vector | |
285 // with the results. | |
286 class DumpSymbols::DumperLineToModule: | |
287 public DwarfCUToModule::LineToModuleHandler { | |
288 public: | |
289 // Create a line-to-module converter using BYTE_READER. | |
290 DumperLineToModule(dwarf2reader::ByteReader *byte_reader) | |
291 : byte_reader_(byte_reader) { } | |
292 | |
293 void StartCompilationUnit(const string& compilation_dir) { | |
294 compilation_dir_ = compilation_dir; | |
295 } | |
296 | |
297 void ReadProgram(const char *program, uint64 length, | |
298 Module *module, vector<Module::Line> *lines) { | |
299 DwarfLineToModule handler(module, compilation_dir_, lines); | |
300 dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); | |
301 parser.Start(); | |
302 } | |
303 private: | |
304 string compilation_dir_; | |
305 dwarf2reader::ByteReader *byte_reader_; // WEAK | |
306 }; | |
307 | |
308 bool DumpSymbols::ReadDwarf(google_breakpad::Module *module, | |
309 const mach_o::Reader &macho_reader, | |
310 const mach_o::SectionMap &dwarf_sections, | |
311 bool handle_inter_cu_refs) const { | |
312 // Build a byte reader of the appropriate endianness. | |
313 ByteReader byte_reader(macho_reader.big_endian() | |
314 ? dwarf2reader::ENDIANNESS_BIG | |
315 : dwarf2reader::ENDIANNESS_LITTLE); | |
316 | |
317 // Construct a context for this file. | |
318 DwarfCUToModule::FileContext file_context(selected_object_name_, | |
319 module, | |
320 handle_inter_cu_refs); | |
321 | |
322 // Build a dwarf2reader::SectionMap from our mach_o::SectionMap. | |
323 for (mach_o::SectionMap::const_iterator it = dwarf_sections.begin(); | |
324 it != dwarf_sections.end(); ++it) { | |
325 file_context.AddSectionToSectionMap( | |
326 it->first, | |
327 reinterpret_cast<const char *>(it->second.contents.start), | |
328 it->second.contents.Size()); | |
329 } | |
330 | |
331 // Find the __debug_info section. | |
332 dwarf2reader::SectionMap::const_iterator debug_info_entry = | |
333 file_context.section_map().find("__debug_info"); | |
334 assert(debug_info_entry != file_context.section_map().end()); | |
335 const std::pair<const char*, uint64>& debug_info_section = | |
336 debug_info_entry->second; | |
337 // There had better be a __debug_info section! | |
338 if (!debug_info_section.first) { | |
339 fprintf(stderr, "%s: __DWARF segment of file has no __debug_info section\n", | |
340 selected_object_name_.c_str()); | |
341 return false; | |
342 } | |
343 | |
344 // Build a line-to-module loader for the root handler to use. | |
345 DumperLineToModule line_to_module(&byte_reader); | |
346 | |
347 // Walk the __debug_info section, one compilation unit at a time. | |
348 uint64 debug_info_length = debug_info_section.second; | |
349 for (uint64 offset = 0; offset < debug_info_length;) { | |
350 // Make a handler for the root DIE that populates MODULE with the | |
351 // debug info. | |
352 DwarfCUToModule::WarningReporter reporter(selected_object_name_, | |
353 offset); | |
354 DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter); | |
355 // Make a Dwarf2Handler that drives our DIEHandler. | |
356 dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); | |
357 // Make a DWARF parser for the compilation unit at OFFSET. | |
358 dwarf2reader::CompilationUnit dwarf_reader(file_context.section_map(), | |
359 offset, | |
360 &byte_reader, | |
361 &die_dispatcher); | |
362 // Process the entire compilation unit; get the offset of the next. | |
363 offset += dwarf_reader.Start(); | |
364 } | |
365 | |
366 return true; | |
367 } | |
368 | |
369 bool DumpSymbols::ReadCFI(google_breakpad::Module *module, | |
370 const mach_o::Reader &macho_reader, | |
371 const mach_o::Section §ion, | |
372 bool eh_frame) const { | |
373 // Find the appropriate set of register names for this file's | |
374 // architecture. | |
375 vector<string> register_names; | |
376 switch (macho_reader.cpu_type()) { | |
377 case CPU_TYPE_X86: | |
378 register_names = DwarfCFIToModule::RegisterNames::I386(); | |
379 break; | |
380 case CPU_TYPE_X86_64: | |
381 register_names = DwarfCFIToModule::RegisterNames::X86_64(); | |
382 break; | |
383 case CPU_TYPE_ARM: | |
384 register_names = DwarfCFIToModule::RegisterNames::ARM(); | |
385 break; | |
386 case CPU_TYPE_ARM64: | |
387 register_names = DwarfCFIToModule::RegisterNames::ARM64(); | |
388 break; | |
389 default: { | |
390 const NXArchInfo *arch = google_breakpad::BreakpadGetArchInfoFromCpuType( | |
391 macho_reader.cpu_type(), macho_reader.cpu_subtype()); | |
392 fprintf(stderr, "%s: cannot convert DWARF call frame information for ", | |
393 selected_object_name_.c_str()); | |
394 if (arch) | |
395 fprintf(stderr, "architecture '%s'", arch->name); | |
396 else | |
397 fprintf(stderr, "architecture %d,%d", | |
398 macho_reader.cpu_type(), macho_reader.cpu_subtype()); | |
399 fprintf(stderr, " to Breakpad symbol file: no register name table\n"); | |
400 return false; | |
401 } | |
402 } | |
403 | |
404 // Find the call frame information and its size. | |
405 const char *cfi = reinterpret_cast<const char *>(section.contents.start); | |
406 size_t cfi_size = section.contents.Size(); | |
407 | |
408 // Plug together the parser, handler, and their entourages. | |
409 DwarfCFIToModule::Reporter module_reporter(selected_object_name_, | |
410 section.section_name); | |
411 DwarfCFIToModule handler(module, register_names, &module_reporter); | |
412 dwarf2reader::ByteReader byte_reader(macho_reader.big_endian() ? | |
413 dwarf2reader::ENDIANNESS_BIG : | |
414 dwarf2reader::ENDIANNESS_LITTLE); | |
415 byte_reader.SetAddressSize(macho_reader.bits_64() ? 8 : 4); | |
416 // At the moment, according to folks at Apple and some cursory | |
417 // investigation, Mac OS X only uses DW_EH_PE_pcrel-based pointers, so | |
418 // this is the only base address the CFI parser will need. | |
419 byte_reader.SetCFIDataBase(section.address, cfi); | |
420 | |
421 dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(selected_object_name_, | |
422 section.section_name); | |
423 dwarf2reader::CallFrameInfo parser(cfi, cfi_size, | |
424 &byte_reader, &handler, &dwarf_reporter, | |
425 eh_frame); | |
426 parser.Start(); | |
427 return true; | |
428 } | |
429 | |
430 // A LoadCommandHandler that loads whatever debugging data it finds into a | |
431 // Module. | |
432 class DumpSymbols::LoadCommandDumper: | |
433 public mach_o::Reader::LoadCommandHandler { | |
434 public: | |
435 // Create a load command dumper handling load commands from READER's | |
436 // file, and adding data to MODULE. | |
437 LoadCommandDumper(const DumpSymbols &dumper, | |
438 google_breakpad::Module *module, | |
439 const mach_o::Reader &reader, | |
440 SymbolData symbol_data, | |
441 bool handle_inter_cu_refs) | |
442 : dumper_(dumper), | |
443 module_(module), | |
444 reader_(reader), | |
445 symbol_data_(symbol_data), | |
446 handle_inter_cu_refs_(handle_inter_cu_refs) { } | |
447 | |
448 bool SegmentCommand(const mach_o::Segment &segment); | |
449 bool SymtabCommand(const ByteBuffer &entries, const ByteBuffer &strings); | |
450 | |
451 private: | |
452 const DumpSymbols &dumper_; | |
453 google_breakpad::Module *module_; // WEAK | |
454 const mach_o::Reader &reader_; | |
455 const SymbolData symbol_data_; | |
456 const bool handle_inter_cu_refs_; | |
457 }; | |
458 | |
459 bool DumpSymbols::LoadCommandDumper::SegmentCommand(const Segment &segment) { | |
460 mach_o::SectionMap section_map; | |
461 if (!reader_.MapSegmentSections(segment, §ion_map)) | |
462 return false; | |
463 | |
464 if (segment.name == "__TEXT") { | |
465 module_->SetLoadAddress(segment.vmaddr); | |
466 if (symbol_data_ != NO_CFI) { | |
467 mach_o::SectionMap::const_iterator eh_frame = | |
468 section_map.find("__eh_frame"); | |
469 if (eh_frame != section_map.end()) { | |
470 // If there is a problem reading this, don't treat it as a fatal error. | |
471 dumper_.ReadCFI(module_, reader_, eh_frame->second, true); | |
472 } | |
473 } | |
474 return true; | |
475 } | |
476 | |
477 if (segment.name == "__DWARF") { | |
478 if (symbol_data_ != ONLY_CFI) { | |
479 if (!dumper_.ReadDwarf(module_, reader_, section_map, | |
480 handle_inter_cu_refs_)) { | |
481 return false; | |
482 } | |
483 } | |
484 if (symbol_data_ != NO_CFI) { | |
485 mach_o::SectionMap::const_iterator debug_frame | |
486 = section_map.find("__debug_frame"); | |
487 if (debug_frame != section_map.end()) { | |
488 // If there is a problem reading this, don't treat it as a fatal error. | |
489 dumper_.ReadCFI(module_, reader_, debug_frame->second, false); | |
490 } | |
491 } | |
492 } | |
493 | |
494 return true; | |
495 } | |
496 | |
497 bool DumpSymbols::LoadCommandDumper::SymtabCommand(const ByteBuffer &entries, | |
498 const ByteBuffer &strings) { | |
499 StabsToModule stabs_to_module(module_); | |
500 // Mac OS X STABS are never "unitized", and the size of the 'value' field | |
501 // matches the address size of the executable. | |
502 StabsReader stabs_reader(entries.start, entries.Size(), | |
503 strings.start, strings.Size(), | |
504 reader_.big_endian(), | |
505 reader_.bits_64() ? 8 : 4, | |
506 true, | |
507 &stabs_to_module); | |
508 if (!stabs_reader.Process()) | |
509 return false; | |
510 stabs_to_module.Finalize(); | |
511 return true; | |
512 } | |
513 | |
514 bool DumpSymbols::ReadSymbolData(Module** out_module) { | |
515 // Select an object file, if SetArchitecture hasn't been called to set one | |
516 // explicitly. | |
517 if (!selected_object_file_) { | |
518 // If there's only one architecture, that's the one. | |
519 if (object_files_.size() == 1) | |
520 selected_object_file_ = &object_files_[0]; | |
521 else { | |
522 // Look for an object file whose architecture matches our own. | |
523 const NXArchInfo *local_arch = NXGetLocalArchInfo(); | |
524 if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) { | |
525 fprintf(stderr, "%s: object file contains more than one" | |
526 " architecture, none of which match the current" | |
527 " architecture; specify an architecture explicitly" | |
528 " with '-a ARCH' to resolve the ambiguity\n", | |
529 [object_filename_ fileSystemRepresentation]); | |
530 return false; | |
531 } | |
532 } | |
533 } | |
534 | |
535 assert(selected_object_file_); | |
536 | |
537 // Find the name of the selected file's architecture, to appear in | |
538 // the MODULE record and in error messages. | |
539 const NXArchInfo *selected_arch_info = | |
540 google_breakpad::BreakpadGetArchInfoFromCpuType( | |
541 selected_object_file_->cputype, selected_object_file_->cpusubtype); | |
542 | |
543 const char *selected_arch_name = selected_arch_info->name; | |
544 if (strcmp(selected_arch_name, "i386") == 0) | |
545 selected_arch_name = "x86"; | |
546 | |
547 // Produce a name to use in error messages that includes the | |
548 // filename, and the architecture, if there is more than one. | |
549 selected_object_name_ = [object_filename_ UTF8String]; | |
550 if (object_files_.size() > 1) { | |
551 selected_object_name_ += ", architecture "; | |
552 selected_object_name_ + selected_arch_name; | |
553 } | |
554 | |
555 // Compute a module name, to appear in the MODULE record. | |
556 NSString *module_name = [object_filename_ lastPathComponent]; | |
557 | |
558 // Choose an identifier string, to appear in the MODULE record. | |
559 string identifier = Identifier(); | |
560 if (identifier.empty()) | |
561 return false; | |
562 identifier += "0"; | |
563 | |
564 // Create a module to hold the debugging information. | |
565 scoped_ptr<Module> module(new Module([module_name UTF8String], | |
566 "mac", | |
567 selected_arch_name, | |
568 identifier)); | |
569 | |
570 // Parse the selected object file. | |
571 mach_o::Reader::Reporter reporter(selected_object_name_); | |
572 mach_o::Reader reader(&reporter); | |
573 if (!reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes]) | |
574 + selected_object_file_->offset, | |
575 selected_object_file_->size, | |
576 selected_object_file_->cputype, | |
577 selected_object_file_->cpusubtype)) | |
578 return false; | |
579 | |
580 // Walk its load commands, and deal with whatever is there. | |
581 LoadCommandDumper load_command_dumper(*this, module.get(), reader, | |
582 symbol_data_, handle_inter_cu_refs_); | |
583 if (!reader.WalkLoadCommands(&load_command_dumper)) | |
584 return false; | |
585 | |
586 *out_module = module.release(); | |
587 | |
588 return true; | |
589 } | |
590 | |
591 bool DumpSymbols::WriteSymbolFile(std::ostream &stream) { | |
592 Module* module = NULL; | |
593 | |
594 if (ReadSymbolData(&module) && module) { | |
595 bool res = module->Write(stream, symbol_data_); | |
596 delete module; | |
597 return res; | |
598 } | |
599 | |
600 return false; | |
601 } | |
602 | |
603 } // namespace google_breakpad | |
OLD | NEW |