Bug 1751497 - adjust wpt test-verify and test-coverage tasks to be fission only....
[gecko.git] / mozglue / linker / BaseElf.cpp
blob78542b38752fa17fbffb6116893453a2124afc47
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/. */
5 #include "BaseElf.h"
6 #include "Elfxx.h"
7 #include "Logging.h"
8 #include "mozilla/IntegerPrintfMacros.h"
9 #include "mozilla/RefPtr.h"
11 using namespace Elf;
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;
22 return h;
25 void* BaseElf::GetSymbolPtr(const char* symbol) const {
26 return GetSymbolPtr(symbol, Hash(symbol));
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;
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];
52 return nullptr;
55 bool BaseElf::Contains(void* addr) const { return base.Contains(addr); }
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;
63 *pcount = 0;
64 return nullptr;
66 #endif
68 already_AddRefed<LibHandle> LoadedElf::Create(const char* path,
69 void* base_addr) {
70 DEBUG_LOG("LoadedElf::Create(\"%s\", %p) = ...", path, base_addr);
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.
80 if (mincore(const_cast<void*>(base_addr), PageSize(), &mapped))
81 return nullptr;
83 RefPtr<LoadedElf> elf = new LoadedElf(path);
85 const Ehdr* ehdr = Ehdr::validate(base_addr);
86 if (!ehdr) return nullptr;
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
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
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.
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;
132 max_vaddr -= min_vaddr;
134 if (!dyn) {
135 LOG("%s: No PT_DYNAMIC segment found", elf->GetPath());
136 return nullptr;
139 elf->base.Assign(base_addr, max_vaddr);
141 if (!elf->InitDyn(dyn)) return nullptr;
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
149 DEBUG_LOG("LoadedElf::Create(\"%s\", %p) = %p", path, base_addr,
150 static_cast<void*>(elf));
152 ElfLoader::Singleton.Register(elf);
153 return elf.forget();
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);
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;
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;
189 return false;