1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "BaseProfilerSharedLibraries.h"
8
9 #define PATH_MAX_TOSTRING(x) #x
10 #define PATH_MAX_STRING(x) PATH_MAX_TOSTRING(x)
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <limits.h>
15 #include <unistd.h>
16 #include <fstream>
17 #include "platform.h"
18 #include "mozilla/Sprintf.h"
19 #include "mozilla/Unused.h"
20
21 #include <algorithm>
22 #include <arpa/inet.h>
23 #include <dlfcn.h>
24 #include <elf.h>
25 #include <fcntl.h>
26 #if defined(GP_OS_linux) || defined(GP_OS_android)
27 # include <features.h>
28 #endif
29 #include <sys/mman.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <vector>
33
34 #if defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
35 # include <link.h> // dl_phdr_info, ElfW()
36 #else
37 # error "Unexpected configuration"
38 #endif
39
40 #if defined(GP_OS_android)
41 extern "C" MOZ_EXPORT __attribute__((weak)) int dl_iterate_phdr(
42 int (*callback)(struct dl_phdr_info* info, size_t size, void* data),
43 void* data);
44 #endif
45
46 #if defined(GP_OS_freebsd) && !defined(ElfW)
47 # define ElfW(type) Elf_##type
48 #endif
49
50 // ----------------------------------------------------------------------------
51 // Starting imports from toolkit/crashreporter/google-breakpad/, as needed by
52 // this file when moved to mozglue.
53
54 // Imported from
55 // toolkit/crashreporter/google-breakpad/src/common/memory_range.h.
56 // A lightweight wrapper with a pointer and a length to encapsulate a contiguous
57 // range of memory. It provides helper methods for checked access of a subrange
58 // of the memory. Its implemementation does not allocate memory or call into
59 // libc functions, and is thus safer to use in a crashed environment.
60 class MemoryRange {
61 public:
MemoryRange()62 MemoryRange() : data_(NULL), length_(0) {}
63
MemoryRange(const void * data,size_t length)64 MemoryRange(const void* data, size_t length) { Set(data, length); }
65
66 // Returns true if this memory range contains no data.
IsEmpty() const67 bool IsEmpty() const {
68 // Set() guarantees that |length_| is zero if |data_| is NULL.
69 return length_ == 0;
70 }
71
72 // Resets to an empty range.
Reset()73 void Reset() {
74 data_ = NULL;
75 length_ = 0;
76 }
77
78 // Sets this memory range to point to |data| and its length to |length|.
Set(const void * data,size_t length)79 void Set(const void* data, size_t length) {
80 data_ = reinterpret_cast<const uint8_t*>(data);
81 // Always set |length_| to zero if |data_| is NULL.
82 length_ = data ? length : 0;
83 }
84
85 // Returns true if this range covers a subrange of |sub_length| bytes
86 // at |sub_offset| bytes of this memory range, or false otherwise.
Covers(size_t sub_offset,size_t sub_length) const87 bool Covers(size_t sub_offset, size_t sub_length) const {
88 // The following checks verify that:
89 // 1. sub_offset is within [ 0 .. length_ - 1 ]
90 // 2. sub_offset + sub_length is within
91 // [ sub_offset .. length_ ]
92 return sub_offset < length_ && sub_offset + sub_length >= sub_offset &&
93 sub_offset + sub_length <= length_;
94 }
95
96 // Returns a raw data pointer to a subrange of |sub_length| bytes at
97 // |sub_offset| bytes of this memory range, or NULL if the subrange
98 // is out of bounds.
GetData(size_t sub_offset,size_t sub_length) const99 const void* GetData(size_t sub_offset, size_t sub_length) const {
100 return Covers(sub_offset, sub_length) ? (data_ + sub_offset) : NULL;
101 }
102
103 // Same as the two-argument version of GetData() but uses sizeof(DataType)
104 // as the subrange length and returns an |DataType| pointer for convenience.
105 template <typename DataType>
GetData(size_t sub_offset) const106 const DataType* GetData(size_t sub_offset) const {
107 return reinterpret_cast<const DataType*>(
108 GetData(sub_offset, sizeof(DataType)));
109 }
110
111 // Returns a raw pointer to the |element_index|-th element of an array
112 // of elements of length |element_size| starting at |sub_offset| bytes
113 // of this memory range, or NULL if the element is out of bounds.
GetArrayElement(size_t element_offset,size_t element_size,unsigned element_index) const114 const void* GetArrayElement(size_t element_offset, size_t element_size,
115 unsigned element_index) const {
116 size_t sub_offset = element_offset + element_index * element_size;
117 return GetData(sub_offset, element_size);
118 }
119
120 // Same as the three-argument version of GetArrayElement() but deduces
121 // the element size using sizeof(ElementType) and returns an |ElementType|
122 // pointer for convenience.
123 template <typename ElementType>
GetArrayElement(size_t element_offset,unsigned element_index) const124 const ElementType* GetArrayElement(size_t element_offset,
125 unsigned element_index) const {
126 return reinterpret_cast<const ElementType*>(
127 GetArrayElement(element_offset, sizeof(ElementType), element_index));
128 }
129
130 // Returns a subrange of |sub_length| bytes at |sub_offset| bytes of
131 // this memory range, or an empty range if the subrange is out of bounds.
Subrange(size_t sub_offset,size_t sub_length) const132 MemoryRange Subrange(size_t sub_offset, size_t sub_length) const {
133 return Covers(sub_offset, sub_length)
134 ? MemoryRange(data_ + sub_offset, sub_length)
135 : MemoryRange();
136 }
137
138 // Returns a pointer to the beginning of this memory range.
data() const139 const uint8_t* data() const { return data_; }
140
141 // Returns the length, in bytes, of this memory range.
length() const142 size_t length() const { return length_; }
143
144 private:
145 // Pointer to the beginning of this memory range.
146 const uint8_t* data_;
147
148 // Length, in bytes, of this memory range.
149 size_t length_;
150 };
151
152 // Imported from
153 // toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.h
154 // and inlined .cc.
155 // A utility class for mapping a file into memory for read-only access of the
156 // file content. Its implementation avoids calling into libc functions by
157 // directly making system calls for open, close, mmap, and munmap.
158 class MemoryMappedFile {
159 public:
MemoryMappedFile()160 MemoryMappedFile() {}
161
162 // Constructor that calls Map() to map a file at |path| into memory.
163 // If Map() fails, the object behaves as if it is default constructed.
MemoryMappedFile(const char * path,size_t offset)164 MemoryMappedFile(const char* path, size_t offset) { Map(path, offset); }
165
166 MemoryMappedFile(const MemoryMappedFile&) = delete;
167 MemoryMappedFile& operator=(const MemoryMappedFile&) = delete;
168
~MemoryMappedFile()169 ~MemoryMappedFile() {}
170
171 // Maps a file at |path| into memory, which can then be accessed via
172 // content() as a MemoryRange object or via data(), and returns true on
173 // success. Mapping an empty file will succeed but with data() and size()
174 // returning NULL and 0, respectively. An existing mapping is unmapped
175 // before a new mapping is created.
Map(const char * path,size_t offset)176 bool Map(const char* path, size_t offset) {
177 Unmap();
178
179 int fd = open(path, O_RDONLY, 0);
180 if (fd == -1) {
181 return false;
182 }
183
184 #if defined(__x86_64__) || defined(__aarch64__) || \
185 (defined(__mips__) && _MIPS_SIM == _ABI64) || \
186 !(defined(GP_OS_linux) || defined(GP_OS_android))
187
188 struct stat st;
189 if (fstat(fd, &st) == -1 || st.st_size < 0) {
190 #else
191 struct stat64 st;
192 if (fstat64(fd, &st) == -1 || st.st_size < 0) {
193 #endif
194 close(fd);
195 return false;
196 }
197
198 // Strangely file size can be negative, but we check above that it is not.
199 size_t file_len = static_cast<size_t>(st.st_size);
200 // If the file does not extend beyond the offset, simply use an empty
201 // MemoryRange and return true. Don't bother to call mmap()
202 // even though mmap() can handle an empty file on some platforms.
203 if (offset >= file_len) {
204 close(fd);
205 return true;
206 }
207
208 void* data = mmap(NULL, file_len, PROT_READ, MAP_PRIVATE, fd, offset);
209 close(fd);
210 if (data == MAP_FAILED) {
211 return false;
212 }
213
214 content_.Set(data, file_len - offset);
215 return true;
216 }
217
218 // Unmaps the memory for the mapped file. It's a no-op if no file is
219 // mapped.
220 void Unmap() {
221 if (content_.data()) {
222 munmap(const_cast<uint8_t*>(content_.data()), content_.length());
223 content_.Set(NULL, 0);
224 }
225 }
226
227 // Returns a MemoryRange object that covers the memory for the mapped
228 // file. The MemoryRange object is empty if no file is mapped.
229 const MemoryRange& content() const { return content_; }
230
231 // Returns a pointer to the beginning of the memory for the mapped file.
232 // or NULL if no file is mapped or the mapped file is empty.
233 const void* data() const { return content_.data(); }
234
235 // Returns the size in bytes of the mapped file, or zero if no file
236 // is mapped.
237 size_t size() const { return content_.length(); }
238
239 private:
240 // Mapped file content as a MemoryRange object.
241 MemoryRange content_;
242 };
243
244 // Imported from
245 // toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h and inlined
246 // .cc.
247 // GNU binutils' ld defaults to 'sha1', which is 160 bits == 20 bytes,
248 // so this is enough to fit that, which most binaries will use.
249 // This is just a sensible default for vectors so most callers can get away with
250 // stack allocation.
251 static const size_t kDefaultBuildIdSize = 20;
252
253 // Used in a few places for backwards-compatibility.
254 typedef struct {
255 uint32_t data1;
256 uint16_t data2;
257 uint16_t data3;
258 uint8_t data4[8];
259 } MDGUID; /* GUID */
260
261 const size_t kMDGUIDSize = sizeof(MDGUID);
262
263 class FileID {
264 public:
FileID(const char * path)265 explicit FileID(const char* path) : path_(path) {}
~FileID()266 ~FileID() {}
267
268 // Load the identifier for the elf file path specified in the constructor into
269 // |identifier|.
270 //
271 // The current implementation will look for a .note.gnu.build-id
272 // section and use that as the file id, otherwise it falls back to
273 // XORing the first 4096 bytes of the .text section to generate an identifier.
ElfFileIdentifier(std::vector<uint8_t> & identifier)274 bool ElfFileIdentifier(std::vector<uint8_t>& identifier) {
275 MemoryMappedFile mapped_file(path_.c_str(), 0);
276 if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)?
277 return false;
278
279 return ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
280 }
281
282 // Traits classes so consumers can write templatized code to deal
283 // with specific ELF bits.
284 struct ElfClass32 {
285 typedef Elf32_Addr Addr;
286 typedef Elf32_Ehdr Ehdr;
287 typedef Elf32_Nhdr Nhdr;
288 typedef Elf32_Phdr Phdr;
289 typedef Elf32_Shdr Shdr;
290 typedef Elf32_Half Half;
291 typedef Elf32_Off Off;
292 typedef Elf32_Sym Sym;
293 typedef Elf32_Word Word;
294
295 static const int kClass = ELFCLASS32;
296 static const uint16_t kMachine = EM_386;
297 static const size_t kAddrSize = sizeof(Elf32_Addr);
298 static constexpr const char* kMachineName = "x86";
299 };
300
301 struct ElfClass64 {
302 typedef Elf64_Addr Addr;
303 typedef Elf64_Ehdr Ehdr;
304 typedef Elf64_Nhdr Nhdr;
305 typedef Elf64_Phdr Phdr;
306 typedef Elf64_Shdr Shdr;
307 typedef Elf64_Half Half;
308 typedef Elf64_Off Off;
309 typedef Elf64_Sym Sym;
310 typedef Elf64_Word Word;
311
312 static const int kClass = ELFCLASS64;
313 static const uint16_t kMachine = EM_X86_64;
314 static const size_t kAddrSize = sizeof(Elf64_Addr);
315 static constexpr const char* kMachineName = "x86_64";
316 };
317
318 // Internal helper method, exposed for convenience for callers
319 // that already have more info.
320 template <typename ElfClass>
FindElfSectionByName(const char * name,typename ElfClass::Word section_type,const typename ElfClass::Shdr * sections,const char * section_names,const char * names_end,int nsection)321 static const typename ElfClass::Shdr* FindElfSectionByName(
322 const char* name, typename ElfClass::Word section_type,
323 const typename ElfClass::Shdr* sections, const char* section_names,
324 const char* names_end, int nsection) {
325 if (!name || !sections || nsection == 0) {
326 return NULL;
327 }
328
329 int name_len = strlen(name);
330 if (name_len == 0) return NULL;
331
332 for (int i = 0; i < nsection; ++i) {
333 const char* section_name = section_names + sections[i].sh_name;
334 if (sections[i].sh_type == section_type &&
335 names_end - section_name >= name_len + 1 &&
336 strcmp(name, section_name) == 0) {
337 return sections + i;
338 }
339 }
340 return NULL;
341 }
342
343 struct ElfSegment {
344 const void* start;
345 size_t size;
346 };
347
348 // Convert an offset from an Elf header into a pointer to the mapped
349 // address in the current process. Takes an extra template parameter
350 // to specify the return type to avoid having to dynamic_cast the
351 // result.
352 template <typename ElfClass, typename T>
GetOffset(const typename ElfClass::Ehdr * elf_header,typename ElfClass::Off offset)353 static const T* GetOffset(const typename ElfClass::Ehdr* elf_header,
354 typename ElfClass::Off offset) {
355 return reinterpret_cast<const T*>(reinterpret_cast<uintptr_t>(elf_header) +
356 offset);
357 }
358
359 // ELF note name and desc are 32-bits word padded.
360 #define NOTE_PADDING(a) ((a + 3) & ~3)
361
ElfClassBuildIDNoteIdentifier(const void * section,size_t length,std::vector<uint8_t> & identifier)362 static bool ElfClassBuildIDNoteIdentifier(const void* section, size_t length,
363 std::vector<uint8_t>& identifier) {
364 static_assert(sizeof(ElfClass32::Nhdr) == sizeof(ElfClass64::Nhdr),
365 "Elf32_Nhdr and Elf64_Nhdr should be the same");
366 typedef typename ElfClass32::Nhdr Nhdr;
367
368 const void* section_end = reinterpret_cast<const char*>(section) + length;
369 const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section);
370 while (reinterpret_cast<const void*>(note_header) < section_end) {
371 if (note_header->n_type == NT_GNU_BUILD_ID) break;
372 note_header = reinterpret_cast<const Nhdr*>(
373 reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) +
374 NOTE_PADDING(note_header->n_namesz) +
375 NOTE_PADDING(note_header->n_descsz));
376 }
377 if (reinterpret_cast<const void*>(note_header) >= section_end ||
378 note_header->n_descsz == 0) {
379 return false;
380 }
381
382 const uint8_t* build_id = reinterpret_cast<const uint8_t*>(note_header) +
383 sizeof(Nhdr) +
384 NOTE_PADDING(note_header->n_namesz);
385 identifier.insert(identifier.end(), build_id,
386 build_id + note_header->n_descsz);
387
388 return true;
389 }
390
391 template <typename ElfClass>
FindElfClassSection(const char * elf_base,const char * section_name,typename ElfClass::Word section_type,const void ** section_start,size_t * section_size)392 static bool FindElfClassSection(const char* elf_base,
393 const char* section_name,
394 typename ElfClass::Word section_type,
395 const void** section_start,
396 size_t* section_size) {
397 typedef typename ElfClass::Ehdr Ehdr;
398 typedef typename ElfClass::Shdr Shdr;
399
400 if (!elf_base || !section_start || !section_size) {
401 return false;
402 }
403
404 if (strncmp(elf_base, ELFMAG, SELFMAG) != 0) {
405 return false;
406 }
407
408 const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
409 if (elf_header->e_ident[EI_CLASS] != ElfClass::kClass) {
410 return false;
411 }
412
413 const Shdr* sections =
414 GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
415 const Shdr* section_names = sections + elf_header->e_shstrndx;
416 const char* names =
417 GetOffset<ElfClass, char>(elf_header, section_names->sh_offset);
418 const char* names_end = names + section_names->sh_size;
419
420 const Shdr* section =
421 FindElfSectionByName<ElfClass>(section_name, section_type, sections,
422 names, names_end, elf_header->e_shnum);
423
424 if (section != NULL && section->sh_size > 0) {
425 *section_start = elf_base + section->sh_offset;
426 *section_size = section->sh_size;
427 }
428
429 return true;
430 }
431
432 template <typename ElfClass>
FindElfClassSegment(const char * elf_base,typename ElfClass::Word segment_type,std::vector<ElfSegment> * segments)433 static bool FindElfClassSegment(const char* elf_base,
434 typename ElfClass::Word segment_type,
435 std::vector<ElfSegment>* segments) {
436 typedef typename ElfClass::Ehdr Ehdr;
437 typedef typename ElfClass::Phdr Phdr;
438
439 if (!elf_base || !segments) {
440 return false;
441 }
442
443 if (strncmp(elf_base, ELFMAG, SELFMAG) != 0) {
444 return false;
445 }
446
447 const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
448 if (elf_header->e_ident[EI_CLASS] != ElfClass::kClass) {
449 return false;
450 }
451
452 const Phdr* phdrs =
453 GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff);
454
455 for (int i = 0; i < elf_header->e_phnum; ++i) {
456 if (phdrs[i].p_type == segment_type) {
457 ElfSegment seg = {};
458 seg.start = elf_base + phdrs[i].p_offset;
459 seg.size = phdrs[i].p_filesz;
460 segments->push_back(seg);
461 }
462 }
463
464 return true;
465 }
466
IsValidElf(const void * elf_base)467 static bool IsValidElf(const void* elf_base) {
468 return strncmp(reinterpret_cast<const char*>(elf_base), ELFMAG, SELFMAG) ==
469 0;
470 }
471
ElfClass(const void * elf_base)472 static int ElfClass(const void* elf_base) {
473 const ElfW(Ehdr)* elf_header =
474 reinterpret_cast<const ElfW(Ehdr)*>(elf_base);
475
476 return elf_header->e_ident[EI_CLASS];
477 }
478
FindElfSection(const void * elf_mapped_base,const char * section_name,uint32_t section_type,const void ** section_start,size_t * section_size)479 static bool FindElfSection(const void* elf_mapped_base,
480 const char* section_name, uint32_t section_type,
481 const void** section_start, size_t* section_size) {
482 if (!elf_mapped_base || !section_start || !section_size) {
483 return false;
484 }
485
486 *section_start = NULL;
487 *section_size = 0;
488
489 if (!IsValidElf(elf_mapped_base)) return false;
490
491 int cls = ElfClass(elf_mapped_base);
492 const char* elf_base = static_cast<const char*>(elf_mapped_base);
493
494 if (cls == ELFCLASS32) {
495 return FindElfClassSection<ElfClass32>(elf_base, section_name,
496 section_type, section_start,
497 section_size) &&
498 *section_start != NULL;
499 } else if (cls == ELFCLASS64) {
500 return FindElfClassSection<ElfClass64>(elf_base, section_name,
501 section_type, section_start,
502 section_size) &&
503 *section_start != NULL;
504 }
505
506 return false;
507 }
508
FindElfSegments(const void * elf_mapped_base,uint32_t segment_type,std::vector<ElfSegment> * segments)509 static bool FindElfSegments(const void* elf_mapped_base,
510 uint32_t segment_type,
511 std::vector<ElfSegment>* segments) {
512 if (!elf_mapped_base || !segments) {
513 return false;
514 }
515
516 if (!IsValidElf(elf_mapped_base)) return false;
517
518 int cls = ElfClass(elf_mapped_base);
519 const char* elf_base = static_cast<const char*>(elf_mapped_base);
520
521 if (cls == ELFCLASS32) {
522 return FindElfClassSegment<ElfClass32>(elf_base, segment_type, segments);
523 } else if (cls == ELFCLASS64) {
524 return FindElfClassSegment<ElfClass64>(elf_base, segment_type, segments);
525 }
526
527 return false;
528 }
529
530 // Attempt to locate a .note.gnu.build-id section in an ELF binary
531 // and copy it into |identifier|.
FindElfBuildIDNote(const void * elf_mapped_base,std::vector<uint8_t> & identifier)532 static bool FindElfBuildIDNote(const void* elf_mapped_base,
533 std::vector<uint8_t>& identifier) {
534 // lld normally creates 2 PT_NOTEs, gold normally creates 1.
535 std::vector<ElfSegment> segs;
536 if (FindElfSegments(elf_mapped_base, PT_NOTE, &segs)) {
537 for (ElfSegment& seg : segs) {
538 if (ElfClassBuildIDNoteIdentifier(seg.start, seg.size, identifier)) {
539 return true;
540 }
541 }
542 }
543
544 void* note_section;
545 size_t note_size;
546 if (FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
547 (const void**)¬e_section, ¬e_size)) {
548 return ElfClassBuildIDNoteIdentifier(note_section, note_size, identifier);
549 }
550
551 return false;
552 }
553
554 // Attempt to locate the .text section of an ELF binary and generate
555 // a simple hash by XORing the first page worth of bytes into |identifier|.
HashElfTextSection(const void * elf_mapped_base,std::vector<uint8_t> & identifier)556 static bool HashElfTextSection(const void* elf_mapped_base,
557 std::vector<uint8_t>& identifier) {
558 identifier.resize(kMDGUIDSize);
559
560 void* text_section;
561 size_t text_size;
562 if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS,
563 (const void**)&text_section, &text_size) ||
564 text_size == 0) {
565 return false;
566 }
567
568 // Only provide |kMDGUIDSize| bytes to keep identifiers produced by this
569 // function backwards-compatible.
570 memset(&identifier[0], 0, kMDGUIDSize);
571 const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section);
572 const uint8_t* ptr_end =
573 ptr + std::min(text_size, static_cast<size_t>(4096));
574 while (ptr < ptr_end) {
575 for (unsigned i = 0; i < kMDGUIDSize; i++) identifier[i] ^= ptr[i];
576 ptr += kMDGUIDSize;
577 }
578 return true;
579 }
580
581 // Load the identifier for the elf file mapped into memory at |base| into
582 // |identifier|. Return false if the identifier could not be created for this
583 // file.
ElfFileIdentifierFromMappedFile(const void * base,std::vector<uint8_t> & identifier)584 static bool ElfFileIdentifierFromMappedFile(
585 const void* base, std::vector<uint8_t>& identifier) {
586 // Look for a build id note first.
587 if (FindElfBuildIDNote(base, identifier)) return true;
588
589 // Fall back on hashing the first page of the text section.
590 return HashElfTextSection(base, identifier);
591 }
592
593 // These three functions are not ever called in an unsafe context, so it's OK
594 // to allocate memory and use libc.
bytes_to_hex_string(const uint8_t * bytes,size_t count)595 static std::string bytes_to_hex_string(const uint8_t* bytes, size_t count) {
596 std::string result;
597 for (unsigned int idx = 0; idx < count; ++idx) {
598 char buf[3];
599 SprintfLiteral(buf, "%02X", bytes[idx]);
600 result.append(buf);
601 }
602 return result;
603 }
604
605 // Convert the |identifier| data to a string. The string will
606 // be formatted as a UUID in all uppercase without dashes.
607 // (e.g., 22F065BBFC9C49F780FE26A7CEBD7BCE).
ConvertIdentifierToUUIDString(const std::vector<uint8_t> & identifier)608 static std::string ConvertIdentifierToUUIDString(
609 const std::vector<uint8_t>& identifier) {
610 uint8_t identifier_swapped[kMDGUIDSize] = {0};
611
612 // Endian-ness swap to match dump processor expectation.
613 memcpy(identifier_swapped, &identifier[0],
614 std::min(kMDGUIDSize, identifier.size()));
615 uint32_t* data1 = reinterpret_cast<uint32_t*>(identifier_swapped);
616 *data1 = htonl(*data1);
617 uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4);
618 *data2 = htons(*data2);
619 uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6);
620 *data3 = htons(*data3);
621
622 return bytes_to_hex_string(identifier_swapped, kMDGUIDSize);
623 }
624
625 // Convert the entire |identifier| data to a hex string.
ConvertIdentifierToString(const std::vector<uint8_t> & identifier)626 static std::string ConvertIdentifierToString(
627 const std::vector<uint8_t>& identifier) {
628 return bytes_to_hex_string(&identifier[0], identifier.size());
629 }
630
631 private:
632 // Storage for the path specified
633 std::string path_;
634 };
635
636 // End of imports from toolkit/crashreporter/google-breakpad/.
637 // ----------------------------------------------------------------------------
638
639 struct LoadedLibraryInfo {
LoadedLibraryInfoLoadedLibraryInfo640 LoadedLibraryInfo(const char* aName, unsigned long aBaseAddress,
641 unsigned long aFirstMappingStart,
642 unsigned long aLastMappingEnd)
643 : mName(aName),
644 mBaseAddress(aBaseAddress),
645 mFirstMappingStart(aFirstMappingStart),
646 mLastMappingEnd(aLastMappingEnd) {}
647
648 std::string mName;
649 unsigned long mBaseAddress;
650 unsigned long mFirstMappingStart;
651 unsigned long mLastMappingEnd;
652 };
653
IDtoUUIDString(const std::vector<uint8_t> & aIdentifier)654 static std::string IDtoUUIDString(const std::vector<uint8_t>& aIdentifier) {
655 std::string uuid = FileID::ConvertIdentifierToUUIDString(aIdentifier);
656 // This is '0', not '\0', since it represents the breakpad id age.
657 uuid += '0';
658 return uuid;
659 }
660
661 // Get the breakpad Id for the binary file pointed by bin_name
getId(const char * bin_name)662 static std::string getId(const char* bin_name) {
663 std::vector<uint8_t> identifier;
664 identifier.reserve(kDefaultBuildIdSize);
665
666 FileID file_id(bin_name);
667 if (file_id.ElfFileIdentifier(identifier)) {
668 return IDtoUUIDString(identifier);
669 }
670
671 return {};
672 }
673
SharedLibraryAtPath(const char * path,unsigned long libStart,unsigned long libEnd,unsigned long offset=0)674 static SharedLibrary SharedLibraryAtPath(const char* path,
675 unsigned long libStart,
676 unsigned long libEnd,
677 unsigned long offset = 0) {
678 std::string pathStr = path;
679
680 size_t pos = pathStr.rfind('\\');
681 std::string nameStr =
682 (pos != std::string::npos) ? pathStr.substr(pos + 1) : pathStr;
683
684 return SharedLibrary(libStart, libEnd, offset, getId(path), nameStr, pathStr,
685 nameStr, pathStr, std::string{}, "");
686 }
687
dl_iterate_callback(struct dl_phdr_info * dl_info,size_t size,void * data)688 static int dl_iterate_callback(struct dl_phdr_info* dl_info, size_t size,
689 void* data) {
690 auto libInfoList = reinterpret_cast<std::vector<LoadedLibraryInfo>*>(data);
691
692 if (dl_info->dlpi_phnum <= 0) return 0;
693
694 unsigned long baseAddress = dl_info->dlpi_addr;
695 unsigned long firstMappingStart = -1;
696 unsigned long lastMappingEnd = 0;
697
698 for (size_t i = 0; i < dl_info->dlpi_phnum; i++) {
699 if (dl_info->dlpi_phdr[i].p_type != PT_LOAD) {
700 continue;
701 }
702 unsigned long start = dl_info->dlpi_addr + dl_info->dlpi_phdr[i].p_vaddr;
703 unsigned long end = start + dl_info->dlpi_phdr[i].p_memsz;
704 if (start < firstMappingStart) {
705 firstMappingStart = start;
706 }
707 if (end > lastMappingEnd) {
708 lastMappingEnd = end;
709 }
710 }
711
712 libInfoList->push_back(LoadedLibraryInfo(dl_info->dlpi_name, baseAddress,
713 firstMappingStart, lastMappingEnd));
714
715 return 0;
716 }
717
GetInfoForSelf()718 SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
719 SharedLibraryInfo info;
720
721 #if defined(GP_OS_linux)
722 // We need to find the name of the executable (exeName, exeNameLen) and the
723 // address of its executable section (exeExeAddr) in the running image.
724 char exeName[PATH_MAX];
725 memset(exeName, 0, sizeof(exeName));
726
727 ssize_t exeNameLen = readlink("/proc/self/exe", exeName, sizeof(exeName) - 1);
728 if (exeNameLen == -1) {
729 // readlink failed for whatever reason. Note this, but keep going.
730 exeName[0] = '\0';
731 exeNameLen = 0;
732 // LOG("SharedLibraryInfo::GetInfoForSelf(): readlink failed");
733 } else {
734 // Assert no buffer overflow.
735 MOZ_RELEASE_ASSERT(exeNameLen >= 0 &&
736 exeNameLen < static_cast<ssize_t>(sizeof(exeName)));
737 }
738
739 unsigned long exeExeAddr = 0;
740 #endif
741
742 #if defined(GP_OS_android)
743 // If dl_iterate_phdr doesn't exist, we give up immediately.
744 if (!dl_iterate_phdr) {
745 // On ARM Android, dl_iterate_phdr is provided by the custom linker.
746 // So if libxul was loaded by the system linker (e.g. as part of
747 // xpcshell when running tests), it won't be available and we should
748 // not call it.
749 return info;
750 }
751 #endif
752
753 #if defined(GP_OS_linux) || defined(GP_OS_android)
754 // Read info from /proc/self/maps. We ignore most of it.
755 pid_t pid = mozilla::baseprofiler::profiler_current_process_id();
756 char path[PATH_MAX];
757 SprintfLiteral(path, "/proc/%d/maps", pid);
758 std::ifstream maps(path);
759 std::string line;
760 while (std::getline(maps, line)) {
761 int ret;
762 unsigned long start;
763 unsigned long end;
764 char perm[6 + 1] = "";
765 unsigned long offset;
766 char modulePath[PATH_MAX + 1] = "";
767 ret = sscanf(line.c_str(),
768 "%lx-%lx %6s %lx %*s %*x %" PATH_MAX_STRING(PATH_MAX) "s\n",
769 &start, &end, perm, &offset, modulePath);
770 if (!strchr(perm, 'x')) {
771 // Ignore non executable entries
772 continue;
773 }
774 if (ret != 5 && ret != 4) {
775 // LOG("SharedLibraryInfo::GetInfoForSelf(): "
776 // "reading /proc/self/maps failed");
777 continue;
778 }
779
780 # if defined(GP_OS_linux)
781 // Try to establish the main executable's load address.
782 if (exeNameLen > 0 && strcmp(modulePath, exeName) == 0) {
783 exeExeAddr = start;
784 }
785 # elif defined(GP_OS_android)
786 // Use /proc/pid/maps to get the dalvik-jit section since it has no
787 // associated phdrs.
788 if (0 == strcmp(modulePath, "/dev/ashmem/dalvik-jit-code-cache")) {
789 info.AddSharedLibrary(
790 SharedLibraryAtPath(modulePath, start, end, offset));
791 if (info.GetSize() > 10000) {
792 // LOG("SharedLibraryInfo::GetInfoForSelf(): "
793 // "implausibly large number of mappings acquired");
794 break;
795 }
796 }
797 # endif
798 }
799 #endif
800
801 std::vector<LoadedLibraryInfo> libInfoList;
802
803 // We collect the bulk of the library info using dl_iterate_phdr.
804 dl_iterate_phdr(dl_iterate_callback, &libInfoList);
805
806 for (const auto& libInfo : libInfoList) {
807 info.AddSharedLibrary(
808 SharedLibraryAtPath(libInfo.mName.c_str(), libInfo.mFirstMappingStart,
809 libInfo.mLastMappingEnd,
810 libInfo.mFirstMappingStart - libInfo.mBaseAddress));
811 }
812
813 #if defined(GP_OS_linux)
814 // Make another pass over the information we just harvested from
815 // dl_iterate_phdr. If we see a nameless object mapped at what we earlier
816 // established to be the main executable's load address, attach the
817 // executable's name to that entry.
818 for (size_t i = 0; i < info.GetSize(); i++) {
819 SharedLibrary& lib = info.GetMutableEntry(i);
820 if (lib.GetStart() <= exeExeAddr && exeExeAddr <= lib.GetEnd() &&
821 lib.GetDebugPath().empty()) {
822 lib = SharedLibraryAtPath(exeName, lib.GetStart(), lib.GetEnd(),
823 lib.GetOffset());
824
825 // We only expect to see one such entry.
826 break;
827 }
828 }
829 #endif
830
831 return info;
832 }
833
Initialize()834 void SharedLibraryInfo::Initialize() { /* do nothing */
835 }
836