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 <dirent.h> |
40 #include <errno.h> | |
41 #include <libgen.h> | |
40 #include <mach-o/arch.h> | 42 #include <mach-o/arch.h> |
41 #include <mach-o/fat.h> | 43 #include <mach-o/fat.h> |
42 #include <stdio.h> | 44 #include <stdio.h> |
45 #include <sys/stat.h> | |
46 #include <sys/types.h> | |
47 #include <unistd.h> | |
43 | 48 |
44 #include <ostream> | 49 #include <ostream> |
45 #include <string> | 50 #include <string> |
46 #include <vector> | 51 #include <vector> |
47 | 52 |
48 #include "common/dwarf/bytereader-inl.h" | 53 #include "common/dwarf/bytereader-inl.h" |
49 #include "common/dwarf/dwarf2reader.h" | 54 #include "common/dwarf/dwarf2reader.h" |
50 #include "common/dwarf_cfi_to_module.h" | 55 #include "common/dwarf_cfi_to_module.h" |
51 #include "common/dwarf_cu_to_module.h" | 56 #include "common/dwarf_cu_to_module.h" |
52 #include "common/dwarf_line_to_module.h" | 57 #include "common/dwarf_line_to_module.h" |
(...skipping 23 matching lines...) Expand all Loading... | |
76 using google_breakpad::mach_o::Segment; | 81 using google_breakpad::mach_o::Segment; |
77 using google_breakpad::Module; | 82 using google_breakpad::Module; |
78 using google_breakpad::StabsReader; | 83 using google_breakpad::StabsReader; |
79 using google_breakpad::StabsToModule; | 84 using google_breakpad::StabsToModule; |
80 using google_breakpad::scoped_ptr; | 85 using google_breakpad::scoped_ptr; |
81 using std::make_pair; | 86 using std::make_pair; |
82 using std::pair; | 87 using std::pair; |
83 using std::string; | 88 using std::string; |
84 using std::vector; | 89 using std::vector; |
85 | 90 |
91 namespace { | |
92 // Return a vector<string> with absolute paths to all the entries | |
93 // in directory (excluding . and ..). | |
94 const vector<string> list_directory(const string& directory) { | |
Mark Mentovai
2015/09/15 18:24:23
The return value shouldn’t be const. It’s up to th
Ted Mielczarek
2015/09/16 10:45:32
Done.
| |
95 vector<string> entries; | |
96 DIR* dir = opendir(directory.c_str()); | |
97 if (!dir) { | |
98 return entries; | |
99 } | |
100 | |
101 string path = directory; | |
102 if (path[path.length() - 1] != '/') { | |
103 path += '/'; | |
104 } | |
105 | |
106 struct dirent* entry = NULL; | |
107 while ((entry = readdir(dir))) { | |
108 if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) { | |
109 entries.push_back(path + entry->d_name); | |
110 } | |
111 } | |
112 | |
113 closedir(dir); | |
114 return entries; | |
115 } | |
116 } | |
117 | |
86 namespace google_breakpad { | 118 namespace google_breakpad { |
87 | 119 |
88 bool DumpSymbols::Read(NSString *filename) { | 120 bool DumpSymbols::Read(const string &filename) { |
89 if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) { | 121 struct stat st; |
90 fprintf(stderr, "Object file does not exist: %s\n", | 122 if (stat(filename.c_str(), &st) == -1) { |
91 [filename fileSystemRepresentation]); | 123 fprintf(stderr, "Could not access object file %s: %s\n", |
124 filename.c_str(), strerror(errno)); | |
92 return false; | 125 return false; |
93 } | 126 } |
94 | 127 |
95 input_pathname_ = [filename retain]; | 128 input_pathname_ = filename; |
96 | 129 |
97 // Does this filename refer to a dSYM bundle? | 130 // Does this filename refer to a dSYM bundle? |
98 NSBundle *bundle = [NSBundle bundleWithPath:input_pathname_]; | 131 string contents_path = input_pathname_ + "/Contents/Resources/DWARF"; |
132 if (S_ISDIR(st.st_mode) && | |
133 access(contents_path.c_str(), F_OK) == 0) { | |
134 // If there's one file under Contents/Resources/DWARF then use that, | |
135 // otherwise bail out. | |
136 const vector<string> entries = list_directory(contents_path); | |
137 if (entries.size() == 0) { | |
138 fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n", | |
139 input_pathname_.c_str()); | |
140 return false; | |
141 } | |
142 if (entries.size() > 1) { | |
143 fprintf(stderr, "Too many DWARF files in bundle: %s\n", | |
144 input_pathname_.c_str()); | |
145 return false; | |
146 } | |
99 | 147 |
100 if (bundle) { | 148 object_filename_ = entries[0]; |
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 { | 149 } else { |
135 object_filename_ = [input_pathname_ retain]; | 150 object_filename_ = input_pathname_; |
136 } | 151 } |
137 | 152 |
138 // Read the file's contents into memory. | 153 // Read the file's contents into memory. |
139 // | 154 bool read_ok = true; |
140 // The documentation for dataWithContentsOfMappedFile says: | 155 string error; |
141 // | 156 if (stat(object_filename_.c_str(), &st) != -1) { |
142 // Because of file mapping restrictions, this method should only be | 157 FILE* f = fopen(object_filename_.c_str(), "rb"); |
143 // used if the file is guaranteed to exist for the duration of the | 158 if (f) { |
144 // data object’s existence. It is generally safer to use the | 159 contents_.reset(new uint8_t[st.st_size]); |
145 // dataWithContentsOfFile: method. | 160 off_t total = 0; |
146 // | 161 while (total < st.st_size && !feof(f)) { |
147 // I gather this means that OS X doesn't have (or at least, that method | 162 size_t read = fread(&contents_[0] + total, 1, st.st_size - total, f); |
148 // doesn't use) a form of mapping like Linux's MAP_PRIVATE, where the | 163 if (read == 0) { |
149 // process appears to get its own copy of the data, and changes to the | 164 if (ferror(f)) { |
150 // file don't affect memory and vice versa). | 165 read_ok = false; |
151 NSError *error; | 166 error = strerror(errno); |
152 contents_ = [NSData dataWithContentsOfFile:object_filename_ | 167 } |
153 options:0 | 168 break; |
154 error:&error]; | 169 } |
155 if (!contents_) { | 170 total += read; |
171 } | |
172 fclose(f); | |
173 } else { | |
174 error = strerror(errno); | |
175 } | |
176 } | |
177 | |
178 if (!read_ok) { | |
156 fprintf(stderr, "Error reading object file: %s: %s\n", | 179 fprintf(stderr, "Error reading object file: %s: %s\n", |
157 [object_filename_ fileSystemRepresentation], | 180 object_filename_.c_str(), |
158 [[error localizedDescription] UTF8String]); | 181 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; | 182 return false; |
170 } | 183 } |
171 | 184 |
185 // Get the list of object files present in the file. | |
186 FatReader::Reporter fat_reporter(object_filename_); | |
187 FatReader fat_reader(&fat_reporter); | |
188 if (!fat_reader.Read(&contents_[0], | |
189 st.st_size)) { | |
190 return false; | |
191 } | |
192 | |
172 // Get our own copy of fat_reader's object file list. | 193 // Get our own copy of fat_reader's object file list. |
173 size_t object_files_count; | 194 size_t object_files_count; |
174 const SuperFatArch *object_files = | 195 const SuperFatArch *object_files = |
175 fat_reader.object_files(&object_files_count); | 196 fat_reader.object_files(&object_files_count); |
176 if (object_files_count == 0) { | 197 if (object_files_count == 0) { |
177 fprintf(stderr, "Fat binary file contains *no* architectures: %s\n", | 198 fprintf(stderr, "Fat binary file contains *no* architectures: %s\n", |
178 [object_filename_ fileSystemRepresentation]); | 199 object_filename_.c_str()); |
179 return false; | 200 return false; |
180 } | 201 } |
181 object_files_.resize(object_files_count); | 202 object_files_.resize(object_files_count); |
182 memcpy(&object_files_[0], object_files, | 203 memcpy(&object_files_[0], object_files, |
183 sizeof(SuperFatArch) * object_files_count); | 204 sizeof(SuperFatArch) * object_files_count); |
184 | 205 |
185 return true; | 206 return true; |
186 } | 207 } |
187 | 208 |
188 bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type, | 209 bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type, |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
236 return &object_files_[i]; | 257 return &object_files_[i]; |
237 } | 258 } |
238 assert(best_match == NULL); | 259 assert(best_match == NULL); |
239 return NULL; | 260 return NULL; |
240 } | 261 } |
241 | 262 |
242 // Check for an exact match with cpu_type and cpu_subtype. | 263 // Check for an exact match with cpu_type and cpu_subtype. |
243 for (vector<SuperFatArch>::iterator it = object_files_.begin(); | 264 for (vector<SuperFatArch>::iterator it = object_files_.begin(); |
244 it != object_files_.end(); | 265 it != object_files_.end(); |
245 ++it) { | 266 ++it) { |
246 if (it->cputype == cpu_type && it->cpusubtype == cpu_subtype) | 267 if (static_cast<cpu_type_t>(it->cputype) == cpu_type && |
268 static_cast<cpu_subtype_t>(it->cpusubtype) == cpu_subtype) | |
247 return &*it; | 269 return &*it; |
248 } | 270 } |
249 | 271 |
250 // No exact match found. | 272 // No exact match found. |
251 // TODO(erikchen): If it becomes necessary, we can copy the implementation of | 273 // TODO(erikchen): If it becomes necessary, we can copy the implementation of |
252 // NXFindBestFatArch, located at | 274 // NXFindBestFatArch, located at |
253 // http://web.mit.edu/darwin/src/modules/cctools/libmacho/arch.c. | 275 // 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 " | 276 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 " | 277 "type: %d and cpu subtype: %d. Furthermore, at least one object file is " |
256 "larger than 2**32.\n", cpu_type, cpu_subtype); | 278 "larger than 2**32.\n", cpu_type, cpu_subtype); |
257 return NULL; | 279 return NULL; |
258 } | 280 } |
259 | 281 |
260 string DumpSymbols::Identifier() { | 282 string DumpSymbols::Identifier() { |
261 FileID file_id([object_filename_ fileSystemRepresentation]); | 283 FileID file_id(object_filename_.c_str()); |
262 unsigned char identifier_bytes[16]; | 284 unsigned char identifier_bytes[16]; |
263 cpu_type_t cpu_type = selected_object_file_->cputype; | 285 cpu_type_t cpu_type = selected_object_file_->cputype; |
264 cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype; | 286 cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype; |
265 if (!file_id.MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) { | 287 if (!file_id.MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) { |
266 fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n", | 288 fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n", |
267 [object_filename_ fileSystemRepresentation]); | 289 object_filename_.c_str()); |
268 return ""; | 290 return ""; |
269 } | 291 } |
270 | 292 |
271 char identifier_string[40]; | 293 char identifier_string[40]; |
272 FileID::ConvertIdentifierToString(identifier_bytes, identifier_string, | 294 FileID::ConvertIdentifierToString(identifier_bytes, identifier_string, |
273 sizeof(identifier_string)); | 295 sizeof(identifier_string)); |
274 | 296 |
275 string compacted(identifier_string); | 297 string compacted(identifier_string); |
276 for(size_t i = compacted.find('-'); i != string::npos; | 298 for(size_t i = compacted.find('-'); i != string::npos; |
277 i = compacted.find('-', i)) | 299 i = compacted.find('-', i)) |
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
519 if (object_files_.size() == 1) | 541 if (object_files_.size() == 1) |
520 selected_object_file_ = &object_files_[0]; | 542 selected_object_file_ = &object_files_[0]; |
521 else { | 543 else { |
522 // Look for an object file whose architecture matches our own. | 544 // Look for an object file whose architecture matches our own. |
523 const NXArchInfo *local_arch = NXGetLocalArchInfo(); | 545 const NXArchInfo *local_arch = NXGetLocalArchInfo(); |
524 if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) { | 546 if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) { |
525 fprintf(stderr, "%s: object file contains more than one" | 547 fprintf(stderr, "%s: object file contains more than one" |
526 " architecture, none of which match the current" | 548 " architecture, none of which match the current" |
527 " architecture; specify an architecture explicitly" | 549 " architecture; specify an architecture explicitly" |
528 " with '-a ARCH' to resolve the ambiguity\n", | 550 " with '-a ARCH' to resolve the ambiguity\n", |
529 [object_filename_ fileSystemRepresentation]); | 551 object_filename_.c_str()); |
530 return false; | 552 return false; |
531 } | 553 } |
532 } | 554 } |
533 } | 555 } |
534 | 556 |
535 assert(selected_object_file_); | 557 assert(selected_object_file_); |
536 | 558 |
537 // Find the name of the selected file's architecture, to appear in | 559 // Find the name of the selected file's architecture, to appear in |
538 // the MODULE record and in error messages. | 560 // the MODULE record and in error messages. |
539 const NXArchInfo *selected_arch_info = | 561 const NXArchInfo *selected_arch_info = |
540 google_breakpad::BreakpadGetArchInfoFromCpuType( | 562 google_breakpad::BreakpadGetArchInfoFromCpuType( |
541 selected_object_file_->cputype, selected_object_file_->cpusubtype); | 563 selected_object_file_->cputype, selected_object_file_->cpusubtype); |
542 | 564 |
543 const char *selected_arch_name = selected_arch_info->name; | 565 const char *selected_arch_name = selected_arch_info->name; |
544 if (strcmp(selected_arch_name, "i386") == 0) | 566 if (strcmp(selected_arch_name, "i386") == 0) |
545 selected_arch_name = "x86"; | 567 selected_arch_name = "x86"; |
546 | 568 |
547 // Produce a name to use in error messages that includes the | 569 // Produce a name to use in error messages that includes the |
548 // filename, and the architecture, if there is more than one. | 570 // filename, and the architecture, if there is more than one. |
549 selected_object_name_ = [object_filename_ UTF8String]; | 571 selected_object_name_ = object_filename_; |
550 if (object_files_.size() > 1) { | 572 if (object_files_.size() > 1) { |
551 selected_object_name_ += ", architecture "; | 573 selected_object_name_ += ", architecture "; |
552 selected_object_name_ + selected_arch_name; | 574 selected_object_name_ + selected_arch_name; |
553 } | 575 } |
554 | 576 |
555 // Compute a module name, to appear in the MODULE record. | 577 // Compute a module name, to appear in the MODULE record. |
556 NSString *module_name = [object_filename_ lastPathComponent]; | 578 string module_name = object_filename_; |
579 module_name = basename(&module_name[0]); | |
557 | 580 |
558 // Choose an identifier string, to appear in the MODULE record. | 581 // Choose an identifier string, to appear in the MODULE record. |
559 string identifier = Identifier(); | 582 string identifier = Identifier(); |
560 if (identifier.empty()) | 583 if (identifier.empty()) |
561 return false; | 584 return false; |
562 identifier += "0"; | 585 identifier += "0"; |
563 | 586 |
564 // Create a module to hold the debugging information. | 587 // Create a module to hold the debugging information. |
565 scoped_ptr<Module> module(new Module([module_name UTF8String], | 588 scoped_ptr<Module> module(new Module(module_name, |
566 "mac", | 589 "mac", |
567 selected_arch_name, | 590 selected_arch_name, |
568 identifier)); | 591 identifier)); |
569 | 592 |
570 // Parse the selected object file. | 593 // Parse the selected object file. |
571 mach_o::Reader::Reporter reporter(selected_object_name_); | 594 mach_o::Reader::Reporter reporter(selected_object_name_); |
572 mach_o::Reader reader(&reporter); | 595 mach_o::Reader reader(&reporter); |
573 if (!reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes]) | 596 if (!reader.Read(&contents_[0] |
574 + selected_object_file_->offset, | 597 + selected_object_file_->offset, |
575 selected_object_file_->size, | 598 selected_object_file_->size, |
576 selected_object_file_->cputype, | 599 selected_object_file_->cputype, |
577 selected_object_file_->cpusubtype)) | 600 selected_object_file_->cpusubtype)) |
578 return false; | 601 return false; |
579 | 602 |
580 // Walk its load commands, and deal with whatever is there. | 603 // Walk its load commands, and deal with whatever is there. |
581 LoadCommandDumper load_command_dumper(*this, module.get(), reader, | 604 LoadCommandDumper load_command_dumper(*this, module.get(), reader, |
582 symbol_data_, handle_inter_cu_refs_); | 605 symbol_data_, handle_inter_cu_refs_); |
583 if (!reader.WalkLoadCommands(&load_command_dumper)) | 606 if (!reader.WalkLoadCommands(&load_command_dumper)) |
(...skipping 10 matching lines...) Expand all Loading... | |
594 if (ReadSymbolData(&module) && module) { | 617 if (ReadSymbolData(&module) && module) { |
595 bool res = module->Write(stream, symbol_data_); | 618 bool res = module->Write(stream, symbol_data_); |
596 delete module; | 619 delete module; |
597 return res; | 620 return res; |
598 } | 621 } |
599 | 622 |
600 return false; | 623 return false; |
601 } | 624 } |
602 | 625 |
603 } // namespace google_breakpad | 626 } // namespace google_breakpad |
OLD | NEW |