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**)&note_section, &note_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