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