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 | // Copyright (c) 2011 Google Inc. |
michael@0 | 2 | // All rights reserved. |
michael@0 | 3 | // |
michael@0 | 4 | // Redistribution and use in source and binary forms, with or without |
michael@0 | 5 | // modification, are permitted provided that the following conditions are |
michael@0 | 6 | // met: |
michael@0 | 7 | // |
michael@0 | 8 | // * Redistributions of source code must retain the above copyright |
michael@0 | 9 | // notice, this list of conditions and the following disclaimer. |
michael@0 | 10 | // * Redistributions in binary form must reproduce the above |
michael@0 | 11 | // copyright notice, this list of conditions and the following disclaimer |
michael@0 | 12 | // in the documentation and/or other materials provided with the |
michael@0 | 13 | // distribution. |
michael@0 | 14 | // * Neither the name of Google Inc. nor the names of its |
michael@0 | 15 | // contributors may be used to endorse or promote products derived from |
michael@0 | 16 | // this software without specific prior written permission. |
michael@0 | 17 | // |
michael@0 | 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
michael@0 | 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
michael@0 | 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
michael@0 | 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
michael@0 | 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
michael@0 | 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
michael@0 | 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
michael@0 | 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
michael@0 | 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
michael@0 | 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
michael@0 | 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
michael@0 | 29 | |
michael@0 | 30 | // Restructured in 2009 by: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> |
michael@0 | 31 | |
michael@0 | 32 | // dump_symbols.cc: implement google_breakpad::WriteSymbolFile: |
michael@0 | 33 | // Find all the debugging info in a file and dump it as a Breakpad symbol file. |
michael@0 | 34 | |
michael@0 | 35 | #include "common/linux/dump_symbols.h" |
michael@0 | 36 | |
michael@0 | 37 | #include <assert.h> |
michael@0 | 38 | #include <elf.h> |
michael@0 | 39 | #include <errno.h> |
michael@0 | 40 | #include <fcntl.h> |
michael@0 | 41 | #include <link.h> |
michael@0 | 42 | #include <stdio.h> |
michael@0 | 43 | #include <stdlib.h> |
michael@0 | 44 | #include <string.h> |
michael@0 | 45 | #include <sys/mman.h> |
michael@0 | 46 | #include <sys/stat.h> |
michael@0 | 47 | #include <unistd.h> |
michael@0 | 48 | |
michael@0 | 49 | #include <iostream> |
michael@0 | 50 | #include <set> |
michael@0 | 51 | #include <string> |
michael@0 | 52 | #include <utility> |
michael@0 | 53 | #include <vector> |
michael@0 | 54 | |
michael@0 | 55 | #include "common/arm_ex_reader.h" |
michael@0 | 56 | #include "common/dwarf/bytereader-inl.h" |
michael@0 | 57 | #include "common/dwarf/dwarf2diehandler.h" |
michael@0 | 58 | #include "common/dwarf_cfi_to_module.h" |
michael@0 | 59 | #include "common/dwarf_cu_to_module.h" |
michael@0 | 60 | #include "common/dwarf_line_to_module.h" |
michael@0 | 61 | #include "common/linux/elfutils.h" |
michael@0 | 62 | #include "common/linux/elfutils-inl.h" |
michael@0 | 63 | #include "common/linux/elf_symbols_to_module.h" |
michael@0 | 64 | #include "common/linux/file_id.h" |
michael@0 | 65 | #include "common/module.h" |
michael@0 | 66 | #include "common/scoped_ptr.h" |
michael@0 | 67 | #ifndef NO_STABS_SUPPORT |
michael@0 | 68 | #include "common/stabs_reader.h" |
michael@0 | 69 | #include "common/stabs_to_module.h" |
michael@0 | 70 | #endif |
michael@0 | 71 | #include "common/using_std_string.h" |
michael@0 | 72 | #include "common/logging.h" |
michael@0 | 73 | |
michael@0 | 74 | #ifndef SHT_ARM_EXIDX |
michael@0 | 75 | // bionic and older glibc don't define it |
michael@0 | 76 | # define SHT_ARM_EXIDX (SHT_LOPROC + 1) |
michael@0 | 77 | #endif |
michael@0 | 78 | |
michael@0 | 79 | // This namespace contains helper functions. |
michael@0 | 80 | namespace { |
michael@0 | 81 | |
michael@0 | 82 | using google_breakpad::DwarfCFIToModule; |
michael@0 | 83 | using google_breakpad::DwarfCUToModule; |
michael@0 | 84 | using google_breakpad::DwarfLineToModule; |
michael@0 | 85 | using google_breakpad::ElfClass; |
michael@0 | 86 | using google_breakpad::ElfClass32; |
michael@0 | 87 | using google_breakpad::ElfClass64; |
michael@0 | 88 | using google_breakpad::FindElfSectionByName; |
michael@0 | 89 | using google_breakpad::GetOffset; |
michael@0 | 90 | using google_breakpad::IsValidElf; |
michael@0 | 91 | using google_breakpad::Module; |
michael@0 | 92 | #ifndef NO_STABS_SUPPORT |
michael@0 | 93 | using google_breakpad::StabsToModule; |
michael@0 | 94 | #endif |
michael@0 | 95 | using google_breakpad::UniqueString; |
michael@0 | 96 | using google_breakpad::scoped_ptr; |
michael@0 | 97 | |
michael@0 | 98 | // |
michael@0 | 99 | // FDWrapper |
michael@0 | 100 | // |
michael@0 | 101 | // Wrapper class to make sure opened file is closed. |
michael@0 | 102 | // |
michael@0 | 103 | class FDWrapper { |
michael@0 | 104 | public: |
michael@0 | 105 | explicit FDWrapper(int fd) : |
michael@0 | 106 | fd_(fd) {} |
michael@0 | 107 | ~FDWrapper() { |
michael@0 | 108 | if (fd_ != -1) |
michael@0 | 109 | close(fd_); |
michael@0 | 110 | } |
michael@0 | 111 | int get() { |
michael@0 | 112 | return fd_; |
michael@0 | 113 | } |
michael@0 | 114 | int release() { |
michael@0 | 115 | int fd = fd_; |
michael@0 | 116 | fd_ = -1; |
michael@0 | 117 | return fd; |
michael@0 | 118 | } |
michael@0 | 119 | private: |
michael@0 | 120 | int fd_; |
michael@0 | 121 | }; |
michael@0 | 122 | |
michael@0 | 123 | // |
michael@0 | 124 | // MmapWrapper |
michael@0 | 125 | // |
michael@0 | 126 | // Wrapper class to make sure mapped regions are unmapped. |
michael@0 | 127 | // |
michael@0 | 128 | class MmapWrapper { |
michael@0 | 129 | public: |
michael@0 | 130 | MmapWrapper() : is_set_(false) {} |
michael@0 | 131 | ~MmapWrapper() { |
michael@0 | 132 | if (is_set_ && base_ != NULL) { |
michael@0 | 133 | assert(size_ > 0); |
michael@0 | 134 | munmap(base_, size_); |
michael@0 | 135 | } |
michael@0 | 136 | } |
michael@0 | 137 | void set(void *mapped_address, size_t mapped_size) { |
michael@0 | 138 | is_set_ = true; |
michael@0 | 139 | base_ = mapped_address; |
michael@0 | 140 | size_ = mapped_size; |
michael@0 | 141 | } |
michael@0 | 142 | void release() { |
michael@0 | 143 | assert(is_set_); |
michael@0 | 144 | is_set_ = false; |
michael@0 | 145 | base_ = NULL; |
michael@0 | 146 | size_ = 0; |
michael@0 | 147 | } |
michael@0 | 148 | |
michael@0 | 149 | private: |
michael@0 | 150 | bool is_set_; |
michael@0 | 151 | void *base_; |
michael@0 | 152 | size_t size_; |
michael@0 | 153 | }; |
michael@0 | 154 | |
michael@0 | 155 | // Find the preferred loading address of the binary. |
michael@0 | 156 | template<typename ElfClass> |
michael@0 | 157 | typename ElfClass::Addr GetLoadingAddress( |
michael@0 | 158 | const typename ElfClass::Phdr* program_headers, |
michael@0 | 159 | int nheader) { |
michael@0 | 160 | typedef typename ElfClass::Phdr Phdr; |
michael@0 | 161 | |
michael@0 | 162 | for (int i = 0; i < nheader; ++i) { |
michael@0 | 163 | const Phdr& header = program_headers[i]; |
michael@0 | 164 | // For executable, it is the PT_LOAD segment with offset to zero. |
michael@0 | 165 | if (header.p_type == PT_LOAD && |
michael@0 | 166 | header.p_offset == 0) |
michael@0 | 167 | return header.p_vaddr; |
michael@0 | 168 | } |
michael@0 | 169 | // For other types of ELF, return 0. |
michael@0 | 170 | return 0; |
michael@0 | 171 | } |
michael@0 | 172 | |
michael@0 | 173 | #ifndef NO_STABS_SUPPORT |
michael@0 | 174 | template<typename ElfClass> |
michael@0 | 175 | bool LoadStabs(const typename ElfClass::Ehdr* elf_header, |
michael@0 | 176 | const typename ElfClass::Shdr* stab_section, |
michael@0 | 177 | const typename ElfClass::Shdr* stabstr_section, |
michael@0 | 178 | const bool big_endian, |
michael@0 | 179 | Module* module) { |
michael@0 | 180 | // A callback object to handle data from the STABS reader. |
michael@0 | 181 | StabsToModule handler(module); |
michael@0 | 182 | // Find the addresses of the STABS data, and create a STABS reader object. |
michael@0 | 183 | // On Linux, STABS entries always have 32-bit values, regardless of the |
michael@0 | 184 | // address size of the architecture whose code they're describing, and |
michael@0 | 185 | // the strings are always "unitized". |
michael@0 | 186 | const uint8_t* stabs = |
michael@0 | 187 | GetOffset<ElfClass, uint8_t>(elf_header, stab_section->sh_offset); |
michael@0 | 188 | const uint8_t* stabstr = |
michael@0 | 189 | GetOffset<ElfClass, uint8_t>(elf_header, stabstr_section->sh_offset); |
michael@0 | 190 | google_breakpad::StabsReader reader(stabs, stab_section->sh_size, |
michael@0 | 191 | stabstr, stabstr_section->sh_size, |
michael@0 | 192 | big_endian, 4, true, &handler); |
michael@0 | 193 | // Read the STABS data, and do post-processing. |
michael@0 | 194 | if (!reader.Process()) |
michael@0 | 195 | return false; |
michael@0 | 196 | handler.Finalize(); |
michael@0 | 197 | return true; |
michael@0 | 198 | } |
michael@0 | 199 | #endif // NO_STABS_SUPPORT |
michael@0 | 200 | |
michael@0 | 201 | // A line-to-module loader that accepts line number info parsed by |
michael@0 | 202 | // dwarf2reader::LineInfo and populates a Module and a line vector |
michael@0 | 203 | // with the results. |
michael@0 | 204 | class DumperLineToModule: public DwarfCUToModule::LineToModuleHandler { |
michael@0 | 205 | public: |
michael@0 | 206 | // Create a line-to-module converter using BYTE_READER. |
michael@0 | 207 | explicit DumperLineToModule(dwarf2reader::ByteReader *byte_reader) |
michael@0 | 208 | : byte_reader_(byte_reader) { } |
michael@0 | 209 | void StartCompilationUnit(const string& compilation_dir) { |
michael@0 | 210 | compilation_dir_ = compilation_dir; |
michael@0 | 211 | } |
michael@0 | 212 | void ReadProgram(const char *program, uint64 length, |
michael@0 | 213 | Module *module, std::vector<Module::Line> *lines) { |
michael@0 | 214 | DwarfLineToModule handler(module, compilation_dir_, lines); |
michael@0 | 215 | dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); |
michael@0 | 216 | parser.Start(); |
michael@0 | 217 | } |
michael@0 | 218 | private: |
michael@0 | 219 | string compilation_dir_; |
michael@0 | 220 | dwarf2reader::ByteReader *byte_reader_; |
michael@0 | 221 | }; |
michael@0 | 222 | |
michael@0 | 223 | template<typename ElfClass> |
michael@0 | 224 | bool LoadDwarf(const string& dwarf_filename, |
michael@0 | 225 | const typename ElfClass::Ehdr* elf_header, |
michael@0 | 226 | const bool big_endian, |
michael@0 | 227 | Module* module) { |
michael@0 | 228 | typedef typename ElfClass::Shdr Shdr; |
michael@0 | 229 | |
michael@0 | 230 | const dwarf2reader::Endianness endianness = big_endian ? |
michael@0 | 231 | dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE; |
michael@0 | 232 | dwarf2reader::ByteReader byte_reader(endianness); |
michael@0 | 233 | |
michael@0 | 234 | // Construct a context for this file. |
michael@0 | 235 | DwarfCUToModule::FileContext file_context(dwarf_filename, module); |
michael@0 | 236 | |
michael@0 | 237 | // Build a map of the ELF file's sections. |
michael@0 | 238 | const Shdr* sections = |
michael@0 | 239 | GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff); |
michael@0 | 240 | int num_sections = elf_header->e_shnum; |
michael@0 | 241 | const Shdr* section_names = sections + elf_header->e_shstrndx; |
michael@0 | 242 | for (int i = 0; i < num_sections; i++) { |
michael@0 | 243 | const Shdr* section = §ions[i]; |
michael@0 | 244 | string name = GetOffset<ElfClass, char>(elf_header, |
michael@0 | 245 | section_names->sh_offset) + |
michael@0 | 246 | section->sh_name; |
michael@0 | 247 | const char* contents = GetOffset<ElfClass, char>(elf_header, |
michael@0 | 248 | section->sh_offset); |
michael@0 | 249 | uint64 length = section->sh_size; |
michael@0 | 250 | file_context.section_map[name] = std::make_pair(contents, length); |
michael@0 | 251 | } |
michael@0 | 252 | |
michael@0 | 253 | // Parse all the compilation units in the .debug_info section. |
michael@0 | 254 | DumperLineToModule line_to_module(&byte_reader); |
michael@0 | 255 | std::pair<const char *, uint64> debug_info_section |
michael@0 | 256 | = file_context.section_map[".debug_info"]; |
michael@0 | 257 | // This should never have been called if the file doesn't have a |
michael@0 | 258 | // .debug_info section. |
michael@0 | 259 | assert(debug_info_section.first); |
michael@0 | 260 | uint64 debug_info_length = debug_info_section.second; |
michael@0 | 261 | for (uint64 offset = 0; offset < debug_info_length;) { |
michael@0 | 262 | // Make a handler for the root DIE that populates MODULE with the |
michael@0 | 263 | // data that was found. |
michael@0 | 264 | DwarfCUToModule::WarningReporter reporter(dwarf_filename, offset); |
michael@0 | 265 | DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter); |
michael@0 | 266 | // Make a Dwarf2Handler that drives the DIEHandler. |
michael@0 | 267 | dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); |
michael@0 | 268 | // Make a DWARF parser for the compilation unit at OFFSET. |
michael@0 | 269 | dwarf2reader::CompilationUnit reader(file_context.section_map, |
michael@0 | 270 | offset, |
michael@0 | 271 | &byte_reader, |
michael@0 | 272 | &die_dispatcher); |
michael@0 | 273 | // Process the entire compilation unit; get the offset of the next. |
michael@0 | 274 | offset += reader.Start(); |
michael@0 | 275 | } |
michael@0 | 276 | return true; |
michael@0 | 277 | } |
michael@0 | 278 | |
michael@0 | 279 | // Fill REGISTER_NAMES with the register names appropriate to the |
michael@0 | 280 | // machine architecture given in HEADER, indexed by the register |
michael@0 | 281 | // numbers used in DWARF call frame information. Return true on |
michael@0 | 282 | // success, or false if HEADER's machine architecture is not |
michael@0 | 283 | // supported. |
michael@0 | 284 | template<typename ElfClass> |
michael@0 | 285 | bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header, |
michael@0 | 286 | std::vector<const UniqueString*>* register_names) { |
michael@0 | 287 | switch (elf_header->e_machine) { |
michael@0 | 288 | case EM_386: |
michael@0 | 289 | *register_names = DwarfCFIToModule::RegisterNames::I386(); |
michael@0 | 290 | return true; |
michael@0 | 291 | case EM_ARM: |
michael@0 | 292 | *register_names = DwarfCFIToModule::RegisterNames::ARM(); |
michael@0 | 293 | return true; |
michael@0 | 294 | case EM_X86_64: |
michael@0 | 295 | *register_names = DwarfCFIToModule::RegisterNames::X86_64(); |
michael@0 | 296 | return true; |
michael@0 | 297 | default: |
michael@0 | 298 | return false; |
michael@0 | 299 | } |
michael@0 | 300 | } |
michael@0 | 301 | |
michael@0 | 302 | template<typename ElfClass> |
michael@0 | 303 | bool LoadDwarfCFI(const string& dwarf_filename, |
michael@0 | 304 | const typename ElfClass::Ehdr* elf_header, |
michael@0 | 305 | const char* section_name, |
michael@0 | 306 | const typename ElfClass::Shdr* section, |
michael@0 | 307 | const bool eh_frame, |
michael@0 | 308 | const typename ElfClass::Shdr* got_section, |
michael@0 | 309 | const typename ElfClass::Shdr* text_section, |
michael@0 | 310 | const bool big_endian, |
michael@0 | 311 | Module* module) { |
michael@0 | 312 | // Find the appropriate set of register names for this file's |
michael@0 | 313 | // architecture. |
michael@0 | 314 | std::vector<const UniqueString*> register_names; |
michael@0 | 315 | if (!DwarfCFIRegisterNames<ElfClass>(elf_header, ®ister_names)) { |
michael@0 | 316 | fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';" |
michael@0 | 317 | " cannot convert DWARF call frame information\n", |
michael@0 | 318 | dwarf_filename.c_str(), elf_header->e_machine); |
michael@0 | 319 | return false; |
michael@0 | 320 | } |
michael@0 | 321 | |
michael@0 | 322 | const dwarf2reader::Endianness endianness = big_endian ? |
michael@0 | 323 | dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE; |
michael@0 | 324 | |
michael@0 | 325 | // Find the call frame information and its size. |
michael@0 | 326 | const char* cfi = |
michael@0 | 327 | GetOffset<ElfClass, char>(elf_header, section->sh_offset); |
michael@0 | 328 | size_t cfi_size = section->sh_size; |
michael@0 | 329 | |
michael@0 | 330 | // Plug together the parser, handler, and their entourages. |
michael@0 | 331 | DwarfCFIToModule::Reporter module_reporter(dwarf_filename, section_name); |
michael@0 | 332 | DwarfCFIToModule handler(module, register_names, &module_reporter); |
michael@0 | 333 | dwarf2reader::ByteReader byte_reader(endianness); |
michael@0 | 334 | |
michael@0 | 335 | byte_reader.SetAddressSize(ElfClass::kAddrSize); |
michael@0 | 336 | |
michael@0 | 337 | // Provide the base addresses for .eh_frame encoded pointers, if |
michael@0 | 338 | // possible. |
michael@0 | 339 | byte_reader.SetCFIDataBase(section->sh_addr, cfi); |
michael@0 | 340 | if (got_section) |
michael@0 | 341 | byte_reader.SetDataBase(got_section->sh_addr); |
michael@0 | 342 | if (text_section) |
michael@0 | 343 | byte_reader.SetTextBase(text_section->sh_addr); |
michael@0 | 344 | |
michael@0 | 345 | dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename, |
michael@0 | 346 | section_name); |
michael@0 | 347 | dwarf2reader::CallFrameInfo parser(cfi, cfi_size, |
michael@0 | 348 | &byte_reader, &handler, &dwarf_reporter, |
michael@0 | 349 | eh_frame); |
michael@0 | 350 | parser.Start(); |
michael@0 | 351 | return true; |
michael@0 | 352 | } |
michael@0 | 353 | |
michael@0 | 354 | template<typename ElfClass> |
michael@0 | 355 | bool LoadARMexidx(const typename ElfClass::Ehdr* elf_header, |
michael@0 | 356 | const typename ElfClass::Shdr* exidx_section, |
michael@0 | 357 | const typename ElfClass::Shdr* extab_section, |
michael@0 | 358 | uint32_t loading_addr, |
michael@0 | 359 | Module* module) { |
michael@0 | 360 | // To do this properly we need to know: |
michael@0 | 361 | // * the bounds of the .ARM.exidx section in the mapped image |
michael@0 | 362 | // * the bounds of the .ARM.extab section in the mapped image |
michael@0 | 363 | // * the vma of the last byte in the text section associated with the .exidx |
michael@0 | 364 | // The first two are easy. The third is a bit tricky. If we can't |
michael@0 | 365 | // figure out what it is, just pass in zero. |
michael@0 | 366 | const char *exidx_img |
michael@0 | 367 | = GetOffset<ElfClass, char>(elf_header, exidx_section->sh_offset); |
michael@0 | 368 | size_t exidx_size = exidx_section->sh_size; |
michael@0 | 369 | const char *extab_img |
michael@0 | 370 | = GetOffset<ElfClass, char>(elf_header, extab_section->sh_offset); |
michael@0 | 371 | size_t extab_size = extab_section->sh_size; |
michael@0 | 372 | |
michael@0 | 373 | // The sh_link field of the exidx section gives the section number |
michael@0 | 374 | // for the associated text section. |
michael@0 | 375 | uint32_t exidx_text_last_svma = 0; |
michael@0 | 376 | int exidx_text_sno = exidx_section->sh_link; |
michael@0 | 377 | typedef typename ElfClass::Shdr Shdr; |
michael@0 | 378 | // |sections| points to the section header table |
michael@0 | 379 | const Shdr* sections |
michael@0 | 380 | = GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff); |
michael@0 | 381 | const int num_sections = elf_header->e_shnum; |
michael@0 | 382 | if (exidx_text_sno >= 0 && exidx_text_sno < num_sections) { |
michael@0 | 383 | const Shdr* exidx_text_shdr = §ions[exidx_text_sno]; |
michael@0 | 384 | if (exidx_text_shdr->sh_size > 0) { |
michael@0 | 385 | exidx_text_last_svma |
michael@0 | 386 | = exidx_text_shdr->sh_addr + exidx_text_shdr->sh_size - 1; |
michael@0 | 387 | } |
michael@0 | 388 | } |
michael@0 | 389 | |
michael@0 | 390 | arm_ex_to_module::ARMExToModule handler(module); |
michael@0 | 391 | arm_ex_reader::ExceptionTableInfo |
michael@0 | 392 | parser(exidx_img, exidx_size, extab_img, extab_size, exidx_text_last_svma, |
michael@0 | 393 | &handler, |
michael@0 | 394 | reinterpret_cast<const char*>(elf_header), |
michael@0 | 395 | loading_addr); |
michael@0 | 396 | parser.Start(); |
michael@0 | 397 | return true; |
michael@0 | 398 | } |
michael@0 | 399 | |
michael@0 | 400 | bool LoadELF(const string& obj_file, MmapWrapper* map_wrapper, |
michael@0 | 401 | void** elf_header) { |
michael@0 | 402 | int obj_fd = open(obj_file.c_str(), O_RDONLY); |
michael@0 | 403 | if (obj_fd < 0) { |
michael@0 | 404 | fprintf(stderr, "Failed to open ELF file '%s': %s\n", |
michael@0 | 405 | obj_file.c_str(), strerror(errno)); |
michael@0 | 406 | return false; |
michael@0 | 407 | } |
michael@0 | 408 | FDWrapper obj_fd_wrapper(obj_fd); |
michael@0 | 409 | struct stat st; |
michael@0 | 410 | if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) { |
michael@0 | 411 | fprintf(stderr, "Unable to fstat ELF file '%s': %s\n", |
michael@0 | 412 | obj_file.c_str(), strerror(errno)); |
michael@0 | 413 | return false; |
michael@0 | 414 | } |
michael@0 | 415 | void *obj_base = mmap(NULL, st.st_size, |
michael@0 | 416 | PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0); |
michael@0 | 417 | if (obj_base == MAP_FAILED) { |
michael@0 | 418 | fprintf(stderr, "Failed to mmap ELF file '%s': %s\n", |
michael@0 | 419 | obj_file.c_str(), strerror(errno)); |
michael@0 | 420 | return false; |
michael@0 | 421 | } |
michael@0 | 422 | map_wrapper->set(obj_base, st.st_size); |
michael@0 | 423 | *elf_header = obj_base; |
michael@0 | 424 | if (!IsValidElf(*elf_header)) { |
michael@0 | 425 | fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str()); |
michael@0 | 426 | return false; |
michael@0 | 427 | } |
michael@0 | 428 | return true; |
michael@0 | 429 | } |
michael@0 | 430 | |
michael@0 | 431 | // Get the endianness of ELF_HEADER. If it's invalid, return false. |
michael@0 | 432 | template<typename ElfClass> |
michael@0 | 433 | bool ElfEndianness(const typename ElfClass::Ehdr* elf_header, |
michael@0 | 434 | bool* big_endian) { |
michael@0 | 435 | if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) { |
michael@0 | 436 | *big_endian = false; |
michael@0 | 437 | return true; |
michael@0 | 438 | } |
michael@0 | 439 | if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) { |
michael@0 | 440 | *big_endian = true; |
michael@0 | 441 | return true; |
michael@0 | 442 | } |
michael@0 | 443 | |
michael@0 | 444 | fprintf(stderr, "bad data encoding in ELF header: %d\n", |
michael@0 | 445 | elf_header->e_ident[EI_DATA]); |
michael@0 | 446 | return false; |
michael@0 | 447 | } |
michael@0 | 448 | |
michael@0 | 449 | // Read the .gnu_debuglink and get the debug file name. If anything goes |
michael@0 | 450 | // wrong, return an empty string. |
michael@0 | 451 | template<typename ElfClass> |
michael@0 | 452 | string ReadDebugLink(const char* debuglink, |
michael@0 | 453 | size_t debuglink_size, |
michael@0 | 454 | const string& obj_file, |
michael@0 | 455 | const std::vector<string>& debug_dirs) { |
michael@0 | 456 | size_t debuglink_len = strlen(debuglink) + 5; // '\0' + CRC32. |
michael@0 | 457 | debuglink_len = 4 * ((debuglink_len + 3) / 4); // Round to nearest 4 bytes. |
michael@0 | 458 | |
michael@0 | 459 | // Sanity check. |
michael@0 | 460 | if (debuglink_len != debuglink_size) { |
michael@0 | 461 | fprintf(stderr, "Mismatched .gnu_debuglink string / section size: " |
michael@0 | 462 | "%zx %zx\n", debuglink_len, debuglink_size); |
michael@0 | 463 | return ""; |
michael@0 | 464 | } |
michael@0 | 465 | |
michael@0 | 466 | bool found = false; |
michael@0 | 467 | int debuglink_fd = -1; |
michael@0 | 468 | string debuglink_path; |
michael@0 | 469 | std::vector<string>::const_iterator it; |
michael@0 | 470 | for (it = debug_dirs.begin(); it < debug_dirs.end(); ++it) { |
michael@0 | 471 | const string& debug_dir = *it; |
michael@0 | 472 | debuglink_path = debug_dir + "/" + debuglink; |
michael@0 | 473 | debuglink_fd = open(debuglink_path.c_str(), O_RDONLY); |
michael@0 | 474 | if (debuglink_fd >= 0) { |
michael@0 | 475 | found = true; |
michael@0 | 476 | break; |
michael@0 | 477 | } |
michael@0 | 478 | } |
michael@0 | 479 | |
michael@0 | 480 | if (!found) { |
michael@0 | 481 | fprintf(stderr, "Failed to find debug ELF file for '%s' after trying:\n", |
michael@0 | 482 | obj_file.c_str()); |
michael@0 | 483 | for (it = debug_dirs.begin(); it < debug_dirs.end(); ++it) { |
michael@0 | 484 | const string debug_dir = *it; |
michael@0 | 485 | fprintf(stderr, " %s/%s\n", debug_dir.c_str(), debuglink); |
michael@0 | 486 | } |
michael@0 | 487 | return ""; |
michael@0 | 488 | } |
michael@0 | 489 | |
michael@0 | 490 | FDWrapper debuglink_fd_wrapper(debuglink_fd); |
michael@0 | 491 | // TODO(thestig) check the CRC-32 at the end of the .gnu_debuglink |
michael@0 | 492 | // section. |
michael@0 | 493 | |
michael@0 | 494 | return debuglink_path; |
michael@0 | 495 | } |
michael@0 | 496 | |
michael@0 | 497 | // |
michael@0 | 498 | // LoadSymbolsInfo |
michael@0 | 499 | // |
michael@0 | 500 | // Holds the state between the two calls to LoadSymbols() in case it's necessary |
michael@0 | 501 | // to follow the .gnu_debuglink section and load debug information from a |
michael@0 | 502 | // different file. |
michael@0 | 503 | // |
michael@0 | 504 | template<typename ElfClass> |
michael@0 | 505 | class LoadSymbolsInfo { |
michael@0 | 506 | public: |
michael@0 | 507 | typedef typename ElfClass::Addr Addr; |
michael@0 | 508 | |
michael@0 | 509 | explicit LoadSymbolsInfo(const std::vector<string>& dbg_dirs) : |
michael@0 | 510 | debug_dirs_(dbg_dirs), |
michael@0 | 511 | has_loading_addr_(false) {} |
michael@0 | 512 | |
michael@0 | 513 | // Keeps track of which sections have been loaded so sections don't |
michael@0 | 514 | // accidentally get loaded twice from two different files. |
michael@0 | 515 | void LoadedSection(const string §ion) { |
michael@0 | 516 | if (loaded_sections_.count(section) == 0) { |
michael@0 | 517 | loaded_sections_.insert(section); |
michael@0 | 518 | } else { |
michael@0 | 519 | fprintf(stderr, "Section %s has already been loaded.\n", |
michael@0 | 520 | section.c_str()); |
michael@0 | 521 | } |
michael@0 | 522 | } |
michael@0 | 523 | |
michael@0 | 524 | // The ELF file and linked debug file are expected to have the same preferred |
michael@0 | 525 | // loading address. |
michael@0 | 526 | void set_loading_addr(Addr addr, const string &filename) { |
michael@0 | 527 | if (!has_loading_addr_) { |
michael@0 | 528 | loading_addr_ = addr; |
michael@0 | 529 | loaded_file_ = filename; |
michael@0 | 530 | return; |
michael@0 | 531 | } |
michael@0 | 532 | |
michael@0 | 533 | if (addr != loading_addr_) { |
michael@0 | 534 | fprintf(stderr, |
michael@0 | 535 | "ELF file '%s' and debug ELF file '%s' " |
michael@0 | 536 | "have different load addresses.\n", |
michael@0 | 537 | loaded_file_.c_str(), filename.c_str()); |
michael@0 | 538 | assert(false); |
michael@0 | 539 | } |
michael@0 | 540 | } |
michael@0 | 541 | |
michael@0 | 542 | // Setters and getters |
michael@0 | 543 | const std::vector<string>& debug_dirs() const { |
michael@0 | 544 | return debug_dirs_; |
michael@0 | 545 | } |
michael@0 | 546 | |
michael@0 | 547 | string debuglink_file() const { |
michael@0 | 548 | return debuglink_file_; |
michael@0 | 549 | } |
michael@0 | 550 | void set_debuglink_file(string file) { |
michael@0 | 551 | debuglink_file_ = file; |
michael@0 | 552 | } |
michael@0 | 553 | |
michael@0 | 554 | private: |
michael@0 | 555 | const std::vector<string>& debug_dirs_; // Directories in which to |
michael@0 | 556 | // search for the debug ELF file. |
michael@0 | 557 | |
michael@0 | 558 | string debuglink_file_; // Full path to the debug ELF file. |
michael@0 | 559 | |
michael@0 | 560 | bool has_loading_addr_; // Indicate if LOADING_ADDR_ is valid. |
michael@0 | 561 | |
michael@0 | 562 | Addr loading_addr_; // Saves the preferred loading address from the |
michael@0 | 563 | // first call to LoadSymbols(). |
michael@0 | 564 | |
michael@0 | 565 | string loaded_file_; // Name of the file loaded from the first call to |
michael@0 | 566 | // LoadSymbols(). |
michael@0 | 567 | |
michael@0 | 568 | std::set<string> loaded_sections_; // Tracks the Loaded ELF sections |
michael@0 | 569 | // between calls to LoadSymbols(). |
michael@0 | 570 | }; |
michael@0 | 571 | |
michael@0 | 572 | template<typename ElfClass> |
michael@0 | 573 | bool LoadSymbols(const string& obj_file, |
michael@0 | 574 | const bool big_endian, |
michael@0 | 575 | const typename ElfClass::Ehdr* elf_header, |
michael@0 | 576 | const bool read_gnu_debug_link, |
michael@0 | 577 | LoadSymbolsInfo<ElfClass>* info, |
michael@0 | 578 | SymbolData symbol_data, |
michael@0 | 579 | Module* module) { |
michael@0 | 580 | typedef typename ElfClass::Addr Addr; |
michael@0 | 581 | typedef typename ElfClass::Phdr Phdr; |
michael@0 | 582 | typedef typename ElfClass::Shdr Shdr; |
michael@0 | 583 | |
michael@0 | 584 | BPLOG(INFO) << ""; |
michael@0 | 585 | BPLOG(INFO) << "LoadSymbols: BEGIN " << obj_file; |
michael@0 | 586 | |
michael@0 | 587 | Addr loading_addr = GetLoadingAddress<ElfClass>( |
michael@0 | 588 | GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff), |
michael@0 | 589 | elf_header->e_phnum); |
michael@0 | 590 | module->SetLoadAddress(loading_addr); |
michael@0 | 591 | info->set_loading_addr(loading_addr, obj_file); |
michael@0 | 592 | |
michael@0 | 593 | const Shdr* sections = |
michael@0 | 594 | GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff); |
michael@0 | 595 | const Shdr* section_names = sections + elf_header->e_shstrndx; |
michael@0 | 596 | const char* names = |
michael@0 | 597 | GetOffset<ElfClass, char>(elf_header, section_names->sh_offset); |
michael@0 | 598 | const char *names_end = names + section_names->sh_size; |
michael@0 | 599 | bool found_debug_info_section = false; |
michael@0 | 600 | bool found_usable_info = false; |
michael@0 | 601 | |
michael@0 | 602 | if (symbol_data != ONLY_CFI) { |
michael@0 | 603 | #ifndef NO_STABS_SUPPORT |
michael@0 | 604 | // Look for STABS debugging information, and load it if present. |
michael@0 | 605 | const Shdr* stab_section = |
michael@0 | 606 | FindElfSectionByName<ElfClass>(".stab", SHT_PROGBITS, |
michael@0 | 607 | sections, names, names_end, |
michael@0 | 608 | elf_header->e_shnum); |
michael@0 | 609 | if (stab_section) { |
michael@0 | 610 | const Shdr* stabstr_section = stab_section->sh_link + sections; |
michael@0 | 611 | if (stabstr_section) { |
michael@0 | 612 | found_debug_info_section = true; |
michael@0 | 613 | found_usable_info = true; |
michael@0 | 614 | info->LoadedSection(".stab"); |
michael@0 | 615 | if (!LoadStabs<ElfClass>(elf_header, stab_section, stabstr_section, |
michael@0 | 616 | big_endian, module)) { |
michael@0 | 617 | fprintf(stderr, "%s: \".stab\" section found, but failed to load" |
michael@0 | 618 | " STABS debugging information\n", obj_file.c_str()); |
michael@0 | 619 | } |
michael@0 | 620 | } |
michael@0 | 621 | } |
michael@0 | 622 | #endif // NO_STABS_SUPPORT |
michael@0 | 623 | |
michael@0 | 624 | // Look for DWARF debugging information, and load it if present. |
michael@0 | 625 | const Shdr* dwarf_section = |
michael@0 | 626 | FindElfSectionByName<ElfClass>(".debug_info", SHT_PROGBITS, |
michael@0 | 627 | sections, names, names_end, |
michael@0 | 628 | elf_header->e_shnum); |
michael@0 | 629 | if (dwarf_section) { |
michael@0 | 630 | found_debug_info_section = true; |
michael@0 | 631 | found_usable_info = true; |
michael@0 | 632 | info->LoadedSection(".debug_info"); |
michael@0 | 633 | if (!LoadDwarf<ElfClass>(obj_file, elf_header, big_endian, module)) |
michael@0 | 634 | fprintf(stderr, "%s: \".debug_info\" section found, but failed to load " |
michael@0 | 635 | "DWARF debugging information\n", obj_file.c_str()); |
michael@0 | 636 | } |
michael@0 | 637 | } |
michael@0 | 638 | |
michael@0 | 639 | if (symbol_data != NO_CFI) { |
michael@0 | 640 | // Dwarf Call Frame Information (CFI) is actually independent from |
michael@0 | 641 | // the other DWARF debugging information, and can be used alone. |
michael@0 | 642 | const Shdr* dwarf_cfi_section = |
michael@0 | 643 | FindElfSectionByName<ElfClass>(".debug_frame", SHT_PROGBITS, |
michael@0 | 644 | sections, names, names_end, |
michael@0 | 645 | elf_header->e_shnum); |
michael@0 | 646 | if (dwarf_cfi_section) { |
michael@0 | 647 | // Ignore the return value of this function; even without call frame |
michael@0 | 648 | // information, the other debugging information could be perfectly |
michael@0 | 649 | // useful. |
michael@0 | 650 | info->LoadedSection(".debug_frame"); |
michael@0 | 651 | bool result = |
michael@0 | 652 | LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".debug_frame", |
michael@0 | 653 | dwarf_cfi_section, false, 0, 0, big_endian, |
michael@0 | 654 | module); |
michael@0 | 655 | found_usable_info = found_usable_info || result; |
michael@0 | 656 | if (result) |
michael@0 | 657 | BPLOG(INFO) << "LoadSymbols: read CFI from .debug_frame"; |
michael@0 | 658 | } |
michael@0 | 659 | |
michael@0 | 660 | // Linux C++ exception handling information can also provide |
michael@0 | 661 | // unwinding data. |
michael@0 | 662 | const Shdr* eh_frame_section = |
michael@0 | 663 | FindElfSectionByName<ElfClass>(".eh_frame", SHT_PROGBITS, |
michael@0 | 664 | sections, names, names_end, |
michael@0 | 665 | elf_header->e_shnum); |
michael@0 | 666 | if (eh_frame_section) { |
michael@0 | 667 | // Pointers in .eh_frame data may be relative to the base addresses of |
michael@0 | 668 | // certain sections. Provide those sections if present. |
michael@0 | 669 | const Shdr* got_section = |
michael@0 | 670 | FindElfSectionByName<ElfClass>(".got", SHT_PROGBITS, |
michael@0 | 671 | sections, names, names_end, |
michael@0 | 672 | elf_header->e_shnum); |
michael@0 | 673 | const Shdr* text_section = |
michael@0 | 674 | FindElfSectionByName<ElfClass>(".text", SHT_PROGBITS, |
michael@0 | 675 | sections, names, names_end, |
michael@0 | 676 | elf_header->e_shnum); |
michael@0 | 677 | info->LoadedSection(".eh_frame"); |
michael@0 | 678 | // As above, ignore the return value of this function. |
michael@0 | 679 | bool result = |
michael@0 | 680 | LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".eh_frame", |
michael@0 | 681 | eh_frame_section, true, |
michael@0 | 682 | got_section, text_section, big_endian, module); |
michael@0 | 683 | found_usable_info = found_usable_info || result; |
michael@0 | 684 | if (result) |
michael@0 | 685 | BPLOG(INFO) << "LoadSymbols: read CFI from .eh_frame"; |
michael@0 | 686 | } |
michael@0 | 687 | } |
michael@0 | 688 | |
michael@0 | 689 | // ARM has special unwind tables that can be used. |
michael@0 | 690 | const Shdr* arm_exidx_section = |
michael@0 | 691 | FindElfSectionByName<ElfClass>(".ARM.exidx", SHT_ARM_EXIDX, |
michael@0 | 692 | sections, names, names_end, |
michael@0 | 693 | elf_header->e_shnum); |
michael@0 | 694 | const Shdr* arm_extab_section = |
michael@0 | 695 | FindElfSectionByName<ElfClass>(".ARM.extab", SHT_PROGBITS, |
michael@0 | 696 | sections, names, names_end, |
michael@0 | 697 | elf_header->e_shnum); |
michael@0 | 698 | // Only load information from this section if there isn't a .debug_info |
michael@0 | 699 | // section. |
michael@0 | 700 | if (!found_debug_info_section |
michael@0 | 701 | && arm_exidx_section && arm_extab_section && symbol_data != NO_CFI) { |
michael@0 | 702 | info->LoadedSection(".ARM.exidx"); |
michael@0 | 703 | info->LoadedSection(".ARM.extab"); |
michael@0 | 704 | bool result = LoadARMexidx<ElfClass>(elf_header, |
michael@0 | 705 | arm_exidx_section, arm_extab_section, |
michael@0 | 706 | loading_addr, module); |
michael@0 | 707 | found_usable_info = found_usable_info || result; |
michael@0 | 708 | if (result) |
michael@0 | 709 | BPLOG(INFO) << "LoadSymbols: read EXIDX from .ARM.{exidx,extab}"; |
michael@0 | 710 | } |
michael@0 | 711 | |
michael@0 | 712 | if (!found_debug_info_section && symbol_data != ONLY_CFI) { |
michael@0 | 713 | fprintf(stderr, "%s: file contains no debugging information" |
michael@0 | 714 | " (no \".stab\" or \".debug_info\" sections)\n", |
michael@0 | 715 | obj_file.c_str()); |
michael@0 | 716 | |
michael@0 | 717 | // Failed, but maybe there's a .gnu_debuglink section? |
michael@0 | 718 | if (read_gnu_debug_link) { |
michael@0 | 719 | const Shdr* gnu_debuglink_section |
michael@0 | 720 | = FindElfSectionByName<ElfClass>(".gnu_debuglink", SHT_PROGBITS, |
michael@0 | 721 | sections, names, |
michael@0 | 722 | names_end, elf_header->e_shnum); |
michael@0 | 723 | if (gnu_debuglink_section) { |
michael@0 | 724 | if (!info->debug_dirs().empty()) { |
michael@0 | 725 | const char* debuglink_contents = |
michael@0 | 726 | GetOffset<ElfClass, char>(elf_header, |
michael@0 | 727 | gnu_debuglink_section->sh_offset); |
michael@0 | 728 | string debuglink_file |
michael@0 | 729 | = ReadDebugLink<ElfClass>(debuglink_contents, |
michael@0 | 730 | gnu_debuglink_section->sh_size, |
michael@0 | 731 | obj_file, info->debug_dirs()); |
michael@0 | 732 | info->set_debuglink_file(debuglink_file); |
michael@0 | 733 | } else { |
michael@0 | 734 | fprintf(stderr, ".gnu_debuglink section found in '%s', " |
michael@0 | 735 | "but no debug path specified.\n", obj_file.c_str()); |
michael@0 | 736 | } |
michael@0 | 737 | } else { |
michael@0 | 738 | fprintf(stderr, "%s does not contain a .gnu_debuglink section.\n", |
michael@0 | 739 | obj_file.c_str()); |
michael@0 | 740 | } |
michael@0 | 741 | } else { |
michael@0 | 742 | if (symbol_data != ONLY_CFI) { |
michael@0 | 743 | // The caller doesn't want to consult .gnu_debuglink. |
michael@0 | 744 | // See if there are export symbols available. |
michael@0 | 745 | const Shdr* dynsym_section = |
michael@0 | 746 | FindElfSectionByName<ElfClass>(".dynsym", SHT_DYNSYM, |
michael@0 | 747 | sections, names, names_end, |
michael@0 | 748 | elf_header->e_shnum); |
michael@0 | 749 | const Shdr* dynstr_section = |
michael@0 | 750 | FindElfSectionByName<ElfClass>(".dynstr", SHT_STRTAB, |
michael@0 | 751 | sections, names, names_end, |
michael@0 | 752 | elf_header->e_shnum); |
michael@0 | 753 | if (dynsym_section && dynstr_section) { |
michael@0 | 754 | info->LoadedSection(".dynsym"); |
michael@0 | 755 | |
michael@0 | 756 | const uint8_t* dynsyms = |
michael@0 | 757 | GetOffset<ElfClass, uint8_t>(elf_header, |
michael@0 | 758 | dynsym_section->sh_offset); |
michael@0 | 759 | const uint8_t* dynstrs = |
michael@0 | 760 | GetOffset<ElfClass, uint8_t>(elf_header, |
michael@0 | 761 | dynstr_section->sh_offset); |
michael@0 | 762 | bool result = |
michael@0 | 763 | ELFSymbolsToModule(dynsyms, |
michael@0 | 764 | dynsym_section->sh_size, |
michael@0 | 765 | dynstrs, |
michael@0 | 766 | dynstr_section->sh_size, |
michael@0 | 767 | big_endian, |
michael@0 | 768 | ElfClass::kAddrSize, |
michael@0 | 769 | module); |
michael@0 | 770 | found_usable_info = found_usable_info || result; |
michael@0 | 771 | } |
michael@0 | 772 | } |
michael@0 | 773 | |
michael@0 | 774 | // Return true if some usable information was found, since |
michael@0 | 775 | // the caller doesn't want to use .gnu_debuglink. |
michael@0 | 776 | BPLOG(INFO) << "LoadSymbols: " |
michael@0 | 777 | << (found_usable_info ? "SUCCESS " : "FAILURE ") |
michael@0 | 778 | << obj_file; |
michael@0 | 779 | return found_usable_info; |
michael@0 | 780 | } |
michael@0 | 781 | |
michael@0 | 782 | // No debug info was found, let the user try again with .gnu_debuglink |
michael@0 | 783 | // if present. |
michael@0 | 784 | BPLOG(INFO) << "LoadSymbols: FAILURE " << obj_file; |
michael@0 | 785 | return false; |
michael@0 | 786 | } |
michael@0 | 787 | |
michael@0 | 788 | BPLOG(INFO) << "LoadSymbols: SUCCESS " << obj_file; |
michael@0 | 789 | return true; |
michael@0 | 790 | } |
michael@0 | 791 | |
michael@0 | 792 | // Return the breakpad symbol file identifier for the architecture of |
michael@0 | 793 | // ELF_HEADER. |
michael@0 | 794 | template<typename ElfClass> |
michael@0 | 795 | const char* ElfArchitecture(const typename ElfClass::Ehdr* elf_header) { |
michael@0 | 796 | typedef typename ElfClass::Half Half; |
michael@0 | 797 | Half arch = elf_header->e_machine; |
michael@0 | 798 | switch (arch) { |
michael@0 | 799 | case EM_386: return "x86"; |
michael@0 | 800 | case EM_ARM: return "arm"; |
michael@0 | 801 | case EM_MIPS: return "mips"; |
michael@0 | 802 | case EM_PPC64: return "ppc64"; |
michael@0 | 803 | case EM_PPC: return "ppc"; |
michael@0 | 804 | case EM_S390: return "s390"; |
michael@0 | 805 | case EM_SPARC: return "sparc"; |
michael@0 | 806 | case EM_SPARCV9: return "sparcv9"; |
michael@0 | 807 | case EM_X86_64: return "x86_64"; |
michael@0 | 808 | default: return NULL; |
michael@0 | 809 | } |
michael@0 | 810 | } |
michael@0 | 811 | |
michael@0 | 812 | // Format the Elf file identifier in IDENTIFIER as a UUID with the |
michael@0 | 813 | // dashes removed. |
michael@0 | 814 | string FormatIdentifier(unsigned char identifier[16]) { |
michael@0 | 815 | char identifier_str[40]; |
michael@0 | 816 | google_breakpad::FileID::ConvertIdentifierToString( |
michael@0 | 817 | identifier, |
michael@0 | 818 | identifier_str, |
michael@0 | 819 | sizeof(identifier_str)); |
michael@0 | 820 | string id_no_dash; |
michael@0 | 821 | for (int i = 0; identifier_str[i] != '\0'; ++i) |
michael@0 | 822 | if (identifier_str[i] != '-') |
michael@0 | 823 | id_no_dash += identifier_str[i]; |
michael@0 | 824 | // Add an extra "0" by the end. PDB files on Windows have an 'age' |
michael@0 | 825 | // number appended to the end of the file identifier; this isn't |
michael@0 | 826 | // really used or necessary on other platforms, but be consistent. |
michael@0 | 827 | id_no_dash += '0'; |
michael@0 | 828 | return id_no_dash; |
michael@0 | 829 | } |
michael@0 | 830 | |
michael@0 | 831 | // Return the non-directory portion of FILENAME: the portion after the |
michael@0 | 832 | // last slash, or the whole filename if there are no slashes. |
michael@0 | 833 | string BaseFileName(const string &filename) { |
michael@0 | 834 | // Lots of copies! basename's behavior is less than ideal. |
michael@0 | 835 | char *c_filename = strdup(filename.c_str()); |
michael@0 | 836 | string base = basename(c_filename); |
michael@0 | 837 | free(c_filename); |
michael@0 | 838 | return base; |
michael@0 | 839 | } |
michael@0 | 840 | |
michael@0 | 841 | template<typename ElfClass> |
michael@0 | 842 | bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header, |
michael@0 | 843 | const string& obj_filename, |
michael@0 | 844 | const std::vector<string>& debug_dirs, |
michael@0 | 845 | SymbolData symbol_data, |
michael@0 | 846 | Module** out_module) { |
michael@0 | 847 | typedef typename ElfClass::Ehdr Ehdr; |
michael@0 | 848 | typedef typename ElfClass::Shdr Shdr; |
michael@0 | 849 | |
michael@0 | 850 | *out_module = NULL; |
michael@0 | 851 | |
michael@0 | 852 | unsigned char identifier[16]; |
michael@0 | 853 | if (!google_breakpad::FileID::ElfFileIdentifierFromMappedFile(elf_header, |
michael@0 | 854 | identifier)) { |
michael@0 | 855 | fprintf(stderr, "%s: unable to generate file identifier\n", |
michael@0 | 856 | obj_filename.c_str()); |
michael@0 | 857 | return false; |
michael@0 | 858 | } |
michael@0 | 859 | |
michael@0 | 860 | const char *architecture = ElfArchitecture<ElfClass>(elf_header); |
michael@0 | 861 | if (!architecture) { |
michael@0 | 862 | fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n", |
michael@0 | 863 | obj_filename.c_str(), elf_header->e_machine); |
michael@0 | 864 | return false; |
michael@0 | 865 | } |
michael@0 | 866 | |
michael@0 | 867 | // Figure out what endianness this file is. |
michael@0 | 868 | bool big_endian; |
michael@0 | 869 | if (!ElfEndianness<ElfClass>(elf_header, &big_endian)) |
michael@0 | 870 | return false; |
michael@0 | 871 | |
michael@0 | 872 | string name = BaseFileName(obj_filename); |
michael@0 | 873 | string os = "Linux"; |
michael@0 | 874 | string id = FormatIdentifier(identifier); |
michael@0 | 875 | |
michael@0 | 876 | LoadSymbolsInfo<ElfClass> info(debug_dirs); |
michael@0 | 877 | scoped_ptr<Module> module(new Module(name, os, architecture, id)); |
michael@0 | 878 | if (!LoadSymbols<ElfClass>(obj_filename, big_endian, elf_header, |
michael@0 | 879 | !debug_dirs.empty(), &info, |
michael@0 | 880 | symbol_data, module.get())) { |
michael@0 | 881 | const string debuglink_file = info.debuglink_file(); |
michael@0 | 882 | if (debuglink_file.empty()) |
michael@0 | 883 | return false; |
michael@0 | 884 | |
michael@0 | 885 | // Load debuglink ELF file. |
michael@0 | 886 | fprintf(stderr, "Found debugging info in %s\n", debuglink_file.c_str()); |
michael@0 | 887 | MmapWrapper debug_map_wrapper; |
michael@0 | 888 | Ehdr* debug_elf_header = NULL; |
michael@0 | 889 | if (!LoadELF(debuglink_file, &debug_map_wrapper, |
michael@0 | 890 | reinterpret_cast<void**>(&debug_elf_header))) |
michael@0 | 891 | return false; |
michael@0 | 892 | // Sanity checks to make sure everything matches up. |
michael@0 | 893 | const char *debug_architecture = |
michael@0 | 894 | ElfArchitecture<ElfClass>(debug_elf_header); |
michael@0 | 895 | if (!debug_architecture) { |
michael@0 | 896 | fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n", |
michael@0 | 897 | debuglink_file.c_str(), debug_elf_header->e_machine); |
michael@0 | 898 | return false; |
michael@0 | 899 | } |
michael@0 | 900 | if (strcmp(architecture, debug_architecture)) { |
michael@0 | 901 | fprintf(stderr, "%s with ELF machine architecture %s does not match " |
michael@0 | 902 | "%s with ELF architecture %s\n", |
michael@0 | 903 | debuglink_file.c_str(), debug_architecture, |
michael@0 | 904 | obj_filename.c_str(), architecture); |
michael@0 | 905 | return false; |
michael@0 | 906 | } |
michael@0 | 907 | |
michael@0 | 908 | bool debug_big_endian; |
michael@0 | 909 | if (!ElfEndianness<ElfClass>(debug_elf_header, &debug_big_endian)) |
michael@0 | 910 | return false; |
michael@0 | 911 | if (debug_big_endian != big_endian) { |
michael@0 | 912 | fprintf(stderr, "%s and %s does not match in endianness\n", |
michael@0 | 913 | obj_filename.c_str(), debuglink_file.c_str()); |
michael@0 | 914 | return false; |
michael@0 | 915 | } |
michael@0 | 916 | |
michael@0 | 917 | if (!LoadSymbols<ElfClass>(debuglink_file, debug_big_endian, |
michael@0 | 918 | debug_elf_header, false, &info, |
michael@0 | 919 | symbol_data, module.get())) { |
michael@0 | 920 | return false; |
michael@0 | 921 | } |
michael@0 | 922 | } |
michael@0 | 923 | |
michael@0 | 924 | *out_module = module.release(); |
michael@0 | 925 | return true; |
michael@0 | 926 | } |
michael@0 | 927 | |
michael@0 | 928 | } // namespace |
michael@0 | 929 | |
michael@0 | 930 | namespace google_breakpad { |
michael@0 | 931 | |
michael@0 | 932 | // Not explicitly exported, but not static so it can be used in unit tests. |
michael@0 | 933 | bool ReadSymbolDataInternal(const uint8_t* obj_file, |
michael@0 | 934 | const string& obj_filename, |
michael@0 | 935 | const std::vector<string>& debug_dirs, |
michael@0 | 936 | SymbolData symbol_data, |
michael@0 | 937 | Module** module) { |
michael@0 | 938 | |
michael@0 | 939 | if (!IsValidElf(obj_file)) { |
michael@0 | 940 | fprintf(stderr, "Not a valid ELF file: %s\n", obj_filename.c_str()); |
michael@0 | 941 | return false; |
michael@0 | 942 | } |
michael@0 | 943 | |
michael@0 | 944 | int elfclass = ElfClass(obj_file); |
michael@0 | 945 | if (elfclass == ELFCLASS32) { |
michael@0 | 946 | return ReadSymbolDataElfClass<ElfClass32>( |
michael@0 | 947 | reinterpret_cast<const Elf32_Ehdr*>(obj_file), obj_filename, debug_dirs, |
michael@0 | 948 | symbol_data, module); |
michael@0 | 949 | } |
michael@0 | 950 | if (elfclass == ELFCLASS64) { |
michael@0 | 951 | return ReadSymbolDataElfClass<ElfClass64>( |
michael@0 | 952 | reinterpret_cast<const Elf64_Ehdr*>(obj_file), obj_filename, debug_dirs, |
michael@0 | 953 | symbol_data, module); |
michael@0 | 954 | } |
michael@0 | 955 | |
michael@0 | 956 | return false; |
michael@0 | 957 | } |
michael@0 | 958 | |
michael@0 | 959 | bool WriteSymbolFile(const string &obj_file, |
michael@0 | 960 | const std::vector<string>& debug_dirs, |
michael@0 | 961 | SymbolData symbol_data, |
michael@0 | 962 | std::ostream &sym_stream) { |
michael@0 | 963 | Module* module; |
michael@0 | 964 | if (!ReadSymbolData(obj_file, debug_dirs, symbol_data, &module)) |
michael@0 | 965 | return false; |
michael@0 | 966 | |
michael@0 | 967 | bool result = module->Write(sym_stream, symbol_data); |
michael@0 | 968 | delete module; |
michael@0 | 969 | return result; |
michael@0 | 970 | } |
michael@0 | 971 | |
michael@0 | 972 | bool ReadSymbolData(const string& obj_file, |
michael@0 | 973 | const std::vector<string>& debug_dirs, |
michael@0 | 974 | SymbolData symbol_data, |
michael@0 | 975 | Module** module) { |
michael@0 | 976 | MmapWrapper map_wrapper; |
michael@0 | 977 | void* elf_header = NULL; |
michael@0 | 978 | if (!LoadELF(obj_file, &map_wrapper, &elf_header)) |
michael@0 | 979 | return false; |
michael@0 | 980 | |
michael@0 | 981 | return ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(elf_header), |
michael@0 | 982 | obj_file, debug_dirs, symbol_data, module); |
michael@0 | 983 | } |
michael@0 | 984 | |
michael@0 | 985 | } // namespace google_breakpad |