OLD | NEW |
---|---|
1 // -*- mode: c++ -*- | 1 // -*- mode: c++ -*- |
2 | 2 |
3 // Copyright (c) 2011, Google Inc. | 3 // Copyright (c) 2011, Google Inc. |
4 // All rights reserved. | 4 // All rights reserved. |
5 // | 5 // |
6 // Redistribution and use in source and binary forms, with or without | 6 // Redistribution and use in source and binary forms, with or without |
7 // modification, are permitted provided that the following conditions are | 7 // modification, are permitted provided that the following conditions are |
8 // met: | 8 // met: |
9 // | 9 // |
10 // * Redistributions of source code must retain the above copyright | 10 // * Redistributions of source code must retain the above copyright |
(...skipping 18 matching lines...) Expand all Loading... | |
29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | 31 |
32 // Author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | 32 // Author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> |
33 | 33 |
34 // dump_syms.mm: Create a symbol file for use with minidumps | 34 // dump_syms.mm: Create a symbol file for use with minidumps |
35 | 35 |
36 #include "common/mac/dump_syms.h" | 36 #include "common/mac/dump_syms.h" |
37 | 37 |
38 #include <assert.h> | 38 #include <assert.h> |
39 #include <Foundation/Foundation.h> | 39 #include <libgen.h> |
40 #include <mach-o/arch.h> | 40 #include <mach-o/arch.h> |
41 #include <mach-o/fat.h> | 41 #include <mach-o/fat.h> |
42 #include <stdio.h> | 42 #include <stdio.h> |
43 #include <sys/stat.h> | |
44 #include <unistd.h> | |
43 | 45 |
44 #include <ostream> | 46 #include <ostream> |
45 #include <string> | 47 #include <string> |
46 #include <vector> | 48 #include <vector> |
47 | 49 |
48 #include "common/dwarf/bytereader-inl.h" | 50 #include "common/dwarf/bytereader-inl.h" |
49 #include "common/dwarf/dwarf2reader.h" | 51 #include "common/dwarf/dwarf2reader.h" |
50 #include "common/dwarf_cfi_to_module.h" | 52 #include "common/dwarf_cfi_to_module.h" |
51 #include "common/dwarf_cu_to_module.h" | 53 #include "common/dwarf_cu_to_module.h" |
52 #include "common/dwarf_line_to_module.h" | 54 #include "common/dwarf_line_to_module.h" |
(...skipping 25 matching lines...) Expand all Loading... | |
78 using google_breakpad::StabsReader; | 80 using google_breakpad::StabsReader; |
79 using google_breakpad::StabsToModule; | 81 using google_breakpad::StabsToModule; |
80 using google_breakpad::scoped_ptr; | 82 using google_breakpad::scoped_ptr; |
81 using std::make_pair; | 83 using std::make_pair; |
82 using std::pair; | 84 using std::pair; |
83 using std::string; | 85 using std::string; |
84 using std::vector; | 86 using std::vector; |
85 | 87 |
86 namespace google_breakpad { | 88 namespace google_breakpad { |
87 | 89 |
88 bool DumpSymbols::Read(NSString *filename) { | 90 bool DumpSymbols::Read(const string &filename) { |
89 if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) { | 91 struct stat st; |
92 if (stat(filename.c_str(), &st) == -1) { | |
90 fprintf(stderr, "Object file does not exist: %s\n", | 93 fprintf(stderr, "Object file does not exist: %s\n", |
Mark Mentovai
2015/09/11 15:49:50
Not necessarily—use strerror(errno) for the real e
Ted Mielczarek
2015/09/15 12:58:36
Done.
| |
91 [filename fileSystemRepresentation]); | 94 filename.c_str()); |
92 return false; | 95 return false; |
93 } | 96 } |
94 | 97 |
95 input_pathname_ = [filename retain]; | 98 input_pathname_ = filename; |
96 | 99 |
97 // Does this filename refer to a dSYM bundle? | 100 // Does this filename refer to a dSYM bundle? |
98 NSBundle *bundle = [NSBundle bundleWithPath:input_pathname_]; | 101 string contents_path = input_pathname_ + "/Contents/Resources/DWARF"; |
99 | 102 if (S_ISDIR(st.st_mode) && |
100 if (bundle) { | 103 access(contents_path.c_str(), F_OK) == 0) { |
101 // Filenames referring to bundles usually have names of the form | 104 // Filenames referring to bundles usually have names of the form |
Mark Mentovai
2015/09/11 15:49:50
This is totally hokey. It seems to break down if t
Ted Mielczarek
2015/09/15 12:58:36
Done.
| |
102 // "<basename>.dSYM"; however, if the user has specified a wrapper | 105 // "<basename>.dSYM"; however, if the user has specified a wrapper |
103 // suffix (the WRAPPER_SUFFIX and WRAPPER_EXTENSION build settings), | 106 // suffix (the WRAPPER_SUFFIX and WRAPPER_EXTENSION build settings), |
104 // then the name may have the form "<basename>.<extension>.dSYM". In | 107 // then the name may have the form "<basename>.<extension>.dSYM". In |
105 // either case, the resource name for the file containing the DWARF | 108 // either case, the resource name for the file containing the DWARF |
106 // info within the bundle is <basename>. | 109 // info within the bundle is <basename>. |
107 // | 110 // |
108 // Since there's no way to tell how much to strip off, remove one | 111 // 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 | 112 // extension at a time, and use the first one that |
110 // pathForResource:ofType:inDirectory likes. | 113 // exists under Contents/Resources/DWARF. |
111 NSString *base_name = [input_pathname_ lastPathComponent]; | 114 string base_name = input_pathname_; |
112 NSString *dwarf_resource; | 115 base_name = basename(&base_name[0]); |
116 | |
117 string dwarf_resource; | |
113 | 118 |
114 do { | 119 do { |
115 NSString *new_base_name = [base_name stringByDeletingPathExtension]; | 120 size_t lastindex = base_name.find_last_of("."); |
116 | 121 if (lastindex == string::npos) { |
117 // If stringByDeletingPathExtension returned the name unchanged, then | 122 // there's nothing more for us to strip off --- lose. |
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", | 123 fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n", |
121 [input_pathname_ fileSystemRepresentation]); | 124 input_pathname_.c_str()); |
122 return false; | 125 return false; |
123 } | 126 } |
124 | 127 |
125 // Take the shortened result as our new base_name. | 128 // Take the shortened result as our new base_name. |
126 base_name = new_base_name; | 129 base_name = base_name.substr(0, lastindex); |
127 | 130 |
128 // Try to find a DWARF resource in the bundle under the new base_name. | 131 // Try to find a DWARF resource in the bundle under the new base_name. |
129 dwarf_resource = [bundle pathForResource:base_name | 132 dwarf_resource = contents_path + "/" + base_name; |
130 ofType:nil inDirectory:@"DWARF"]; | 133 } while (access(dwarf_resource.c_str(), F_OK) == -1); |
131 } while (!dwarf_resource); | |
132 | 134 |
133 object_filename_ = [dwarf_resource retain]; | 135 object_filename_ = dwarf_resource; |
134 } else { | 136 } else { |
135 object_filename_ = [input_pathname_ retain]; | 137 object_filename_ = input_pathname_; |
136 } | 138 } |
137 | 139 |
138 // Read the file's contents into memory. | 140 // Read the file's contents into memory. |
139 // | 141 bool read_ok = false; |
140 // The documentation for dataWithContentsOfMappedFile says: | 142 string error; |
141 // | 143 if (stat(object_filename_.c_str(), &st) != -1) { |
142 // Because of file mapping restrictions, this method should only be | 144 FILE* f = fopen(object_filename_.c_str(), "rb"); |
143 // used if the file is guaranteed to exist for the duration of the | 145 if (f) { |
144 // data object’s existence. It is generally safer to use the | 146 contents_.resize(st.st_size); |
145 // dataWithContentsOfFile: method. | 147 read_ok = (fread(&contents_[0], st.st_size, 1, f) == 1); |
Mark Mentovai
2015/09/11 15:49:50
No read loop?
Generally it’s preferable to read u
Ted Mielczarek
2015/09/15 12:58:36
Done.
| |
146 // | 148 if (!read_ok) { |
147 // I gather this means that OS X doesn't have (or at least, that method | 149 error = strerror(errno); |
148 // doesn't use) a form of mapping like Linux's MAP_PRIVATE, where the | 150 } |
149 // process appears to get its own copy of the data, and changes to the | 151 fclose(f); |
150 // file don't affect memory and vice versa). | 152 } else { |
151 NSError *error; | 153 error = strerror(errno); |
152 contents_ = [NSData dataWithContentsOfFile:object_filename_ | 154 } |
153 options:0 | 155 } |
154 error:&error]; | 156 |
155 if (!contents_) { | 157 if (!read_ok) { |
156 fprintf(stderr, "Error reading object file: %s: %s\n", | 158 fprintf(stderr, "Error reading object file: %s: %s\n", |
157 [object_filename_ fileSystemRepresentation], | 159 object_filename_.c_str(), |
158 [[error localizedDescription] UTF8String]); | 160 error.c_str()); |
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; | 161 return false; |
170 } | 162 } |
171 | 163 |
164 // Get the list of object files present in the file. | |
165 FatReader::Reporter fat_reporter(object_filename_); | |
166 FatReader fat_reader(&fat_reporter); | |
167 if (!fat_reader.Read(&contents_[0], | |
168 contents_.size())) { | |
169 return false; | |
170 } | |
171 | |
172 // Get our own copy of fat_reader's object file list. | 172 // Get our own copy of fat_reader's object file list. |
173 size_t object_files_count; | 173 size_t object_files_count; |
174 const SuperFatArch *object_files = | 174 const SuperFatArch *object_files = |
175 fat_reader.object_files(&object_files_count); | 175 fat_reader.object_files(&object_files_count); |
176 if (object_files_count == 0) { | 176 if (object_files_count == 0) { |
177 fprintf(stderr, "Fat binary file contains *no* architectures: %s\n", | 177 fprintf(stderr, "Fat binary file contains *no* architectures: %s\n", |
178 [object_filename_ fileSystemRepresentation]); | 178 object_filename_.c_str()); |
179 return false; | 179 return false; |
180 } | 180 } |
181 object_files_.resize(object_files_count); | 181 object_files_.resize(object_files_count); |
182 memcpy(&object_files_[0], object_files, | 182 memcpy(&object_files_[0], object_files, |
183 sizeof(SuperFatArch) * object_files_count); | 183 sizeof(SuperFatArch) * object_files_count); |
184 | 184 |
185 return true; | 185 return true; |
186 } | 186 } |
187 | 187 |
188 bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type, | 188 bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type, |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
251 // TODO(erikchen): If it becomes necessary, we can copy the implementation of | 251 // TODO(erikchen): If it becomes necessary, we can copy the implementation of |
252 // NXFindBestFatArch, located at | 252 // NXFindBestFatArch, located at |
253 // http://web.mit.edu/darwin/src/modules/cctools/libmacho/arch.c. | 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 " | 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 " | 255 "type: %d and cpu subtype: %d. Furthermore, at least one object file is " |
256 "larger than 2**32.\n", cpu_type, cpu_subtype); | 256 "larger than 2**32.\n", cpu_type, cpu_subtype); |
257 return NULL; | 257 return NULL; |
258 } | 258 } |
259 | 259 |
260 string DumpSymbols::Identifier() { | 260 string DumpSymbols::Identifier() { |
261 FileID file_id([object_filename_ fileSystemRepresentation]); | 261 FileID file_id(object_filename_.c_str()); |
262 unsigned char identifier_bytes[16]; | 262 unsigned char identifier_bytes[16]; |
263 cpu_type_t cpu_type = selected_object_file_->cputype; | 263 cpu_type_t cpu_type = selected_object_file_->cputype; |
264 cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype; | 264 cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype; |
265 if (!file_id.MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) { | 265 if (!file_id.MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) { |
266 fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n", | 266 fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n", |
267 [object_filename_ fileSystemRepresentation]); | 267 object_filename_.c_str()); |
268 return ""; | 268 return ""; |
269 } | 269 } |
270 | 270 |
271 char identifier_string[40]; | 271 char identifier_string[40]; |
272 FileID::ConvertIdentifierToString(identifier_bytes, identifier_string, | 272 FileID::ConvertIdentifierToString(identifier_bytes, identifier_string, |
273 sizeof(identifier_string)); | 273 sizeof(identifier_string)); |
274 | 274 |
275 string compacted(identifier_string); | 275 string compacted(identifier_string); |
276 for(size_t i = compacted.find('-'); i != string::npos; | 276 for(size_t i = compacted.find('-'); i != string::npos; |
277 i = compacted.find('-', i)) | 277 i = compacted.find('-', i)) |
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
519 if (object_files_.size() == 1) | 519 if (object_files_.size() == 1) |
520 selected_object_file_ = &object_files_[0]; | 520 selected_object_file_ = &object_files_[0]; |
521 else { | 521 else { |
522 // Look for an object file whose architecture matches our own. | 522 // Look for an object file whose architecture matches our own. |
523 const NXArchInfo *local_arch = NXGetLocalArchInfo(); | 523 const NXArchInfo *local_arch = NXGetLocalArchInfo(); |
524 if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) { | 524 if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) { |
525 fprintf(stderr, "%s: object file contains more than one" | 525 fprintf(stderr, "%s: object file contains more than one" |
526 " architecture, none of which match the current" | 526 " architecture, none of which match the current" |
527 " architecture; specify an architecture explicitly" | 527 " architecture; specify an architecture explicitly" |
528 " with '-a ARCH' to resolve the ambiguity\n", | 528 " with '-a ARCH' to resolve the ambiguity\n", |
529 [object_filename_ fileSystemRepresentation]); | 529 object_filename_.c_str()); |
530 return false; | 530 return false; |
531 } | 531 } |
532 } | 532 } |
533 } | 533 } |
534 | 534 |
535 assert(selected_object_file_); | 535 assert(selected_object_file_); |
536 | 536 |
537 // Find the name of the selected file's architecture, to appear in | 537 // Find the name of the selected file's architecture, to appear in |
538 // the MODULE record and in error messages. | 538 // the MODULE record and in error messages. |
539 const NXArchInfo *selected_arch_info = | 539 const NXArchInfo *selected_arch_info = |
540 google_breakpad::BreakpadGetArchInfoFromCpuType( | 540 google_breakpad::BreakpadGetArchInfoFromCpuType( |
541 selected_object_file_->cputype, selected_object_file_->cpusubtype); | 541 selected_object_file_->cputype, selected_object_file_->cpusubtype); |
542 | 542 |
543 const char *selected_arch_name = selected_arch_info->name; | 543 const char *selected_arch_name = selected_arch_info->name; |
544 if (strcmp(selected_arch_name, "i386") == 0) | 544 if (strcmp(selected_arch_name, "i386") == 0) |
545 selected_arch_name = "x86"; | 545 selected_arch_name = "x86"; |
546 | 546 |
547 // Produce a name to use in error messages that includes the | 547 // Produce a name to use in error messages that includes the |
548 // filename, and the architecture, if there is more than one. | 548 // filename, and the architecture, if there is more than one. |
549 selected_object_name_ = [object_filename_ UTF8String]; | 549 selected_object_name_ = object_filename_; |
550 if (object_files_.size() > 1) { | 550 if (object_files_.size() > 1) { |
551 selected_object_name_ += ", architecture "; | 551 selected_object_name_ += ", architecture "; |
552 selected_object_name_ + selected_arch_name; | 552 selected_object_name_ + selected_arch_name; |
553 } | 553 } |
554 | 554 |
555 // Compute a module name, to appear in the MODULE record. | 555 // Compute a module name, to appear in the MODULE record. |
556 NSString *module_name = [object_filename_ lastPathComponent]; | 556 string module_name = object_filename_; |
557 module_name = basename(&module_name[0]); | |
557 | 558 |
558 // Choose an identifier string, to appear in the MODULE record. | 559 // Choose an identifier string, to appear in the MODULE record. |
559 string identifier = Identifier(); | 560 string identifier = Identifier(); |
560 if (identifier.empty()) | 561 if (identifier.empty()) |
561 return false; | 562 return false; |
562 identifier += "0"; | 563 identifier += "0"; |
563 | 564 |
564 // Create a module to hold the debugging information. | 565 // Create a module to hold the debugging information. |
565 scoped_ptr<Module> module(new Module([module_name UTF8String], | 566 scoped_ptr<Module> module(new Module(module_name, |
566 "mac", | 567 "mac", |
567 selected_arch_name, | 568 selected_arch_name, |
568 identifier)); | 569 identifier)); |
569 | 570 |
570 // Parse the selected object file. | 571 // Parse the selected object file. |
571 mach_o::Reader::Reporter reporter(selected_object_name_); | 572 mach_o::Reader::Reporter reporter(selected_object_name_); |
572 mach_o::Reader reader(&reporter); | 573 mach_o::Reader reader(&reporter); |
573 if (!reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes]) | 574 if (!reader.Read(&contents_[0] |
574 + selected_object_file_->offset, | 575 + selected_object_file_->offset, |
575 selected_object_file_->size, | 576 selected_object_file_->size, |
576 selected_object_file_->cputype, | 577 selected_object_file_->cputype, |
577 selected_object_file_->cpusubtype)) | 578 selected_object_file_->cpusubtype)) |
578 return false; | 579 return false; |
579 | 580 |
580 // Walk its load commands, and deal with whatever is there. | 581 // Walk its load commands, and deal with whatever is there. |
581 LoadCommandDumper load_command_dumper(*this, module.get(), reader, | 582 LoadCommandDumper load_command_dumper(*this, module.get(), reader, |
582 symbol_data_, handle_inter_cu_refs_); | 583 symbol_data_, handle_inter_cu_refs_); |
583 if (!reader.WalkLoadCommands(&load_command_dumper)) | 584 if (!reader.WalkLoadCommands(&load_command_dumper)) |
(...skipping 10 matching lines...) Expand all Loading... | |
594 if (ReadSymbolData(&module) && module) { | 595 if (ReadSymbolData(&module) && module) { |
595 bool res = module->Write(stream, symbol_data_); | 596 bool res = module->Write(stream, symbol_data_); |
596 delete module; | 597 delete module; |
597 return res; | 598 return res; |
598 } | 599 } |
599 | 600 |
600 return false; | 601 return false; |
601 } | 602 } |
602 | 603 |
603 } // namespace google_breakpad | 604 } // namespace google_breakpad |
OLD | NEW |