PageRenderTime 32ms CodeModel.GetById 9ms app.highlight 20ms RepoModel.GetById 1ms app.codeStats 0ms

/thirdparty/breakpad/common/linux/file_id.cc

http://github.com/tomahawk-player/tomahawk
C++ | 281 lines | 187 code | 42 blank | 52 comment | 45 complexity | ec3ecc68b4edd3076c089b932caa213c MD5 | raw file
  1// Copyright (c) 2006, Google Inc.
  2// All rights reserved.
  3//
  4// Redistribution and use in source and binary forms, with or without
  5// modification, are permitted provided that the following conditions are
  6// met:
  7//
  8//     * Redistributions of source code must retain the above copyright
  9// notice, this list of conditions and the following disclaimer.
 10//     * Redistributions in binary form must reproduce the above
 11// copyright notice, this list of conditions and the following disclaimer
 12// in the documentation and/or other materials provided with the
 13// distribution.
 14//     * Neither the name of Google Inc. nor the names of its
 15// contributors may be used to endorse or promote products derived from
 16// this software without specific prior written permission.
 17//
 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 29//
 30// file_id.cc: Return a unique identifier for a file
 31//
 32// See file_id.h for documentation
 33//
 34
 35#include "common/linux/file_id.h"
 36
 37#include <arpa/inet.h>
 38#include <assert.h>
 39#if defined(__ANDROID__)
 40#include <linux/elf.h>
 41#include "client/linux/android_link.h"
 42#else
 43#include <elf.h>
 44#include <link.h>
 45#endif
 46#include <string.h>
 47
 48#include <algorithm>
 49
 50#include "common/linux/linux_libc_support.h"
 51#include "common/linux/memory_mapped_file.h"
 52#include "third_party/lss/linux_syscall_support.h"
 53
 54namespace google_breakpad {
 55
 56#ifndef NT_GNU_BUILD_ID
 57#define NT_GNU_BUILD_ID 3
 58#endif
 59
 60FileID::FileID(const char* path) {
 61  strncpy(path_, path, sizeof(path_));
 62}
 63
 64struct ElfClass32 {
 65  typedef Elf32_Ehdr Ehdr;
 66  typedef Elf32_Nhdr Nhdr;
 67  typedef Elf32_Shdr Shdr;
 68  static const int kClass = ELFCLASS32;
 69};
 70
 71struct ElfClass64 {
 72  typedef Elf64_Ehdr Ehdr;
 73  typedef Elf64_Nhdr Nhdr;
 74  typedef Elf64_Shdr Shdr;
 75  static const int kClass = ELFCLASS64;
 76};
 77
 78// These six functions are also used inside the crashed process, so be safe
 79// and use the syscall/libc wrappers instead of direct syscalls or libc.
 80template<typename ElfClass>
 81static void FindElfClassSection(const char *elf_base,
 82                                const char *section_name,
 83                                uint32_t section_type,
 84                                const void **section_start,
 85                                int *section_size) {
 86  typedef typename ElfClass::Ehdr Ehdr;
 87  typedef typename ElfClass::Shdr Shdr;
 88
 89  assert(elf_base);
 90  assert(section_start);
 91  assert(section_size);
 92
 93  assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0);
 94
 95  int name_len = my_strlen(section_name);
 96
 97  const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
 98  assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
 99
100  const Shdr* sections =
101      reinterpret_cast<const Shdr*>(elf_base + elf_header->e_shoff);
102  const Shdr* string_section = sections + elf_header->e_shstrndx;
103
104  const Shdr* section = NULL;
105  for (int i = 0; i < elf_header->e_shnum; ++i) {
106    if (sections[i].sh_type == section_type) {
107      const char* current_section_name = (char*)(elf_base +
108                                                 string_section->sh_offset +
109                                                 sections[i].sh_name);
110      if (!my_strncmp(current_section_name, section_name, name_len)) {
111        section = &sections[i];
112        break;
113      }
114    }
115  }
116  if (section != NULL && section->sh_size > 0) {
117    *section_start = elf_base + section->sh_offset;
118    *section_size = section->sh_size;
119  }
120}
121
122// Attempt to find a section named |section_name| of type |section_type|
123// in the ELF binary data at |elf_mapped_base|. On success, returns true
124// and sets |*section_start| to point to the start of the section data,
125// and |*section_size| to the size of the section's data. If |elfclass|
126// is not NULL, set |*elfclass| to the ELF file class.
127static bool FindElfSection(const void *elf_mapped_base,
128                           const char *section_name,
129                           uint32_t section_type,
130                           const void **section_start,
131                           int *section_size,
132                           int *elfclass) {
133  assert(elf_mapped_base);
134  assert(section_start);
135  assert(section_size);
136
137  *section_start = NULL;
138  *section_size = 0;
139
140  const char* elf_base =
141    static_cast<const char*>(elf_mapped_base);
142  const ElfW(Ehdr)* elf_header =
143    reinterpret_cast<const ElfW(Ehdr)*>(elf_base);
144  if (my_strncmp(elf_base, ELFMAG, SELFMAG) != 0)
145    return false;
146
147  if (elfclass) {
148    *elfclass = elf_header->e_ident[EI_CLASS];
149  }
150
151  if (elf_header->e_ident[EI_CLASS] == ELFCLASS32) {
152    FindElfClassSection<ElfClass32>(elf_base, section_name, section_type,
153                                    section_start, section_size);
154    return *section_start != NULL;
155  } else if (elf_header->e_ident[EI_CLASS] == ELFCLASS64) {
156    FindElfClassSection<ElfClass64>(elf_base, section_name, section_type,
157                                    section_start, section_size);
158    return *section_start != NULL;
159  }
160
161  return false;
162}
163
164template<typename ElfClass>
165static bool ElfClassBuildIDNoteIdentifier(const void *section,
166                                          uint8_t identifier[kMDGUIDSize]) {
167  typedef typename ElfClass::Nhdr Nhdr;
168
169  const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section);
170  if (note_header->n_type != NT_GNU_BUILD_ID ||
171      note_header->n_descsz == 0) {
172    return false;
173  }
174
175  const char* build_id = reinterpret_cast<const char*>(section) +
176    sizeof(Nhdr) + note_header->n_namesz;
177  // Copy as many bits of the build ID as will fit
178  // into the GUID space.
179  my_memset(identifier, 0, kMDGUIDSize);
180  memcpy(identifier, build_id,
181         std::min(kMDGUIDSize, (size_t)note_header->n_descsz));
182
183  return true;
184}
185
186// Attempt to locate a .note.gnu.build-id section in an ELF binary
187// and copy as many bytes of it as will fit into |identifier|.
188static bool FindElfBuildIDNote(const void *elf_mapped_base,
189                               uint8_t identifier[kMDGUIDSize]) {
190  void* note_section;
191  int note_size, elfclass;
192  if (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
193                      (const void**)&note_section, &note_size, &elfclass) ||
194      note_size == 0) {
195    return false;
196  }
197
198  if (elfclass == ELFCLASS32) {
199    return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, identifier);
200  } else if (elfclass == ELFCLASS64) {
201    return ElfClassBuildIDNoteIdentifier<ElfClass64>(note_section, identifier);
202  }
203
204  return false;
205}
206
207// Attempt to locate the .text section of an ELF binary and generate
208// a simple hash by XORing the first page worth of bytes into |identifier|.
209static bool HashElfTextSection(const void *elf_mapped_base,
210                               uint8_t identifier[kMDGUIDSize]) {
211  void* text_section;
212  int text_size;
213  if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS,
214                      (const void**)&text_section, &text_size, NULL) ||
215      text_size == 0) {
216    return false;
217  }
218
219  my_memset(identifier, 0, kMDGUIDSize);
220  const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section);
221  const uint8_t* ptr_end = ptr + std::min(text_size, 4096);
222  while (ptr < ptr_end) {
223    for (unsigned i = 0; i < kMDGUIDSize; i++)
224      identifier[i] ^= ptr[i];
225    ptr += kMDGUIDSize;
226  }
227  return true;
228}
229
230// static
231bool FileID::ElfFileIdentifierFromMappedFile(const void* base,
232                                             uint8_t identifier[kMDGUIDSize]) {
233  // Look for a build id note first.
234  if (FindElfBuildIDNote(base, identifier))
235    return true;
236
237  // Fall back on hashing the first page of the text section.
238  return HashElfTextSection(base, identifier);
239}
240
241bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) {
242  MemoryMappedFile mapped_file(path_);
243  if (!mapped_file.data())  // Should probably check if size >= ElfW(Ehdr)?
244    return false;
245
246  return ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
247}
248
249// static
250void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize],
251                                       char* buffer, int buffer_length) {
252  uint8_t identifier_swapped[kMDGUIDSize];
253
254  // Endian-ness swap to match dump processor expectation.
255  memcpy(identifier_swapped, identifier, kMDGUIDSize);
256  uint32_t* data1 = reinterpret_cast<uint32_t*>(identifier_swapped);
257  *data1 = htonl(*data1);
258  uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4);
259  *data2 = htons(*data2);
260  uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6);
261  *data3 = htons(*data3);
262
263  int buffer_idx = 0;
264  for (unsigned int idx = 0;
265       (buffer_idx < buffer_length) && (idx < kMDGUIDSize);
266       ++idx) {
267    int hi = (identifier_swapped[idx] >> 4) & 0x0F;
268    int lo = (identifier_swapped[idx]) & 0x0F;
269
270    if (idx == 4 || idx == 6 || idx == 8 || idx == 10)
271      buffer[buffer_idx++] = '-';
272
273    buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi;
274    buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo;
275  }
276
277  // NULL terminate
278  buffer[(buffer_idx < buffer_length) ? buffer_idx : buffer_idx - 1] = 0;
279}
280
281}  // namespace google_breakpad