Chromium Code Reviews| 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 |