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.cc: Create a symbol file for use with minidumps |
35 | |
36 | #include "common/mac/dump_syms.h" |
37 | |
38 | #include <assert.h> |
39 | #include <dirent.h> |
40 | #include <errno.h> |
41 | #include <mach-o/arch.h> |
42 | #include <mach-o/fat.h> |
43 | #include <stdint.h> |
44 | #include <stdio.h> |
45 | #include <sys/stat.h> |
46 | #include <sys/types.h> |
47 | #include <unistd.h> |
48 | |
49 | #include <ostream> |
50 | #include <string> |
51 | #include <vector> |
52 | |
53 | #include "common/dwarf/bytereader-inl.h" |
54 | #include "common/dwarf/dwarf2reader.h" |
55 | #include "common/dwarf_cfi_to_module.h" |
56 | #include "common/dwarf_cu_to_module.h" |
57 | #include "common/dwarf_line_to_module.h" |
58 | #include "common/dwarf_range_list_handler.h" |
59 | #include "common/mac/file_id.h" |
60 | #include "common/mac/arch_utilities.h" |
61 | #include "common/mac/macho_reader.h" |
62 | #include "common/module.h" |
63 | #include "common/path_helper.h" |
64 | #include "common/scoped_ptr.h" |
65 | #include "common/stabs_reader.h" |
66 | #include "common/stabs_to_module.h" |
67 | #include "common/symbol_data.h" |
68 | |
69 | #ifndef CPU_TYPE_ARM |
70 | #define CPU_TYPE_ARM (static_cast<cpu_type_t>(12)) |
71 | #endif // CPU_TYPE_ARM |
72 | |
73 | #ifndef CPU_TYPE_ARM64 |
74 | #define CPU_TYPE_ARM64 (static_cast<cpu_type_t>(16777228)) |
75 | #endif // CPU_TYPE_ARM64 |
76 | |
77 | using google_breakpad::ByteReader; |
78 | using google_breakpad::DwarfCUToModule; |
79 | using google_breakpad::DwarfLineToModule; |
80 | using google_breakpad::DwarfRangeListHandler; |
81 | using google_breakpad::mach_o::FatReader; |
82 | using google_breakpad::mach_o::FileID; |
83 | using google_breakpad::mach_o::Section; |
84 | using google_breakpad::mach_o::Segment; |
85 | using google_breakpad::Module; |
86 | using google_breakpad::StabsReader; |
87 | using google_breakpad::StabsToModule; |
88 | using google_breakpad::scoped_ptr; |
89 | using std::make_pair; |
90 | using std::pair; |
91 | using std::string; |
92 | using std::vector; |
93 | |
94 | namespace { |
95 | // Return a vector<string> with absolute paths to all the entries |
96 | // in directory (excluding . and ..). |
97 | vector<string> list_directory(const string& directory) { |
98 | vector<string> entries; |
99 | DIR* dir = opendir(directory.c_str()); |
100 | if (!dir) { |
101 | return entries; |
102 | } |
103 | |
104 | string path = directory; |
105 | if (path[path.length() - 1] != '/') { |
106 | path += '/'; |
107 | } |
108 | |
109 | struct dirent* entry = NULL; |
110 | while ((entry = readdir(dir))) { |
111 | if (strcmp(entry->d_name, "." ) != 0 && strcmp(entry->d_name, ".." ) != 0) { |
112 | entries.push_back(path + entry->d_name); |
113 | } |
114 | } |
115 | |
116 | closedir(dir); |
117 | return entries; |
118 | } |
119 | } |
120 | |
121 | namespace google_breakpad { |
122 | |
123 | bool DumpSymbols::Read(const string& filename) { |
124 | struct stat st; |
125 | if (stat(filename.c_str(), &st) == -1) { |
126 | fprintf(stderr, "Could not access object file %s: %s\n" , |
127 | filename.c_str(), strerror(errno)); |
128 | return false; |
129 | } |
130 | |
131 | from_disk_ = true; |
132 | |
133 | // Does this filename refer to a dSYM bundle? |
134 | string contents_path = filename + "/Contents/Resources/DWARF" ; |
135 | string object_filename; |
136 | if (S_ISDIR(st.st_mode) && |
137 | access(contents_path.c_str(), F_OK) == 0) { |
138 | // If there's one file under Contents/Resources/DWARF then use that, |
139 | // otherwise bail out. |
140 | const vector<string> entries = list_directory(contents_path); |
141 | if (entries.size() == 0) { |
142 | fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n" , |
143 | filename.c_str()); |
144 | return false; |
145 | } |
146 | if (entries.size() > 1) { |
147 | fprintf(stderr, "Too many DWARF files in bundle: %s\n" , |
148 | filename.c_str()); |
149 | return false; |
150 | } |
151 | |
152 | object_filename = entries[0]; |
153 | } else { |
154 | object_filename = filename; |
155 | } |
156 | |
157 | // Read the file's contents into memory. |
158 | bool read_ok = true; |
159 | string error; |
160 | scoped_array<uint8_t> contents; |
161 | off_t total = 0; |
162 | if (stat(object_filename.c_str(), &st) != -1) { |
163 | FILE* f = fopen(object_filename.c_str(), "rb" ); |
164 | if (f) { |
165 | contents.reset(new uint8_t[st.st_size]); |
166 | while (total < st.st_size && !feof(f)) { |
167 | size_t read = fread(&contents[0] + total, 1, st.st_size - total, f); |
168 | if (read == 0) { |
169 | if (ferror(f)) { |
170 | read_ok = false; |
171 | error = strerror(errno); |
172 | } |
173 | break; |
174 | } |
175 | total += read; |
176 | } |
177 | fclose(f); |
178 | } else { |
179 | error = strerror(errno); |
180 | } |
181 | } |
182 | |
183 | if (!read_ok) { |
184 | fprintf(stderr, "Error reading object file: %s: %s\n" , |
185 | object_filename.c_str(), error.c_str()); |
186 | return false; |
187 | } |
188 | return ReadData(contents.release(), total, object_filename); |
189 | } |
190 | |
191 | bool DumpSymbols::ReadData(uint8_t* contents, size_t size, |
192 | const std::string& filename) { |
193 | contents_.reset(contents); |
194 | size_ = size; |
195 | object_filename_ = filename; |
196 | |
197 | // Get the list of object files present in the file. |
198 | FatReader::Reporter fat_reporter(object_filename_); |
199 | FatReader fat_reader(&fat_reporter); |
200 | if (!fat_reader.Read(contents_.get(), size)) { |
201 | return false; |
202 | } |
203 | |
204 | // Get our own copy of fat_reader's object file list. |
205 | size_t object_files_count; |
206 | const SuperFatArch* object_files = |
207 | fat_reader.object_files(&object_files_count); |
208 | if (object_files_count == 0) { |
209 | fprintf(stderr, "Fat binary file contains *no* architectures: %s\n" , |
210 | object_filename_.c_str()); |
211 | return false; |
212 | } |
213 | object_files_.resize(object_files_count); |
214 | memcpy(&object_files_[0], object_files, |
215 | sizeof(SuperFatArch) * object_files_count); |
216 | |
217 | return true; |
218 | } |
219 | |
220 | bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type, |
221 | cpu_subtype_t cpu_subtype) { |
222 | // Find the best match for the architecture the user requested. |
223 | const SuperFatArch* best_match = FindBestMatchForArchitecture( |
224 | cpu_type, cpu_subtype); |
225 | if (!best_match) return false; |
226 | |
227 | // Record the selected object file. |
228 | selected_object_file_ = best_match; |
229 | return true; |
230 | } |
231 | |
232 | bool DumpSymbols::SetArchitecture(const std::string& arch_name) { |
233 | bool arch_set = false; |
234 | const NXArchInfo* arch_info = |
235 | google_breakpad::BreakpadGetArchInfoFromName(arch_name.c_str()); |
236 | if (arch_info) { |
237 | arch_set = SetArchitecture(arch_info->cputype, arch_info->cpusubtype); |
238 | } |
239 | return arch_set; |
240 | } |
241 | |
242 | SuperFatArch* DumpSymbols::FindBestMatchForArchitecture( |
243 | cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) { |
244 | // Check if all the object files can be converted to struct fat_arch. |
245 | bool can_convert_to_fat_arch = true; |
246 | vector<struct fat_arch> fat_arch_vector; |
247 | for (vector<SuperFatArch>::const_iterator it = object_files_.begin(); |
248 | it != object_files_.end(); |
249 | ++it) { |
250 | struct fat_arch arch; |
251 | bool success = it->ConvertToFatArch(&arch); |
252 | if (!success) { |
253 | can_convert_to_fat_arch = false; |
254 | break; |
255 | } |
256 | fat_arch_vector.push_back(arch); |
257 | } |
258 | |
259 | // If all the object files can be converted to struct fat_arch, use |
260 | // NXFindBestFatArch. |
261 | if (can_convert_to_fat_arch) { |
262 | const struct fat_arch* best_match |
263 | = NXFindBestFatArch(cpu_type, cpu_subtype, &fat_arch_vector[0], |
264 | static_cast<uint32_t>(fat_arch_vector.size())); |
265 | |
266 | for (size_t i = 0; i < fat_arch_vector.size(); ++i) { |
267 | if (best_match == &fat_arch_vector[i]) |
268 | return &object_files_[i]; |
269 | } |
270 | assert(best_match == NULL); |
271 | return NULL; |
272 | } |
273 | |
274 | // Check for an exact match with cpu_type and cpu_subtype. |
275 | for (vector<SuperFatArch>::iterator it = object_files_.begin(); |
276 | it != object_files_.end(); |
277 | ++it) { |
278 | if (static_cast<cpu_type_t>(it->cputype) == cpu_type && |
279 | static_cast<cpu_subtype_t>(it->cpusubtype) == cpu_subtype) |
280 | return &*it; |
281 | } |
282 | |
283 | // No exact match found. |
284 | // TODO(erikchen): If it becomes necessary, we can copy the implementation of |
285 | // NXFindBestFatArch, located at |
286 | // http://web.mit.edu/darwin/src/modules/cctools/libmacho/arch.c. |
287 | fprintf(stderr, "Failed to find an exact match for an object file with cpu " |
288 | "type: %d and cpu subtype: %d. Furthermore, at least one object file is " |
289 | "larger than 2**32.\n" , cpu_type, cpu_subtype); |
290 | return NULL; |
291 | } |
292 | |
293 | string DumpSymbols::Identifier() { |
294 | scoped_ptr<FileID> file_id; |
295 | |
296 | if (from_disk_) { |
297 | file_id.reset(new FileID(object_filename_.c_str())); |
298 | } else { |
299 | file_id.reset(new FileID(contents_.get(), size_)); |
300 | } |
301 | unsigned char identifier_bytes[16]; |
302 | scoped_ptr<Module> module; |
303 | if (!selected_object_file_) { |
304 | if (!CreateEmptyModule(module)) |
305 | return string(); |
306 | } |
307 | cpu_type_t cpu_type = selected_object_file_->cputype; |
308 | cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype; |
309 | if (!file_id->MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) { |
310 | fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n" , |
311 | object_filename_.c_str()); |
312 | return "" ; |
313 | } |
314 | |
315 | char identifier_string[40]; |
316 | FileID::ConvertIdentifierToString(identifier_bytes, identifier_string, |
317 | sizeof(identifier_string)); |
318 | |
319 | string compacted(identifier_string); |
320 | for(size_t i = compacted.find('-'); i != string::npos; |
321 | i = compacted.find('-', i)) |
322 | compacted.erase(i, 1); |
323 | |
324 | // The pdb for these IDs has an extra byte, so to make everything uniform put |
325 | // a 0 on the end of mac IDs. |
326 | compacted += "0" ; |
327 | |
328 | return compacted; |
329 | } |
330 | |
331 | // A range handler that accepts rangelist data parsed by |
332 | // RangeListReader and populates a range vector (typically |
333 | // owned by a function) with the results. |
334 | class DumpSymbols::DumperRangesHandler: |
335 | public DwarfCUToModule::RangesHandler { |
336 | public: |
337 | DumperRangesHandler(ByteReader* reader) : |
338 | reader_(reader) { } |
339 | |
340 | bool ReadRanges( |
341 | enum DwarfForm form, uint64_t data, |
342 | RangeListReader::CURangesInfo* cu_info, |
343 | vector<Module::Range>* ranges) { |
344 | DwarfRangeListHandler handler(ranges); |
345 | RangeListReader range_list_reader(reader_, cu_info, |
346 | &handler); |
347 | return range_list_reader.ReadRanges(form, data); |
348 | } |
349 | |
350 | private: |
351 | ByteReader* reader_; |
352 | }; |
353 | |
354 | // A line-to-module loader that accepts line number info parsed by |
355 | // LineInfo and populates a Module and a line vector |
356 | // with the results. |
357 | class DumpSymbols::DumperLineToModule: |
358 | public DwarfCUToModule::LineToModuleHandler { |
359 | public: |
360 | // Create a line-to-module converter using BYTE_READER. |
361 | DumperLineToModule(ByteReader* byte_reader) |
362 | : byte_reader_(byte_reader) { } |
363 | |
364 | void StartCompilationUnit(const string& compilation_dir) { |
365 | compilation_dir_ = compilation_dir; |
366 | } |
367 | |
368 | void ReadProgram(const uint8_t* program, |
369 | uint64_t length, |
370 | const uint8_t* string_section, |
371 | uint64_t string_section_length, |
372 | const uint8_t* line_string_section, |
373 | uint64_t line_string_section_length, |
374 | Module* module, |
375 | vector<Module::Line>* lines, |
376 | std::map<uint32_t, Module::File*>* files) { |
377 | DwarfLineToModule handler(module, compilation_dir_, lines, files); |
378 | LineInfo parser(program, length, byte_reader_, nullptr, 0, |
379 | nullptr, 0, &handler); |
380 | parser.Start(); |
381 | } |
382 | private: |
383 | string compilation_dir_; |
384 | ByteReader* byte_reader_; // WEAK |
385 | }; |
386 | |
387 | bool DumpSymbols::CreateEmptyModule(scoped_ptr<Module>& module) { |
388 | // Select an object file, if SetArchitecture hasn't been called to set one |
389 | // explicitly. |
390 | if (!selected_object_file_) { |
391 | // If there's only one architecture, that's the one. |
392 | if (object_files_.size() == 1) |
393 | selected_object_file_ = &object_files_[0]; |
394 | else { |
395 | // Look for an object file whose architecture matches our own. |
396 | const NXArchInfo* local_arch = NXGetLocalArchInfo(); |
397 | if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) { |
398 | fprintf(stderr, "%s: object file contains more than one" |
399 | " architecture, none of which match the current" |
400 | " architecture; specify an architecture explicitly" |
401 | " with '-a ARCH' to resolve the ambiguity\n" , |
402 | object_filename_.c_str()); |
403 | return false; |
404 | } |
405 | } |
406 | } |
407 | |
408 | assert(selected_object_file_); |
409 | |
410 | // Find the name of the selected file's architecture, to appear in |
411 | // the MODULE record and in error messages. |
412 | const NXArchInfo* selected_arch_info = |
413 | google_breakpad::BreakpadGetArchInfoFromCpuType( |
414 | selected_object_file_->cputype, selected_object_file_->cpusubtype); |
415 | |
416 | const char* selected_arch_name = selected_arch_info->name; |
417 | if (strcmp(selected_arch_name, "i386" ) == 0) |
418 | selected_arch_name = "x86" ; |
419 | |
420 | // Produce a name to use in error messages that includes the |
421 | // filename, and the architecture, if there is more than one. |
422 | selected_object_name_ = object_filename_; |
423 | if (object_files_.size() > 1) { |
424 | selected_object_name_ += ", architecture " ; |
425 | selected_object_name_ + selected_arch_name; |
426 | } |
427 | |
428 | // Compute a module name, to appear in the MODULE record. |
429 | string module_name = google_breakpad::BaseName(object_filename_); |
430 | |
431 | // Choose an identifier string, to appear in the MODULE record. |
432 | string identifier = Identifier(); |
433 | if (identifier.empty()) |
434 | return false; |
435 | |
436 | // Create a module to hold the debugging information. |
437 | module.reset(new Module(module_name, |
438 | "mac" , |
439 | selected_arch_name, |
440 | identifier)); |
441 | return true; |
442 | } |
443 | |
444 | void DumpSymbols::ReadDwarf(google_breakpad::Module* module, |
445 | const mach_o::Reader& macho_reader, |
446 | const mach_o::SectionMap& dwarf_sections, |
447 | bool handle_inter_cu_refs) const { |
448 | // Build a byte reader of the appropriate endianness. |
449 | ByteReader byte_reader(macho_reader.big_endian() |
450 | ? ENDIANNESS_BIG |
451 | : ENDIANNESS_LITTLE); |
452 | |
453 | // Construct a context for this file. |
454 | DwarfCUToModule::FileContext file_context(selected_object_name_, |
455 | module, |
456 | handle_inter_cu_refs); |
457 | |
458 | // Build a SectionMap from our mach_o::SectionMap. |
459 | for (mach_o::SectionMap::const_iterator it = dwarf_sections.begin(); |
460 | it != dwarf_sections.end(); ++it) { |
461 | file_context.AddSectionToSectionMap( |
462 | it->first, |
463 | it->second.contents.start, |
464 | it->second.contents.Size()); |
465 | } |
466 | |
467 | // Find the __debug_info section. |
468 | SectionMap::const_iterator debug_info_entry = |
469 | file_context.section_map().find("__debug_info" ); |
470 | // There had better be a __debug_info section! |
471 | if (debug_info_entry == file_context.section_map().end()) { |
472 | fprintf(stderr, "%s: __DWARF segment of file has no __debug_info section\n" , |
473 | selected_object_name_.c_str()); |
474 | return; |
475 | } |
476 | const std::pair<const uint8_t*, uint64_t>& debug_info_section = |
477 | debug_info_entry->second; |
478 | |
479 | // Build a line-to-module loader for the root handler to use. |
480 | DumperLineToModule line_to_module(&byte_reader); |
481 | |
482 | // .debug_ranges and .debug_rngslists reader |
483 | DumperRangesHandler ranges_handler(&byte_reader); |
484 | |
485 | // Walk the __debug_info section, one compilation unit at a time. |
486 | uint64_t debug_info_length = debug_info_section.second; |
487 | for (uint64_t offset = 0; offset < debug_info_length;) { |
488 | // Make a handler for the root DIE that populates MODULE with the |
489 | // debug info. |
490 | DwarfCUToModule::WarningReporter reporter(selected_object_name_, |
491 | offset); |
492 | DwarfCUToModule root_handler(&file_context, &line_to_module, |
493 | &ranges_handler, &reporter, |
494 | symbol_data_ & INLINES); |
495 | // Make a Dwarf2Handler that drives our DIEHandler. |
496 | DIEDispatcher die_dispatcher(&root_handler); |
497 | // Make a DWARF parser for the compilation unit at OFFSET. |
498 | CompilationUnit dwarf_reader(selected_object_name_, |
499 | file_context.section_map(), |
500 | offset, |
501 | &byte_reader, |
502 | &die_dispatcher); |
503 | // Process the entire compilation unit; get the offset of the next. |
504 | offset += dwarf_reader.Start(); |
505 | } |
506 | } |
507 | |
508 | bool DumpSymbols::ReadCFI(google_breakpad::Module* module, |
509 | const mach_o::Reader& macho_reader, |
510 | const mach_o::Section& section, |
511 | bool eh_frame) const { |
512 | // Find the appropriate set of register names for this file's |
513 | // architecture. |
514 | vector<string> register_names; |
515 | switch (macho_reader.cpu_type()) { |
516 | case CPU_TYPE_X86: |
517 | register_names = DwarfCFIToModule::RegisterNames::I386(); |
518 | break; |
519 | case CPU_TYPE_X86_64: |
520 | register_names = DwarfCFIToModule::RegisterNames::X86_64(); |
521 | break; |
522 | case CPU_TYPE_ARM: |
523 | register_names = DwarfCFIToModule::RegisterNames::ARM(); |
524 | break; |
525 | case CPU_TYPE_ARM64: |
526 | register_names = DwarfCFIToModule::RegisterNames::ARM64(); |
527 | break; |
528 | default: { |
529 | const NXArchInfo* arch = google_breakpad::BreakpadGetArchInfoFromCpuType( |
530 | macho_reader.cpu_type(), macho_reader.cpu_subtype()); |
531 | fprintf(stderr, "%s: cannot convert DWARF call frame information for " , |
532 | selected_object_name_.c_str()); |
533 | if (arch) |
534 | fprintf(stderr, "architecture '%s'" , arch->name); |
535 | else |
536 | fprintf(stderr, "architecture %d,%d" , |
537 | macho_reader.cpu_type(), macho_reader.cpu_subtype()); |
538 | fprintf(stderr, " to Breakpad symbol file: no register name table\n" ); |
539 | return false; |
540 | } |
541 | } |
542 | |
543 | // Find the call frame information and its size. |
544 | const uint8_t* cfi = section.contents.start; |
545 | size_t cfi_size = section.contents.Size(); |
546 | |
547 | // Plug together the parser, handler, and their entourages. |
548 | DwarfCFIToModule::Reporter module_reporter(selected_object_name_, |
549 | section.section_name); |
550 | DwarfCFIToModule handler(module, register_names, &module_reporter); |
551 | ByteReader byte_reader(macho_reader.big_endian() ? |
552 | ENDIANNESS_BIG : |
553 | ENDIANNESS_LITTLE); |
554 | byte_reader.SetAddressSize(macho_reader.bits_64() ? 8 : 4); |
555 | // At the moment, according to folks at Apple and some cursory |
556 | // investigation, Mac OS X only uses DW_EH_PE_pcrel-based pointers, so |
557 | // this is the only base address the CFI parser will need. |
558 | byte_reader.SetCFIDataBase(section.address, cfi); |
559 | |
560 | CallFrameInfo::Reporter dwarf_reporter(selected_object_name_, |
561 | section.section_name); |
562 | CallFrameInfo parser(cfi, cfi_size, |
563 | &byte_reader, &handler, &dwarf_reporter, |
564 | eh_frame); |
565 | parser.Start(); |
566 | return true; |
567 | } |
568 | |
569 | // A LoadCommandHandler that loads whatever debugging data it finds into a |
570 | // Module. |
571 | class DumpSymbols::LoadCommandDumper: |
572 | public mach_o::Reader::LoadCommandHandler { |
573 | public: |
574 | // Create a load command dumper handling load commands from READER's |
575 | // file, and adding data to MODULE. |
576 | LoadCommandDumper(const DumpSymbols& dumper, |
577 | google_breakpad::Module* module, |
578 | const mach_o::Reader& reader, |
579 | SymbolData symbol_data, |
580 | bool handle_inter_cu_refs) |
581 | : dumper_(dumper), |
582 | module_(module), |
583 | reader_(reader), |
584 | symbol_data_(symbol_data), |
585 | handle_inter_cu_refs_(handle_inter_cu_refs) { } |
586 | |
587 | bool SegmentCommand(const mach_o::Segment& segment); |
588 | bool SymtabCommand(const ByteBuffer& entries, const ByteBuffer& strings); |
589 | |
590 | private: |
591 | const DumpSymbols& dumper_; |
592 | google_breakpad::Module* module_; // WEAK |
593 | const mach_o::Reader& reader_; |
594 | const SymbolData symbol_data_; |
595 | const bool handle_inter_cu_refs_; |
596 | }; |
597 | |
598 | bool DumpSymbols::LoadCommandDumper::SegmentCommand(const Segment& segment) { |
599 | mach_o::SectionMap section_map; |
600 | if (!reader_.MapSegmentSections(segment, §ion_map)) |
601 | return false; |
602 | |
603 | if (segment.name == "__TEXT" ) { |
604 | module_->SetLoadAddress(segment.vmaddr); |
605 | if (symbol_data_ & CFI) { |
606 | mach_o::SectionMap::const_iterator eh_frame = |
607 | section_map.find("__eh_frame" ); |
608 | if (eh_frame != section_map.end()) { |
609 | // If there is a problem reading this, don't treat it as a fatal error. |
610 | dumper_.ReadCFI(module_, reader_, eh_frame->second, true); |
611 | } |
612 | } |
613 | return true; |
614 | } |
615 | |
616 | if (segment.name == "__DWARF" ) { |
617 | if ((symbol_data_ & SYMBOLS_AND_FILES) || (symbol_data_ & INLINES)) { |
618 | dumper_.ReadDwarf(module_, reader_, section_map, handle_inter_cu_refs_); |
619 | } |
620 | if (symbol_data_ & CFI) { |
621 | mach_o::SectionMap::const_iterator debug_frame |
622 | = section_map.find("__debug_frame" ); |
623 | if (debug_frame != section_map.end()) { |
624 | // If there is a problem reading this, don't treat it as a fatal error. |
625 | dumper_.ReadCFI(module_, reader_, debug_frame->second, false); |
626 | } |
627 | } |
628 | } |
629 | |
630 | return true; |
631 | } |
632 | |
633 | bool DumpSymbols::LoadCommandDumper::SymtabCommand(const ByteBuffer& entries, |
634 | const ByteBuffer& strings) { |
635 | StabsToModule stabs_to_module(module_); |
636 | // Mac OS X STABS are never "unitized", and the size of the 'value' field |
637 | // matches the address size of the executable. |
638 | StabsReader stabs_reader(entries.start, entries.Size(), |
639 | strings.start, strings.Size(), |
640 | reader_.big_endian(), |
641 | reader_.bits_64() ? 8 : 4, |
642 | true, |
643 | &stabs_to_module); |
644 | if (!stabs_reader.Process()) |
645 | return false; |
646 | stabs_to_module.Finalize(); |
647 | return true; |
648 | } |
649 | |
650 | bool DumpSymbols::ReadSymbolData(Module** out_module) { |
651 | scoped_ptr<Module> module; |
652 | if (!CreateEmptyModule(module)) |
653 | return false; |
654 | |
655 | // Parse the selected object file. |
656 | mach_o::Reader::Reporter reporter(selected_object_name_); |
657 | mach_o::Reader reader(&reporter); |
658 | if (!reader.Read(&contents_[0] |
659 | + selected_object_file_->offset, |
660 | selected_object_file_->size, |
661 | selected_object_file_->cputype, |
662 | selected_object_file_->cpusubtype)) |
663 | return false; |
664 | |
665 | // Walk its load commands, and deal with whatever is there. |
666 | LoadCommandDumper load_command_dumper(*this, module.get(), reader, |
667 | symbol_data_, handle_inter_cu_refs_); |
668 | if (!reader.WalkLoadCommands(&load_command_dumper)) |
669 | return false; |
670 | |
671 | *out_module = module.release(); |
672 | |
673 | return true; |
674 | } |
675 | |
676 | bool DumpSymbols::WriteSymbolFile(std::ostream& stream) { |
677 | Module* module = NULL; |
678 | |
679 | if (ReadSymbolData(&module) && module) { |
680 | bool res = module->Write(stream, symbol_data_); |
681 | delete module; |
682 | return res; |
683 | } |
684 | |
685 | return false; |
686 | } |
687 | |
688 | // Read the selected object file's debugging information, and write out the |
689 | // header only to |stream|. Return true on success; if an error occurs, report |
690 | // it and return false. |
691 | bool DumpSymbols::(std::ostream& stream) { |
692 | scoped_ptr<Module> module; |
693 | if (!CreateEmptyModule(module)) |
694 | return false; |
695 | |
696 | return module->Write(stream, symbol_data_); |
697 | } |
698 | |
699 | } // namespace google_breakpad |
700 | |