tor-browser

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

BaseElf.cpp (6427B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "BaseElf.h"
      6 #include "Elfxx.h"
      7 #include "Logging.h"
      8 #include "mozilla/IntegerPrintfMacros.h"
      9 #include "mozilla/RefPtr.h"
     10 
     11 using namespace Elf;
     12 
     13 unsigned long BaseElf::Hash(const char* symbol) {
     14  const unsigned char* sym = reinterpret_cast<const unsigned char*>(symbol);
     15  unsigned long h = 0, g;
     16  while (*sym) {
     17    h = (h << 4) + *sym++;
     18    g = h & 0xf0000000;
     19    h ^= g;
     20    h ^= g >> 24;
     21  }
     22  return h;
     23 }
     24 
     25 void* BaseElf::GetSymbolPtr(const char* symbol) const {
     26  return GetSymbolPtr(symbol, Hash(symbol));
     27 }
     28 
     29 void* BaseElf::GetSymbolPtr(const char* symbol, unsigned long hash) const {
     30  const Sym* sym = GetSymbol(symbol, hash);
     31  void* ptr = nullptr;
     32  if (sym && sym->st_shndx != SHN_UNDEF) ptr = GetPtr(sym->st_value);
     33  DEBUG_LOG("BaseElf::GetSymbolPtr(%p [\"%s\"], \"%s\") = %p",
     34            reinterpret_cast<const void*>(this), GetPath(), symbol, ptr);
     35  return ptr;
     36 }
     37 
     38 const Sym* BaseElf::GetSymbol(const char* symbol, unsigned long hash) const {
     39  /* Search symbol with the buckets and chains tables.
     40   * The hash computed from the symbol name gives an index in the buckets
     41   * table. The corresponding value in the bucket table is an index in the
     42   * symbols table and in the chains table.
     43   * If the corresponding symbol in the symbols table matches, we're done.
     44   * Otherwise, the corresponding value in the chains table is a new index
     45   * in both tables, which corresponding symbol is tested and so on and so
     46   * forth */
     47  size_t bucket = hash % buckets.numElements();
     48  for (size_t y = buckets[bucket]; y != STN_UNDEF; y = chains[y]) {
     49    if (strcmp(symbol, strtab.GetStringAt(symtab[y].st_name))) continue;
     50    return &symtab[y];
     51  }
     52  return nullptr;
     53 }
     54 
     55 bool BaseElf::Contains(void* addr) const { return base.Contains(addr); }
     56 
     57 #ifdef __ARM_EABI__
     58 const void* BaseElf::FindExidx(int* pcount) const {
     59  if (arm_exidx) {
     60    *pcount = arm_exidx.numElements();
     61    return arm_exidx;
     62  }
     63  *pcount = 0;
     64  return nullptr;
     65 }
     66 #endif
     67 
     68 already_AddRefed<LibHandle> LoadedElf::Create(const char* path,
     69                                              void* base_addr) {
     70  DEBUG_LOG("LoadedElf::Create(\"%s\", %p) = ...", path, base_addr);
     71 
     72  uint8_t mapped;
     73  /* If the page is not mapped, mincore returns an error. If base_addr is
     74   * nullptr, as would happen if the corresponding binary is prelinked with
     75   * the prelink look (but not with the android apriori tool), no page being
     76   * mapped there (right?), mincore returns an error, too, which makes
     77   * prelinked libraries on glibc unsupported. This is not an interesting
     78   * use case for now, so don't try supporting that case.
     79   */
     80  if (mincore(const_cast<void*>(base_addr), PageSize(), &mapped))
     81    return nullptr;
     82 
     83  RefPtr<LoadedElf> elf = new LoadedElf(path);
     84 
     85  const Ehdr* ehdr = Ehdr::validate(base_addr);
     86  if (!ehdr) return nullptr;
     87 
     88  Addr min_vaddr = (Addr)-1;  // We want to find the lowest and biggest
     89  Addr max_vaddr = 0;         // virtual address used by this Elf.
     90  const Phdr* dyn = nullptr;
     91 #ifdef __ARM_EABI__
     92  const Phdr* arm_exidx_phdr = nullptr;
     93 #endif
     94 
     95  Array<Phdr> phdrs(reinterpret_cast<const char*>(ehdr) + ehdr->e_phoff,
     96                    ehdr->e_phnum);
     97  for (auto phdr = phdrs.begin(); phdr < phdrs.end(); ++phdr) {
     98    switch (phdr->p_type) {
     99      case PT_LOAD:
    100        if (phdr->p_vaddr < min_vaddr) min_vaddr = phdr->p_vaddr;
    101        if (max_vaddr < phdr->p_vaddr + phdr->p_memsz)
    102          max_vaddr = phdr->p_vaddr + phdr->p_memsz;
    103        break;
    104      case PT_DYNAMIC:
    105        dyn = &*phdr;
    106        break;
    107 #ifdef __ARM_EABI__
    108      case PT_ARM_EXIDX:
    109        /* We cannot initialize arm_exidx here
    110           because we don't have a base yet */
    111        arm_exidx_phdr = &*phdr;
    112        break;
    113 #endif
    114    }
    115  }
    116 
    117  /* If the lowest PT_LOAD virtual address in headers is not 0, then the ELF
    118   * is either prelinked or a non-PIE executable. The former case is not
    119   * possible, because base_addr would be nullptr and the mincore test above
    120   * would already have made us return.
    121   * For a non-PIE executable, PT_LOADs contain absolute addresses, so in
    122   * practice, this means min_vaddr should be equal to base_addr. max_vaddr
    123   * can thus be adjusted accordingly.
    124   */
    125  if (min_vaddr != 0) {
    126    void* min_vaddr_ptr =
    127        reinterpret_cast<void*>(static_cast<uintptr_t>(min_vaddr));
    128    if (min_vaddr_ptr != base_addr) {
    129      LOG("%s: %p != %p", elf->GetPath(), min_vaddr_ptr, base_addr);
    130      return nullptr;
    131    }
    132    max_vaddr -= min_vaddr;
    133  }
    134  if (!dyn) {
    135    LOG("%s: No PT_DYNAMIC segment found", elf->GetPath());
    136    return nullptr;
    137  }
    138 
    139  elf->base.Assign(base_addr, max_vaddr);
    140 
    141  if (!elf->InitDyn(dyn)) return nullptr;
    142 
    143 #ifdef __ARM_EABI__
    144  if (arm_exidx_phdr)
    145    elf->arm_exidx.InitSize(elf->GetPtr(arm_exidx_phdr->p_vaddr),
    146                            arm_exidx_phdr->p_memsz);
    147 #endif
    148 
    149  DEBUG_LOG("LoadedElf::Create(\"%s\", %p) = %p", path, base_addr,
    150            static_cast<void*>(elf));
    151 
    152  ElfLoader::Singleton.Register(elf);
    153  return elf.forget();
    154 }
    155 
    156 bool LoadedElf::InitDyn(const Phdr* pt_dyn) {
    157  Array<Dyn> dyns;
    158  dyns.InitSize(GetPtr<Dyn>(pt_dyn->p_vaddr), pt_dyn->p_filesz);
    159 
    160  size_t symnum = 0;
    161  for (auto dyn = dyns.begin(); dyn < dyns.end() && dyn->d_tag; ++dyn) {
    162    switch (dyn->d_tag) {
    163      case DT_HASH: {
    164        DEBUG_LOG("%s 0x%08" PRIxPTR, "DT_HASH", uintptr_t(dyn->d_un.d_val));
    165        const Elf::Word* hash_table_header = GetPtr<Elf::Word>(dyn->d_un.d_ptr);
    166        symnum = hash_table_header[1];
    167        buckets.Init(&hash_table_header[2], hash_table_header[0]);
    168        chains.Init(&*buckets.end());
    169      } break;
    170      case DT_STRTAB:
    171        DEBUG_LOG("%s 0x%08" PRIxPTR, "DT_STRTAB", uintptr_t(dyn->d_un.d_val));
    172        strtab.Init(GetPtr(dyn->d_un.d_ptr));
    173        break;
    174      case DT_SYMTAB:
    175        DEBUG_LOG("%s 0x%08" PRIxPTR, "DT_SYMTAB", uintptr_t(dyn->d_un.d_val));
    176        symtab.Init(GetPtr(dyn->d_un.d_ptr));
    177        break;
    178    }
    179  }
    180  if (!buckets || !symnum) {
    181    ERROR("%s: Missing or broken DT_HASH", GetPath());
    182  } else if (!strtab) {
    183    ERROR("%s: Missing DT_STRTAB", GetPath());
    184  } else if (!symtab) {
    185    ERROR("%s: Missing DT_SYMTAB", GetPath());
    186  } else {
    187    return true;
    188  }
    189  return false;
    190 }