tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

shared-libraries-linux.cc (31640B)


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