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 <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 |