/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. #include "common/linux/file_id.h"
  35. #include <arpa/inet.h>
  36. #include <assert.h>
  37. #if defined(__ANDROID__)
  38. #include <linux/elf.h>
  39. #include "client/linux/android_link.h"
  40. #else
  41. #include <elf.h>
  42. #include <link.h>
  43. #endif
  44. #include <string.h>
  45. #include <algorithm>
  46. #include "common/linux/linux_libc_support.h"
  47. #include "common/linux/memory_mapped_file.h"
  48. #include "third_party/lss/linux_syscall_support.h"
  49. namespace google_breakpad {
  50. #ifndef NT_GNU_BUILD_ID
  51. #define NT_GNU_BUILD_ID 3
  52. #endif
  53. FileID::FileID(const char* path) {
  54. strncpy(path_, path, sizeof(path_));
  55. }
  56. struct ElfClass32 {
  57. typedef Elf32_Ehdr Ehdr;
  58. typedef Elf32_Nhdr Nhdr;
  59. typedef Elf32_Shdr Shdr;
  60. static const int kClass = ELFCLASS32;
  61. };
  62. struct ElfClass64 {
  63. typedef Elf64_Ehdr Ehdr;
  64. typedef Elf64_Nhdr Nhdr;
  65. typedef Elf64_Shdr Shdr;
  66. static const int kClass = ELFCLASS64;
  67. };
  68. // These six functions are also used inside the crashed process, so be safe
  69. // and use the syscall/libc wrappers instead of direct syscalls or libc.
  70. template<typename ElfClass>
  71. static void FindElfClassSection(const char *elf_base,
  72. const char *section_name,
  73. uint32_t section_type,
  74. const void **section_start,
  75. int *section_size) {
  76. typedef typename ElfClass::Ehdr Ehdr;
  77. typedef typename ElfClass::Shdr Shdr;
  78. assert(elf_base);
  79. assert(section_start);
  80. assert(section_size);
  81. assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0);
  82. int name_len = my_strlen(section_name);
  83. const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
  84. assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
  85. const Shdr* sections =
  86. reinterpret_cast<const Shdr*>(elf_base + elf_header->e_shoff);
  87. const Shdr* string_section = sections + elf_header->e_shstrndx;
  88. const Shdr* section = NULL;
  89. for (int i = 0; i < elf_header->e_shnum; ++i) {
  90. if (sections[i].sh_type == section_type) {
  91. const char* current_section_name = (char*)(elf_base +
  92. string_section->sh_offset +
  93. sections[i].sh_name);
  94. if (!my_strncmp(current_section_name, section_name, name_len)) {
  95. section = &sections[i];
  96. break;
  97. }
  98. }
  99. }
  100. if (section != NULL && section->sh_size > 0) {
  101. *section_start = elf_base + section->sh_offset;
  102. *section_size = section->sh_size;
  103. }
  104. }
  105. // Attempt to find a section named |section_name| of type |section_type|
  106. // in the ELF binary data at |elf_mapped_base|. On success, returns true
  107. // and sets |*section_start| to point to the start of the section data,
  108. // and |*section_size| to the size of the section's data. If |elfclass|
  109. // is not NULL, set |*elfclass| to the ELF file class.
  110. static bool FindElfSection(const void *elf_mapped_base,
  111. const char *section_name,
  112. uint32_t section_type,
  113. const void **section_start,
  114. int *section_size,
  115. int *elfclass) {
  116. assert(elf_mapped_base);
  117. assert(section_start);
  118. assert(section_size);
  119. *section_start = NULL;
  120. *section_size = 0;
  121. const char* elf_base =
  122. static_cast<const char*>(elf_mapped_base);
  123. const ElfW(Ehdr)* elf_header =
  124. reinterpret_cast<const ElfW(Ehdr)*>(elf_base);
  125. if (my_strncmp(elf_base, ELFMAG, SELFMAG) != 0)
  126. return false;
  127. if (elfclass) {
  128. *elfclass = elf_header->e_ident[EI_CLASS];
  129. }
  130. if (elf_header->e_ident[EI_CLASS] == ELFCLASS32) {
  131. FindElfClassSection<ElfClass32>(elf_base, section_name, section_type,
  132. section_start, section_size);
  133. return *section_start != NULL;
  134. } else if (elf_header->e_ident[EI_CLASS] == ELFCLASS64) {
  135. FindElfClassSection<ElfClass64>(elf_base, section_name, section_type,
  136. section_start, section_size);
  137. return *section_start != NULL;
  138. }
  139. return false;
  140. }
  141. template<typename ElfClass>
  142. static bool ElfClassBuildIDNoteIdentifier(const void *section,
  143. uint8_t identifier[kMDGUIDSize]) {
  144. typedef typename ElfClass::Nhdr Nhdr;
  145. const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section);
  146. if (note_header->n_type != NT_GNU_BUILD_ID ||
  147. note_header->n_descsz == 0) {
  148. return false;
  149. }
  150. const char* build_id = reinterpret_cast<const char*>(section) +
  151. sizeof(Nhdr) + note_header->n_namesz;
  152. // Copy as many bits of the build ID as will fit
  153. // into the GUID space.
  154. my_memset(identifier, 0, kMDGUIDSize);
  155. memcpy(identifier, build_id,
  156. std::min(kMDGUIDSize, (size_t)note_header->n_descsz));
  157. return true;
  158. }
  159. // Attempt to locate a .note.gnu.build-id section in an ELF binary
  160. // and copy as many bytes of it as will fit into |identifier|.
  161. static bool FindElfBuildIDNote(const void *elf_mapped_base,
  162. uint8_t identifier[kMDGUIDSize]) {
  163. void* note_section;
  164. int note_size, elfclass;
  165. if (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
  166. (const void**)&note_section, &note_size, &elfclass) ||
  167. note_size == 0) {
  168. return false;
  169. }
  170. if (elfclass == ELFCLASS32) {
  171. return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, identifier);
  172. } else if (elfclass == ELFCLASS64) {
  173. return ElfClassBuildIDNoteIdentifier<ElfClass64>(note_section, identifier);
  174. }
  175. return false;
  176. }
  177. // Attempt to locate the .text section of an ELF binary and generate
  178. // a simple hash by XORing the first page worth of bytes into |identifier|.
  179. static bool HashElfTextSection(const void *elf_mapped_base,
  180. uint8_t identifier[kMDGUIDSize]) {
  181. void* text_section;
  182. int text_size;
  183. if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS,
  184. (const void**)&text_section, &text_size, NULL) ||
  185. text_size == 0) {
  186. return false;
  187. }
  188. my_memset(identifier, 0, kMDGUIDSize);
  189. const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section);
  190. const uint8_t* ptr_end = ptr + std::min(text_size, 4096);
  191. while (ptr < ptr_end) {
  192. for (unsigned i = 0; i < kMDGUIDSize; i++)
  193. identifier[i] ^= ptr[i];
  194. ptr += kMDGUIDSize;
  195. }
  196. return true;
  197. }
  198. // static
  199. bool FileID::ElfFileIdentifierFromMappedFile(const void* base,
  200. uint8_t identifier[kMDGUIDSize]) {
  201. // Look for a build id note first.
  202. if (FindElfBuildIDNote(base, identifier))
  203. return true;
  204. // Fall back on hashing the first page of the text section.
  205. return HashElfTextSection(base, identifier);
  206. }
  207. bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) {
  208. MemoryMappedFile mapped_file(path_);
  209. if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)?
  210. return false;
  211. return ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
  212. }
  213. // static
  214. void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize],
  215. char* buffer, int buffer_length) {
  216. uint8_t identifier_swapped[kMDGUIDSize];
  217. // Endian-ness swap to match dump processor expectation.
  218. memcpy(identifier_swapped, identifier, kMDGUIDSize);
  219. uint32_t* data1 = reinterpret_cast<uint32_t*>(identifier_swapped);
  220. *data1 = htonl(*data1);
  221. uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4);
  222. *data2 = htons(*data2);
  223. uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6);
  224. *data3 = htons(*data3);
  225. int buffer_idx = 0;
  226. for (unsigned int idx = 0;
  227. (buffer_idx < buffer_length) && (idx < kMDGUIDSize);
  228. ++idx) {
  229. int hi = (identifier_swapped[idx] >> 4) & 0x0F;
  230. int lo = (identifier_swapped[idx]) & 0x0F;
  231. if (idx == 4 || idx == 6 || idx == 8 || idx == 10)
  232. buffer[buffer_idx++] = '-';
  233. buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi;
  234. buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo;
  235. }
  236. // NULL terminate
  237. buffer[(buffer_idx < buffer_length) ? buffer_idx : buffer_idx - 1] = 0;
  238. }
  239. } // namespace google_breakpad