Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | // -*- mode: c++ -*- |
michael@0 | 2 | |
michael@0 | 3 | // Copyright (c) 2011, Google Inc. |
michael@0 | 4 | // All rights reserved. |
michael@0 | 5 | // |
michael@0 | 6 | // Redistribution and use in source and binary forms, with or without |
michael@0 | 7 | // modification, are permitted provided that the following conditions are |
michael@0 | 8 | // met: |
michael@0 | 9 | // |
michael@0 | 10 | // * Redistributions of source code must retain the above copyright |
michael@0 | 11 | // notice, this list of conditions and the following disclaimer. |
michael@0 | 12 | // * Redistributions in binary form must reproduce the above |
michael@0 | 13 | // copyright notice, this list of conditions and the following disclaimer |
michael@0 | 14 | // in the documentation and/or other materials provided with the |
michael@0 | 15 | // distribution. |
michael@0 | 16 | // * Neither the name of Google Inc. nor the names of its |
michael@0 | 17 | // contributors may be used to endorse or promote products derived from |
michael@0 | 18 | // this software without specific prior written permission. |
michael@0 | 19 | // |
michael@0 | 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
michael@0 | 21 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
michael@0 | 22 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
michael@0 | 23 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
michael@0 | 24 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
michael@0 | 25 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
michael@0 | 26 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
michael@0 | 27 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
michael@0 | 28 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
michael@0 | 29 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
michael@0 | 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
michael@0 | 31 | |
michael@0 | 32 | // Author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> |
michael@0 | 33 | |
michael@0 | 34 | // dump_syms.mm: Create a symbol file for use with minidumps |
michael@0 | 35 | |
michael@0 | 36 | #include "common/mac/dump_syms.h" |
michael@0 | 37 | |
michael@0 | 38 | #include <Foundation/Foundation.h> |
michael@0 | 39 | #include <mach-o/arch.h> |
michael@0 | 40 | #include <mach-o/fat.h> |
michael@0 | 41 | #include <stdio.h> |
michael@0 | 42 | |
michael@0 | 43 | #include <ostream> |
michael@0 | 44 | #include <string> |
michael@0 | 45 | #include <vector> |
michael@0 | 46 | |
michael@0 | 47 | #include "common/dwarf/bytereader-inl.h" |
michael@0 | 48 | #include "common/dwarf/dwarf2reader.h" |
michael@0 | 49 | #include "common/dwarf_cfi_to_module.h" |
michael@0 | 50 | #include "common/dwarf_cu_to_module.h" |
michael@0 | 51 | #include "common/dwarf_line_to_module.h" |
michael@0 | 52 | #include "common/mac/file_id.h" |
michael@0 | 53 | #include "common/mac/arch_utilities.h" |
michael@0 | 54 | #include "common/mac/macho_reader.h" |
michael@0 | 55 | #include "common/module.h" |
michael@0 | 56 | #include "common/scoped_ptr.h" |
michael@0 | 57 | #include "common/stabs_reader.h" |
michael@0 | 58 | #include "common/stabs_to_module.h" |
michael@0 | 59 | #include "common/symbol_data.h" |
michael@0 | 60 | |
michael@0 | 61 | #ifndef CPU_TYPE_ARM |
michael@0 | 62 | #define CPU_TYPE_ARM (static_cast<cpu_type_t>(12)) |
michael@0 | 63 | #endif // CPU_TYPE_ARM |
michael@0 | 64 | |
michael@0 | 65 | using dwarf2reader::ByteReader; |
michael@0 | 66 | using google_breakpad::DwarfCUToModule; |
michael@0 | 67 | using google_breakpad::DwarfLineToModule; |
michael@0 | 68 | using google_breakpad::FileID; |
michael@0 | 69 | using google_breakpad::mach_o::FatReader; |
michael@0 | 70 | using google_breakpad::mach_o::Section; |
michael@0 | 71 | using google_breakpad::mach_o::Segment; |
michael@0 | 72 | using google_breakpad::Module; |
michael@0 | 73 | using google_breakpad::StabsReader; |
michael@0 | 74 | using google_breakpad::StabsToModule; |
michael@0 | 75 | using google_breakpad::scoped_ptr; |
michael@0 | 76 | using std::make_pair; |
michael@0 | 77 | using std::pair; |
michael@0 | 78 | using std::string; |
michael@0 | 79 | using std::vector; |
michael@0 | 80 | |
michael@0 | 81 | namespace google_breakpad { |
michael@0 | 82 | |
michael@0 | 83 | bool DumpSymbols::Read(NSString *filename) { |
michael@0 | 84 | if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) { |
michael@0 | 85 | fprintf(stderr, "Object file does not exist: %s\n", |
michael@0 | 86 | [filename fileSystemRepresentation]); |
michael@0 | 87 | return false; |
michael@0 | 88 | } |
michael@0 | 89 | |
michael@0 | 90 | input_pathname_ = [filename retain]; |
michael@0 | 91 | |
michael@0 | 92 | // Does this filename refer to a dSYM bundle? |
michael@0 | 93 | NSBundle *bundle = [NSBundle bundleWithPath:input_pathname_]; |
michael@0 | 94 | |
michael@0 | 95 | if (bundle) { |
michael@0 | 96 | // Filenames referring to bundles usually have names of the form |
michael@0 | 97 | // "<basename>.dSYM"; however, if the user has specified a wrapper |
michael@0 | 98 | // suffix (the WRAPPER_SUFFIX and WRAPPER_EXTENSION build settings), |
michael@0 | 99 | // then the name may have the form "<basename>.<extension>.dSYM". In |
michael@0 | 100 | // either case, the resource name for the file containing the DWARF |
michael@0 | 101 | // info within the bundle is <basename>. |
michael@0 | 102 | // |
michael@0 | 103 | // Since there's no way to tell how much to strip off, remove one |
michael@0 | 104 | // extension at a time, and use the first one that |
michael@0 | 105 | // pathForResource:ofType:inDirectory likes. |
michael@0 | 106 | NSString *base_name = [input_pathname_ lastPathComponent]; |
michael@0 | 107 | NSString *dwarf_resource; |
michael@0 | 108 | |
michael@0 | 109 | do { |
michael@0 | 110 | NSString *new_base_name = [base_name stringByDeletingPathExtension]; |
michael@0 | 111 | |
michael@0 | 112 | // If stringByDeletingPathExtension returned the name unchanged, then |
michael@0 | 113 | // there's nothing more for us to strip off --- lose. |
michael@0 | 114 | if ([new_base_name isEqualToString:base_name]) { |
michael@0 | 115 | fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n", |
michael@0 | 116 | [input_pathname_ fileSystemRepresentation]); |
michael@0 | 117 | return false; |
michael@0 | 118 | } |
michael@0 | 119 | |
michael@0 | 120 | // Take the shortened result as our new base_name. |
michael@0 | 121 | base_name = new_base_name; |
michael@0 | 122 | |
michael@0 | 123 | // Try to find a DWARF resource in the bundle under the new base_name. |
michael@0 | 124 | dwarf_resource = [bundle pathForResource:base_name |
michael@0 | 125 | ofType:nil inDirectory:@"DWARF"]; |
michael@0 | 126 | } while (!dwarf_resource); |
michael@0 | 127 | |
michael@0 | 128 | object_filename_ = [dwarf_resource retain]; |
michael@0 | 129 | } else { |
michael@0 | 130 | object_filename_ = [input_pathname_ retain]; |
michael@0 | 131 | } |
michael@0 | 132 | |
michael@0 | 133 | // Read the file's contents into memory. |
michael@0 | 134 | // |
michael@0 | 135 | // The documentation for dataWithContentsOfMappedFile says: |
michael@0 | 136 | // |
michael@0 | 137 | // Because of file mapping restrictions, this method should only be |
michael@0 | 138 | // used if the file is guaranteed to exist for the duration of the |
michael@0 | 139 | // data object’s existence. It is generally safer to use the |
michael@0 | 140 | // dataWithContentsOfFile: method. |
michael@0 | 141 | // |
michael@0 | 142 | // I gather this means that OS X doesn't have (or at least, that method |
michael@0 | 143 | // doesn't use) a form of mapping like Linux's MAP_PRIVATE, where the |
michael@0 | 144 | // process appears to get its own copy of the data, and changes to the |
michael@0 | 145 | // file don't affect memory and vice versa). |
michael@0 | 146 | NSError *error; |
michael@0 | 147 | contents_ = [NSData dataWithContentsOfFile:object_filename_ |
michael@0 | 148 | options:0 |
michael@0 | 149 | error:&error]; |
michael@0 | 150 | if (!contents_) { |
michael@0 | 151 | fprintf(stderr, "Error reading object file: %s: %s\n", |
michael@0 | 152 | [object_filename_ fileSystemRepresentation], |
michael@0 | 153 | [[error localizedDescription] UTF8String]); |
michael@0 | 154 | return false; |
michael@0 | 155 | } |
michael@0 | 156 | [contents_ retain]; |
michael@0 | 157 | |
michael@0 | 158 | // Get the list of object files present in the file. |
michael@0 | 159 | FatReader::Reporter fat_reporter([object_filename_ |
michael@0 | 160 | fileSystemRepresentation]); |
michael@0 | 161 | FatReader fat_reader(&fat_reporter); |
michael@0 | 162 | if (!fat_reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes]), |
michael@0 | 163 | [contents_ length])) { |
michael@0 | 164 | return false; |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | // Get our own copy of fat_reader's object file list. |
michael@0 | 168 | size_t object_files_count; |
michael@0 | 169 | const struct fat_arch *object_files = |
michael@0 | 170 | fat_reader.object_files(&object_files_count); |
michael@0 | 171 | if (object_files_count == 0) { |
michael@0 | 172 | fprintf(stderr, "Fat binary file contains *no* architectures: %s\n", |
michael@0 | 173 | [object_filename_ fileSystemRepresentation]); |
michael@0 | 174 | return false; |
michael@0 | 175 | } |
michael@0 | 176 | object_files_.resize(object_files_count); |
michael@0 | 177 | memcpy(&object_files_[0], object_files, |
michael@0 | 178 | sizeof(struct fat_arch) * object_files_count); |
michael@0 | 179 | |
michael@0 | 180 | return true; |
michael@0 | 181 | } |
michael@0 | 182 | |
michael@0 | 183 | bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type, |
michael@0 | 184 | cpu_subtype_t cpu_subtype) { |
michael@0 | 185 | // Find the best match for the architecture the user requested. |
michael@0 | 186 | const struct fat_arch *best_match |
michael@0 | 187 | = NXFindBestFatArch(cpu_type, cpu_subtype, &object_files_[0], |
michael@0 | 188 | static_cast<uint32_t>(object_files_.size())); |
michael@0 | 189 | if (!best_match) return false; |
michael@0 | 190 | |
michael@0 | 191 | // Record the selected object file. |
michael@0 | 192 | selected_object_file_ = best_match; |
michael@0 | 193 | return true; |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | bool DumpSymbols::SetArchitecture(const std::string &arch_name) { |
michael@0 | 197 | bool arch_set = false; |
michael@0 | 198 | const NXArchInfo *arch_info = |
michael@0 | 199 | google_breakpad::BreakpadGetArchInfoFromName(arch_name.c_str()); |
michael@0 | 200 | if (arch_info) { |
michael@0 | 201 | arch_set = SetArchitecture(arch_info->cputype, arch_info->cpusubtype); |
michael@0 | 202 | } |
michael@0 | 203 | return arch_set; |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | string DumpSymbols::Identifier() { |
michael@0 | 207 | FileID file_id([object_filename_ fileSystemRepresentation]); |
michael@0 | 208 | unsigned char identifier_bytes[16]; |
michael@0 | 209 | cpu_type_t cpu_type = selected_object_file_->cputype; |
michael@0 | 210 | cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype; |
michael@0 | 211 | if (!file_id.MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) { |
michael@0 | 212 | fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n", |
michael@0 | 213 | [object_filename_ fileSystemRepresentation]); |
michael@0 | 214 | return ""; |
michael@0 | 215 | } |
michael@0 | 216 | |
michael@0 | 217 | char identifier_string[40]; |
michael@0 | 218 | FileID::ConvertIdentifierToString(identifier_bytes, identifier_string, |
michael@0 | 219 | sizeof(identifier_string)); |
michael@0 | 220 | |
michael@0 | 221 | string compacted(identifier_string); |
michael@0 | 222 | for(size_t i = compacted.find('-'); i != string::npos; |
michael@0 | 223 | i = compacted.find('-', i)) |
michael@0 | 224 | compacted.erase(i, 1); |
michael@0 | 225 | |
michael@0 | 226 | return compacted; |
michael@0 | 227 | } |
michael@0 | 228 | |
michael@0 | 229 | // A line-to-module loader that accepts line number info parsed by |
michael@0 | 230 | // dwarf2reader::LineInfo and populates a Module and a line vector |
michael@0 | 231 | // with the results. |
michael@0 | 232 | class DumpSymbols::DumperLineToModule: |
michael@0 | 233 | public DwarfCUToModule::LineToModuleHandler { |
michael@0 | 234 | public: |
michael@0 | 235 | // Create a line-to-module converter using BYTE_READER. |
michael@0 | 236 | DumperLineToModule(dwarf2reader::ByteReader *byte_reader) |
michael@0 | 237 | : byte_reader_(byte_reader) { } |
michael@0 | 238 | |
michael@0 | 239 | void StartCompilationUnit(const string& compilation_dir) { |
michael@0 | 240 | compilation_dir_ = compilation_dir; |
michael@0 | 241 | } |
michael@0 | 242 | |
michael@0 | 243 | void ReadProgram(const char *program, uint64 length, |
michael@0 | 244 | Module *module, vector<Module::Line> *lines) { |
michael@0 | 245 | DwarfLineToModule handler(module, compilation_dir_, lines); |
michael@0 | 246 | dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); |
michael@0 | 247 | parser.Start(); |
michael@0 | 248 | } |
michael@0 | 249 | private: |
michael@0 | 250 | string compilation_dir_; |
michael@0 | 251 | dwarf2reader::ByteReader *byte_reader_; // WEAK |
michael@0 | 252 | }; |
michael@0 | 253 | |
michael@0 | 254 | bool DumpSymbols::ReadDwarf(google_breakpad::Module *module, |
michael@0 | 255 | const mach_o::Reader &macho_reader, |
michael@0 | 256 | const mach_o::SectionMap &dwarf_sections) const { |
michael@0 | 257 | // Build a byte reader of the appropriate endianness. |
michael@0 | 258 | ByteReader byte_reader(macho_reader.big_endian() |
michael@0 | 259 | ? dwarf2reader::ENDIANNESS_BIG |
michael@0 | 260 | : dwarf2reader::ENDIANNESS_LITTLE); |
michael@0 | 261 | |
michael@0 | 262 | // Construct a context for this file. |
michael@0 | 263 | DwarfCUToModule::FileContext file_context(selected_object_name_, |
michael@0 | 264 | module); |
michael@0 | 265 | |
michael@0 | 266 | // Build a dwarf2reader::SectionMap from our mach_o::SectionMap. |
michael@0 | 267 | for (mach_o::SectionMap::const_iterator it = dwarf_sections.begin(); |
michael@0 | 268 | it != dwarf_sections.end(); it++) { |
michael@0 | 269 | file_context.section_map[it->first] = |
michael@0 | 270 | make_pair(reinterpret_cast<const char *>(it->second.contents.start), |
michael@0 | 271 | it->second.contents.Size()); |
michael@0 | 272 | } |
michael@0 | 273 | |
michael@0 | 274 | // Find the __debug_info section. |
michael@0 | 275 | std::pair<const char *, uint64> debug_info_section |
michael@0 | 276 | = file_context.section_map["__debug_info"]; |
michael@0 | 277 | // There had better be a __debug_info section! |
michael@0 | 278 | if (!debug_info_section.first) { |
michael@0 | 279 | fprintf(stderr, "%s: __DWARF segment of file has no __debug_info section\n", |
michael@0 | 280 | selected_object_name_.c_str()); |
michael@0 | 281 | return false; |
michael@0 | 282 | } |
michael@0 | 283 | |
michael@0 | 284 | // Build a line-to-module loader for the root handler to use. |
michael@0 | 285 | DumperLineToModule line_to_module(&byte_reader); |
michael@0 | 286 | |
michael@0 | 287 | // Walk the __debug_info section, one compilation unit at a time. |
michael@0 | 288 | uint64 debug_info_length = debug_info_section.second; |
michael@0 | 289 | for (uint64 offset = 0; offset < debug_info_length;) { |
michael@0 | 290 | // Make a handler for the root DIE that populates MODULE with the |
michael@0 | 291 | // debug info. |
michael@0 | 292 | DwarfCUToModule::WarningReporter reporter(selected_object_name_, |
michael@0 | 293 | offset); |
michael@0 | 294 | DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter); |
michael@0 | 295 | // Make a Dwarf2Handler that drives our DIEHandler. |
michael@0 | 296 | dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); |
michael@0 | 297 | // Make a DWARF parser for the compilation unit at OFFSET. |
michael@0 | 298 | dwarf2reader::CompilationUnit dwarf_reader(file_context.section_map, |
michael@0 | 299 | offset, |
michael@0 | 300 | &byte_reader, |
michael@0 | 301 | &die_dispatcher); |
michael@0 | 302 | // Process the entire compilation unit; get the offset of the next. |
michael@0 | 303 | offset += dwarf_reader.Start(); |
michael@0 | 304 | } |
michael@0 | 305 | |
michael@0 | 306 | return true; |
michael@0 | 307 | } |
michael@0 | 308 | |
michael@0 | 309 | bool DumpSymbols::ReadCFI(google_breakpad::Module *module, |
michael@0 | 310 | const mach_o::Reader &macho_reader, |
michael@0 | 311 | const mach_o::Section §ion, |
michael@0 | 312 | bool eh_frame) const { |
michael@0 | 313 | // Find the appropriate set of register names for this file's |
michael@0 | 314 | // architecture. |
michael@0 | 315 | vector<const UniqueString*> register_names; |
michael@0 | 316 | switch (macho_reader.cpu_type()) { |
michael@0 | 317 | case CPU_TYPE_X86: |
michael@0 | 318 | register_names = DwarfCFIToModule::RegisterNames::I386(); |
michael@0 | 319 | break; |
michael@0 | 320 | case CPU_TYPE_X86_64: |
michael@0 | 321 | register_names = DwarfCFIToModule::RegisterNames::X86_64(); |
michael@0 | 322 | break; |
michael@0 | 323 | case CPU_TYPE_ARM: |
michael@0 | 324 | register_names = DwarfCFIToModule::RegisterNames::ARM(); |
michael@0 | 325 | break; |
michael@0 | 326 | default: { |
michael@0 | 327 | const NXArchInfo *arch = google_breakpad::BreakpadGetArchInfoFromCpuType( |
michael@0 | 328 | macho_reader.cpu_type(), macho_reader.cpu_subtype()); |
michael@0 | 329 | fprintf(stderr, "%s: cannot convert DWARF call frame information for ", |
michael@0 | 330 | selected_object_name_.c_str()); |
michael@0 | 331 | if (arch) |
michael@0 | 332 | fprintf(stderr, "architecture '%s'", arch->name); |
michael@0 | 333 | else |
michael@0 | 334 | fprintf(stderr, "architecture %d,%d", |
michael@0 | 335 | macho_reader.cpu_type(), macho_reader.cpu_subtype()); |
michael@0 | 336 | fprintf(stderr, " to Breakpad symbol file: no register name table\n"); |
michael@0 | 337 | return false; |
michael@0 | 338 | } |
michael@0 | 339 | } |
michael@0 | 340 | |
michael@0 | 341 | // Find the call frame information and its size. |
michael@0 | 342 | const char *cfi = reinterpret_cast<const char *>(section.contents.start); |
michael@0 | 343 | size_t cfi_size = section.contents.Size(); |
michael@0 | 344 | |
michael@0 | 345 | // Plug together the parser, handler, and their entourages. |
michael@0 | 346 | DwarfCFIToModule::Reporter module_reporter(selected_object_name_, |
michael@0 | 347 | section.section_name); |
michael@0 | 348 | DwarfCFIToModule handler(module, register_names, &module_reporter); |
michael@0 | 349 | dwarf2reader::ByteReader byte_reader(macho_reader.big_endian() ? |
michael@0 | 350 | dwarf2reader::ENDIANNESS_BIG : |
michael@0 | 351 | dwarf2reader::ENDIANNESS_LITTLE); |
michael@0 | 352 | byte_reader.SetAddressSize(macho_reader.bits_64() ? 8 : 4); |
michael@0 | 353 | // At the moment, according to folks at Apple and some cursory |
michael@0 | 354 | // investigation, Mac OS X only uses DW_EH_PE_pcrel-based pointers, so |
michael@0 | 355 | // this is the only base address the CFI parser will need. |
michael@0 | 356 | byte_reader.SetCFIDataBase(section.address, cfi); |
michael@0 | 357 | |
michael@0 | 358 | dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(selected_object_name_, |
michael@0 | 359 | section.section_name); |
michael@0 | 360 | dwarf2reader::CallFrameInfo parser(cfi, cfi_size, |
michael@0 | 361 | &byte_reader, &handler, &dwarf_reporter, |
michael@0 | 362 | eh_frame); |
michael@0 | 363 | parser.Start(); |
michael@0 | 364 | return true; |
michael@0 | 365 | } |
michael@0 | 366 | |
michael@0 | 367 | // A LoadCommandHandler that loads whatever debugging data it finds into a |
michael@0 | 368 | // Module. |
michael@0 | 369 | class DumpSymbols::LoadCommandDumper: |
michael@0 | 370 | public mach_o::Reader::LoadCommandHandler { |
michael@0 | 371 | public: |
michael@0 | 372 | // Create a load command dumper handling load commands from READER's |
michael@0 | 373 | // file, and adding data to MODULE. |
michael@0 | 374 | LoadCommandDumper(const DumpSymbols &dumper, |
michael@0 | 375 | google_breakpad::Module *module, |
michael@0 | 376 | const mach_o::Reader &reader, |
michael@0 | 377 | SymbolData symbol_data) |
michael@0 | 378 | : dumper_(dumper), |
michael@0 | 379 | module_(module), |
michael@0 | 380 | reader_(reader), |
michael@0 | 381 | symbol_data_(symbol_data) { } |
michael@0 | 382 | |
michael@0 | 383 | bool SegmentCommand(const mach_o::Segment &segment); |
michael@0 | 384 | bool SymtabCommand(const ByteBuffer &entries, const ByteBuffer &strings); |
michael@0 | 385 | |
michael@0 | 386 | private: |
michael@0 | 387 | const DumpSymbols &dumper_; |
michael@0 | 388 | google_breakpad::Module *module_; // WEAK |
michael@0 | 389 | const mach_o::Reader &reader_; |
michael@0 | 390 | const SymbolData symbol_data_; |
michael@0 | 391 | }; |
michael@0 | 392 | |
michael@0 | 393 | bool DumpSymbols::LoadCommandDumper::SegmentCommand(const Segment &segment) { |
michael@0 | 394 | mach_o::SectionMap section_map; |
michael@0 | 395 | if (!reader_.MapSegmentSections(segment, §ion_map)) |
michael@0 | 396 | return false; |
michael@0 | 397 | |
michael@0 | 398 | if (segment.name == "__TEXT" && symbol_data_ != NO_CFI) { |
michael@0 | 399 | module_->SetLoadAddress(segment.vmaddr); |
michael@0 | 400 | mach_o::SectionMap::const_iterator eh_frame = |
michael@0 | 401 | section_map.find("__eh_frame"); |
michael@0 | 402 | if (eh_frame != section_map.end()) { |
michael@0 | 403 | // If there is a problem reading this, don't treat it as a fatal error. |
michael@0 | 404 | dumper_.ReadCFI(module_, reader_, eh_frame->second, true); |
michael@0 | 405 | } |
michael@0 | 406 | return true; |
michael@0 | 407 | } |
michael@0 | 408 | |
michael@0 | 409 | if (segment.name == "__DWARF") { |
michael@0 | 410 | if (symbol_data_ != ONLY_CFI) { |
michael@0 | 411 | if (!dumper_.ReadDwarf(module_, reader_, section_map)) |
michael@0 | 412 | return false; |
michael@0 | 413 | } |
michael@0 | 414 | if (symbol_data_ != NO_CFI) { |
michael@0 | 415 | mach_o::SectionMap::const_iterator debug_frame |
michael@0 | 416 | = section_map.find("__debug_frame"); |
michael@0 | 417 | if (debug_frame != section_map.end()) { |
michael@0 | 418 | // If there is a problem reading this, don't treat it as a fatal error. |
michael@0 | 419 | dumper_.ReadCFI(module_, reader_, debug_frame->second, false); |
michael@0 | 420 | } |
michael@0 | 421 | } |
michael@0 | 422 | } |
michael@0 | 423 | |
michael@0 | 424 | return true; |
michael@0 | 425 | } |
michael@0 | 426 | |
michael@0 | 427 | bool DumpSymbols::LoadCommandDumper::SymtabCommand(const ByteBuffer &entries, |
michael@0 | 428 | const ByteBuffer &strings) { |
michael@0 | 429 | StabsToModule stabs_to_module(module_); |
michael@0 | 430 | // Mac OS X STABS are never "unitized", and the size of the 'value' field |
michael@0 | 431 | // matches the address size of the executable. |
michael@0 | 432 | StabsReader stabs_reader(entries.start, entries.Size(), |
michael@0 | 433 | strings.start, strings.Size(), |
michael@0 | 434 | reader_.big_endian(), |
michael@0 | 435 | reader_.bits_64() ? 8 : 4, |
michael@0 | 436 | true, |
michael@0 | 437 | &stabs_to_module); |
michael@0 | 438 | if (!stabs_reader.Process()) |
michael@0 | 439 | return false; |
michael@0 | 440 | stabs_to_module.Finalize(); |
michael@0 | 441 | return true; |
michael@0 | 442 | } |
michael@0 | 443 | |
michael@0 | 444 | bool DumpSymbols::ReadSymbolData(Module** out_module) { |
michael@0 | 445 | // Select an object file, if SetArchitecture hasn't been called to set one |
michael@0 | 446 | // explicitly. |
michael@0 | 447 | if (!selected_object_file_) { |
michael@0 | 448 | // If there's only one architecture, that's the one. |
michael@0 | 449 | if (object_files_.size() == 1) |
michael@0 | 450 | selected_object_file_ = &object_files_[0]; |
michael@0 | 451 | else { |
michael@0 | 452 | // Look for an object file whose architecture matches our own. |
michael@0 | 453 | const NXArchInfo *local_arch = NXGetLocalArchInfo(); |
michael@0 | 454 | if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) { |
michael@0 | 455 | fprintf(stderr, "%s: object file contains more than one" |
michael@0 | 456 | " architecture, none of which match the current" |
michael@0 | 457 | " architecture; specify an architecture explicitly" |
michael@0 | 458 | " with '-a ARCH' to resolve the ambiguity\n", |
michael@0 | 459 | [object_filename_ fileSystemRepresentation]); |
michael@0 | 460 | return false; |
michael@0 | 461 | } |
michael@0 | 462 | } |
michael@0 | 463 | } |
michael@0 | 464 | |
michael@0 | 465 | assert(selected_object_file_); |
michael@0 | 466 | |
michael@0 | 467 | // Find the name of the selected file's architecture, to appear in |
michael@0 | 468 | // the MODULE record and in error messages. |
michael@0 | 469 | const NXArchInfo *selected_arch_info = |
michael@0 | 470 | google_breakpad::BreakpadGetArchInfoFromCpuType( |
michael@0 | 471 | selected_object_file_->cputype, selected_object_file_->cpusubtype); |
michael@0 | 472 | |
michael@0 | 473 | const char *selected_arch_name = selected_arch_info->name; |
michael@0 | 474 | if (strcmp(selected_arch_name, "i386") == 0) |
michael@0 | 475 | selected_arch_name = "x86"; |
michael@0 | 476 | |
michael@0 | 477 | // Produce a name to use in error messages that includes the |
michael@0 | 478 | // filename, and the architecture, if there is more than one. |
michael@0 | 479 | selected_object_name_ = [object_filename_ UTF8String]; |
michael@0 | 480 | if (object_files_.size() > 1) { |
michael@0 | 481 | selected_object_name_ += ", architecture "; |
michael@0 | 482 | selected_object_name_ + selected_arch_name; |
michael@0 | 483 | } |
michael@0 | 484 | |
michael@0 | 485 | // Compute a module name, to appear in the MODULE record. |
michael@0 | 486 | NSString *module_name = [object_filename_ lastPathComponent]; |
michael@0 | 487 | |
michael@0 | 488 | // Choose an identifier string, to appear in the MODULE record. |
michael@0 | 489 | string identifier = Identifier(); |
michael@0 | 490 | if (identifier.empty()) |
michael@0 | 491 | return false; |
michael@0 | 492 | identifier += "0"; |
michael@0 | 493 | |
michael@0 | 494 | // Create a module to hold the debugging information. |
michael@0 | 495 | scoped_ptr<Module> module(new Module([module_name UTF8String], |
michael@0 | 496 | "mac", |
michael@0 | 497 | selected_arch_name, |
michael@0 | 498 | identifier)); |
michael@0 | 499 | |
michael@0 | 500 | // Parse the selected object file. |
michael@0 | 501 | mach_o::Reader::Reporter reporter(selected_object_name_); |
michael@0 | 502 | mach_o::Reader reader(&reporter); |
michael@0 | 503 | if (!reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes]) |
michael@0 | 504 | + selected_object_file_->offset, |
michael@0 | 505 | selected_object_file_->size, |
michael@0 | 506 | selected_object_file_->cputype, |
michael@0 | 507 | selected_object_file_->cpusubtype)) |
michael@0 | 508 | return false; |
michael@0 | 509 | |
michael@0 | 510 | // Walk its load commands, and deal with whatever is there. |
michael@0 | 511 | LoadCommandDumper load_command_dumper(*this, module.get(), reader, |
michael@0 | 512 | symbol_data_); |
michael@0 | 513 | if (!reader.WalkLoadCommands(&load_command_dumper)) |
michael@0 | 514 | return false; |
michael@0 | 515 | |
michael@0 | 516 | *out_module = module.release(); |
michael@0 | 517 | |
michael@0 | 518 | return true; |
michael@0 | 519 | } |
michael@0 | 520 | |
michael@0 | 521 | bool DumpSymbols::WriteSymbolFile(std::ostream &stream) { |
michael@0 | 522 | Module* module = NULL; |
michael@0 | 523 | |
michael@0 | 524 | if (ReadSymbolData(&module) && module) { |
michael@0 | 525 | bool res = module->Write(stream, symbol_data_); |
michael@0 | 526 | delete module; |
michael@0 | 527 | return res; |
michael@0 | 528 | } |
michael@0 | 529 | |
michael@0 | 530 | return false; |
michael@0 | 531 | } |
michael@0 | 532 | |
michael@0 | 533 | } // namespace google_breakpad |