/* 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/RefPtr.h" using namespace Elf; using namespace mozilla; unsigned long BaseElf::Hash(const char *symbol) { const unsigned char *sym = reinterpret_cast(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(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 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(base_addr), PageSize(), &mapped)) return nullptr; RefPtr 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 phdrs(reinterpret_cast(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( static_cast(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(elf)); ElfLoader::Singleton.Register(elf); return elf.forget(); } bool LoadedElf::InitDyn(const Phdr *pt_dyn) { Array dyns; dyns.InitSize(GetPtr(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" PRIxAddr, "DT_HASH", dyn->d_un.d_val); const Elf::Word *hash_table_header = \ GetPtr(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" PRIxAddr, "DT_STRTAB", dyn->d_un.d_val); strtab.Init(GetPtr(dyn->d_un.d_ptr)); break; case DT_SYMTAB: DEBUG_LOG("%s 0x%08" PRIxAddr, "DT_SYMTAB", 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; }