summaryrefslogtreecommitdiffstats
path: root/mozglue/linker/BaseElf.cpp
blob: 78542b38752fa17fbffb6116893453a2124afc47 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "BaseElf.h"
#include "Elfxx.h"
#include "Logging.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/RefPtr.h"

using namespace Elf;

unsigned long BaseElf::Hash(const char* symbol) {
  const unsigned char* sym = reinterpret_cast<const unsigned char*>(symbol);
  unsigned long h = 0, g;
  while (*sym) {
    h = (h << 4) + *sym++;
    g = h & 0xf0000000;
    h ^= g;
    h ^= g >> 24;
  }
  return h;
}

void* BaseElf::GetSymbolPtr(const char* symbol) const {
  return GetSymbolPtr(symbol, Hash(symbol));
}

void* BaseElf::GetSymbolPtr(const char* symbol, unsigned long hash) const {
  const Sym* sym = GetSymbol(symbol, hash);
  void* ptr = nullptr;
  if (sym && sym->st_shndx != SHN_UNDEF) ptr = GetPtr(sym->st_value);
  DEBUG_LOG("BaseElf::GetSymbolPtr(%p [\"%s\"], \"%s\") = %p",
            reinterpret_cast<const void*>(this), GetPath(), symbol, ptr);
  return ptr;
}

const Sym* BaseElf::GetSymbol(const char* symbol, unsigned long hash) const {
  /* Search symbol with the buckets and chains tables.
   * The hash computed from the symbol name gives an index in the buckets
   * table. The corresponding value in the bucket table is an index in the
   * symbols table and in the chains table.
   * If the corresponding symbol in the symbols table matches, we're done.
   * Otherwise, the corresponding value in the chains table is a new index
   * in both tables, which corresponding symbol is tested and so on and so
   * forth */
  size_t bucket = hash % buckets.numElements();
  for (size_t y = buckets[bucket]; y != STN_UNDEF; y = chains[y]) {
    if (strcmp(symbol, strtab.GetStringAt(symtab[y].st_name))) continue;
    return &symtab[y];
  }
  return nullptr;
}

bool BaseElf::Contains(void* addr) const { return base.Contains(addr); }

#ifdef __ARM_EABI__
const void* BaseElf::FindExidx(int* pcount) const {
  if (arm_exidx) {
    *pcount = arm_exidx.numElements();
    return arm_exidx;
  }
  *pcount = 0;
  return nullptr;
}
#endif

already_AddRefed<LibHandle> LoadedElf::Create(const char* path,
                                              void* base_addr) {
  DEBUG_LOG("LoadedElf::Create(\"%s\", %p) = ...", path, base_addr);

  uint8_t mapped;
  /* If the page is not mapped, mincore returns an error. If base_addr is
   * nullptr, as would happen if the corresponding binary is prelinked with
   * the prelink look (but not with the android apriori tool), no page being
   * mapped there (right?), mincore returns an error, too, which makes
   * prelinked libraries on glibc unsupported. This is not an interesting
   * use case for now, so don't try supporting that case.
   */
  if (mincore(const_cast<void*>(base_addr), PageSize(), &mapped))
    return nullptr;

  RefPtr<LoadedElf> elf = new LoadedElf(path);

  const Ehdr* ehdr = Ehdr::validate(base_addr);
  if (!ehdr) return nullptr;

  Addr min_vaddr = (Addr)-1;  // We want to find the lowest and biggest
  Addr max_vaddr = 0;         // virtual address used by this Elf.
  const Phdr* dyn = nullptr;
#ifdef __ARM_EABI__
  const Phdr* arm_exidx_phdr = nullptr;
#endif

  Array<Phdr> phdrs(reinterpret_cast<const char*>(ehdr) + ehdr->e_phoff,
                    ehdr->e_phnum);
  for (auto phdr = phdrs.begin(); phdr < phdrs.end(); ++phdr) {
    switch (phdr->p_type) {
      case PT_LOAD:
        if (phdr->p_vaddr < min_vaddr) min_vaddr = phdr->p_vaddr;
        if (max_vaddr < phdr->p_vaddr + phdr->p_memsz)
          max_vaddr = phdr->p_vaddr + phdr->p_memsz;
        break;
      case PT_DYNAMIC:
        dyn = &*phdr;
        break;
#ifdef __ARM_EABI__
      case PT_ARM_EXIDX:
        /* We cannot initialize arm_exidx here
           because we don't have a base yet */
        arm_exidx_phdr = &*phdr;
        break;
#endif
    }
  }

  /* If the lowest PT_LOAD virtual address in headers is not 0, then the ELF
   * is either prelinked or a non-PIE executable. The former case is not
   * possible, because base_addr would be nullptr and the mincore test above
   * would already have made us return.
   * For a non-PIE executable, PT_LOADs contain absolute addresses, so in
   * practice, this means min_vaddr should be equal to base_addr. max_vaddr
   * can thus be adjusted accordingly.
   */
  if (min_vaddr != 0) {
    void* min_vaddr_ptr =
        reinterpret_cast<void*>(static_cast<uintptr_t>(min_vaddr));
    if (min_vaddr_ptr != base_addr) {
      LOG("%s: %p != %p", elf->GetPath(), min_vaddr_ptr, base_addr);
      return nullptr;
    }
    max_vaddr -= min_vaddr;
  }
  if (!dyn) {
    LOG("%s: No PT_DYNAMIC segment found", elf->GetPath());
    return nullptr;
  }

  elf->base.Assign(base_addr, max_vaddr);

  if (!elf->InitDyn(dyn)) return nullptr;

#ifdef __ARM_EABI__
  if (arm_exidx_phdr)
    elf->arm_exidx.InitSize(elf->GetPtr(arm_exidx_phdr->p_vaddr),
                            arm_exidx_phdr->p_memsz);
#endif

  DEBUG_LOG("LoadedElf::Create(\"%s\", %p) = %p", path, base_addr,
            static_cast<void*>(elf));

  ElfLoader::Singleton.Register(elf);
  return elf.forget();
}

bool LoadedElf::InitDyn(const Phdr* pt_dyn) {
  Array<Dyn> dyns;
  dyns.InitSize(GetPtr<Dyn>(pt_dyn->p_vaddr), pt_dyn->p_filesz);

  size_t symnum = 0;
  for (auto dyn = dyns.begin(); dyn < dyns.end() && dyn->d_tag; ++dyn) {
    switch (dyn->d_tag) {
      case DT_HASH: {
        DEBUG_LOG("%s 0x%08" PRIxPTR, "DT_HASH", uintptr_t(dyn->d_un.d_val));
        const Elf::Word* hash_table_header = GetPtr<Elf::Word>(dyn->d_un.d_ptr);
        symnum = hash_table_header[1];
        buckets.Init(&hash_table_header[2], hash_table_header[0]);
        chains.Init(&*buckets.end());
      } break;
      case DT_STRTAB:
        DEBUG_LOG("%s 0x%08" PRIxPTR, "DT_STRTAB", uintptr_t(dyn->d_un.d_val));
        strtab.Init(GetPtr(dyn->d_un.d_ptr));
        break;
      case DT_SYMTAB:
        DEBUG_LOG("%s 0x%08" PRIxPTR, "DT_SYMTAB", uintptr_t(dyn->d_un.d_val));
        symtab.Init(GetPtr(dyn->d_un.d_ptr));
        break;
    }
  }
  if (!buckets || !symnum) {
    ERROR("%s: Missing or broken DT_HASH", GetPath());
  } else if (!strtab) {
    ERROR("%s: Missing DT_STRTAB", GetPath());
  } else if (!symtab) {
    ERROR("%s: Missing DT_SYMTAB", GetPath());
  } else {
    return true;
  }
  return false;
}