From c1730d52817d1c035975d5f316944949afcec7d0 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Tue, 7 Oct 2014 07:42:18 +0900 Subject: [PATCH] Bug 1077384 - Make libmozglue a pseudo-LD_PRELOAD on android. r=nfroyd In order to avoid adding more dlsym overhead than there already is, resolve symbols directly in the library containing the linker. (GetSymbolPtr is essentially free ; dlsym makes the system linker compule a ElfHash itself, and that's quite expensive to do on all symbols) This also paves the way for direct symbol resolution in all system libraries. --- mozglue/linker/BaseElf.cpp | 55 +++++++++++++++++++++ mozglue/linker/BaseElf.h | 93 ++++++++++++++++++++++++++++++++++++ mozglue/linker/CustomElf.cpp | 84 +++++++------------------------- mozglue/linker/CustomElf.h | 50 ++----------------- mozglue/linker/ElfLoader.cpp | 61 +++++++++++++++++++++++ mozglue/linker/ElfLoader.h | 19 ++++++++ mozglue/linker/Utils.h | 4 ++ mozglue/linker/moz.build | 1 + 8 files changed, 253 insertions(+), 114 deletions(-) create mode 100644 mozglue/linker/BaseElf.cpp create mode 100644 mozglue/linker/BaseElf.h diff --git a/mozglue/linker/BaseElf.cpp b/mozglue/linker/BaseElf.cpp new file mode 100644 index 00000000000..7cefecee03e --- /dev/null +++ b/mozglue/linker/BaseElf.cpp @@ -0,0 +1,55 @@ +/* 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" + +using namespace Elf; + +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, 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; +} diff --git a/mozglue/linker/BaseElf.h b/mozglue/linker/BaseElf.h new file mode 100644 index 00000000000..2c9164e92e7 --- /dev/null +++ b/mozglue/linker/BaseElf.h @@ -0,0 +1,93 @@ +/* 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/. */ + +#ifndef BaseElf_h +#define BaseElf_h + +#include "ElfLoader.h" +#include "Elfxx.h" + + +/** + * Base class for ELF libraries. This class includes things that will be + * common between SystemElfs and CustomElfs. + */ +class BaseElf: public LibHandle +{ +public: + /** + * Hash function for symbol lookup, as defined in ELF standard for System V. + */ + static unsigned long Hash(const char *symbol); + + /** + * Returns the address corresponding to the given symbol name (with a + * pre-computed hash). + */ + void *GetSymbolPtr(const char *symbol, unsigned long hash) const; + + /** + * Returns a pointer to the Elf Symbol in the Dynamic Symbol table + * corresponding to the given symbol name (with a pre-computed hash). + */ + const Elf::Sym *GetSymbol(const char *symbol, unsigned long hash) const; + + BaseElf(const char *path) + : LibHandle(path) { } + +protected: + /** + * Inherited from LibHandle. Those are temporary and are not supposed to + * be used. + */ + virtual void *GetSymbolPtr(const char *symbol) const { return NULL; }; + virtual bool Contains(void *addr) const { return false; }; + virtual void *GetBase() const { return GetPtr(0); } + +#ifdef __ARM_EABI__ + virtual const void *FindExidx(int *pcount) const { return NULL; }; +#endif + + virtual Mappable *GetMappable() const { return NULL; }; + +public: +/* private: */ + /** + * Returns a pointer relative to the base address where the library is + * loaded. + */ + void *GetPtr(const Elf::Addr offset) const + { + if (reinterpret_cast(offset) > base) + return reinterpret_cast(offset); + return base + offset; + } + + /** + * Like the above, but returns a typed (const) pointer + */ + template + const T *GetPtr(const Elf::Addr offset) const + { + if (reinterpret_cast(offset) > base) + return reinterpret_cast(offset); + return reinterpret_cast(base + offset); + } + + /* Base address where the library is loaded */ + MappedPtr base; + + /* Buckets and chains for the System V symbol hash table */ + Array buckets; + UnsizedArray chains; + +/* protected: */ + /* String table */ + Elf::Strtab strtab; + + /* Symbol table */ + UnsizedArray symtab; +}; + +#endif /* BaseElf_h */ diff --git a/mozglue/linker/CustomElf.cpp b/mozglue/linker/CustomElf.cpp index 4f79b5fb90f..999be93406d 100644 --- a/mozglue/linker/CustomElf.cpp +++ b/mozglue/linker/CustomElf.cpp @@ -7,7 +7,9 @@ #include #include #include +#include #include "CustomElf.h" +#include "BaseElf.h" #include "Mappable.h" #include "Logging.h" @@ -279,43 +281,10 @@ CustomElf::~CustomElf() ElfLoader::Singleton.Forget(this); } -namespace { - -/** - * Hash function for symbol lookup, as defined in ELF standard for System V - */ -unsigned long -ElfHash(const char *symbol) -{ - const unsigned char *sym = reinterpret_cast(symbol); - unsigned long h = 0, g; - while (*sym) { - h = (h << 4) + *sym++; - if ((g = h & 0xf0000000)) - h ^= g >> 24; - h &= ~g; - } - return h; -} - -} /* anonymous namespace */ - void * CustomElf::GetSymbolPtr(const char *symbol) const { - return GetSymbolPtr(symbol, ElfHash(symbol)); -} - -void * -CustomElf::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("CustomElf::GetSymbolPtr(%p [\"%s\"], \"%s\") = %p", - reinterpret_cast(this), GetPath(), symbol, ptr); - return ptr; + return BaseElf::GetSymbolPtr(symbol, Hash(symbol)); } void * @@ -372,16 +341,17 @@ CustomElf::GetSymbolPtrInDeps(const char *symbol) const } void *sym; - /* Search the symbol in the main program. Note this also tries all libraries - * the system linker will have loaded RTLD_GLOBAL. Unfortunately, that doesn't - * work with bionic, but its linker doesn't normally search the main binary - * anyways. Moreover, on android, the main binary is dalvik. */ -#ifdef __GLIBC__ - sym = dlsym(RTLD_DEFAULT, symbol); - DEBUG_LOG("dlsym(RTLD_DEFAULT, \"%s\") = %p", symbol, sym); - if (sym) - return sym; -#endif + + unsigned long hash = Hash(symbol); + + /* self_elf should never be NULL, but better safe than sorry. */ + if (ElfLoader::Singleton.self_elf) { + /* We consider the library containing this code a permanent LD_PRELOAD, + * so, check if the symbol exists here first. */ + sym = ElfLoader::Singleton.self_elf->GetSymbolPtr(symbol, hash); + if (sym) + return sym; + } /* Then search the symbol in our dependencies. Since we already searched in * libraries the system linker loaded, skip those (on glibc systems). We @@ -389,15 +359,13 @@ CustomElf::GetSymbolPtrInDeps(const char *symbol) const * directly, not in their own dependent libraries. Building libraries with * --no-allow-shlib-undefined ensures such indirect symbol dependency don't * happen. */ - unsigned long hash = ElfHash(symbol); for (std::vector >::const_iterator it = dependencies.begin(); it < dependencies.end(); ++it) { if (!(*it)->IsSystemElf()) { - sym = reinterpret_cast((*it).get())->GetSymbolPtr(symbol, hash); -#ifndef __GLIBC__ + sym = static_cast( + static_cast((*it).get()))->GetSymbolPtr(symbol, hash); } else { sym = (*it)->GetSymbolPtr(symbol); -#endif } if (sym) return sym; @@ -405,26 +373,6 @@ CustomElf::GetSymbolPtrInDeps(const char *symbol) const return nullptr; } -const Sym * -CustomElf::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 CustomElf::Contains(void *addr) const { diff --git a/mozglue/linker/CustomElf.h b/mozglue/linker/CustomElf.h index 02e1bf523ea..e8fd698d612 100644 --- a/mozglue/linker/CustomElf.h +++ b/mozglue/linker/CustomElf.h @@ -6,6 +6,7 @@ #define CustomElf_h #include "ElfLoader.h" +#include "BaseElf.h" #include "Logging.h" #include "Elfxx.h" @@ -13,7 +14,7 @@ * Library Handle class for ELF libraries we don't let the system linker * handle. */ -class CustomElf: public LibHandle, private ElfLoader::link_map +class CustomElf: public BaseElf, private ElfLoader::link_map { friend class ElfLoader; friend class SEGVHandler; @@ -31,7 +32,7 @@ public: const char *path, int flags); /** - * Inherited from LibHandle + * Inherited from LibHandle/BaseElf */ virtual ~CustomElf(); virtual void *GetSymbolPtr(const char *symbol) const; @@ -54,18 +55,6 @@ public: void stats(const char *when) const; private: - /** - * Returns a pointer to the Elf Symbol in the Dynamic Symbol table - * corresponding to the given symbol name (with a pre-computed hash). - */ - const Elf::Sym *GetSymbol(const char *symbol, unsigned long hash) const; - - /** - * Returns the address corresponding to the given symbol name (with a - * pre-computed hash). - */ - void *GetSymbolPtr(const char *symbol, unsigned long hash) const; - /** * Scan dependent libraries to find the address corresponding to the * given symbol name. This is used to find symbols that are undefined @@ -77,7 +66,7 @@ private: * Private constructor */ CustomElf(Mappable *mappable, const char *path) - : LibHandle(path) + : BaseElf(path) , mappable(mappable) , init(0) , fini(0) @@ -85,24 +74,6 @@ private: , has_text_relocs(false) { } - /** - * Returns a pointer relative to the base address where the library is - * loaded. - */ - void *GetPtr(const Elf::Addr offset) const - { - return base + offset; - } - - /** - * Like the above, but returns a typed (const) pointer - */ - template - const T *GetPtr(const Elf::Addr offset) const - { - return reinterpret_cast(base + offset); - } - /** * Loads an Elf segment defined by the given PT_LOAD header. * Returns whether this succeeded or failed. @@ -167,19 +138,6 @@ private: /* Appropriated Mappable */ mozilla::RefPtr mappable; - /* Base address where the library is loaded */ - MappedPtr base; - - /* String table */ - Elf::Strtab strtab; - - /* Symbol table */ - UnsizedArray symtab; - - /* Buckets and chains for the System V symbol hash table */ - Array buckets; - UnsizedArray chains; - /* List of dependent libraries */ std::vector > dependencies; diff --git a/mozglue/linker/ElfLoader.cpp b/mozglue/linker/ElfLoader.cpp index fd53750d769..21990e109b3 100644 --- a/mozglue/linker/ElfLoader.cpp +++ b/mozglue/linker/ElfLoader.cpp @@ -11,6 +11,7 @@ #include #include #include "ElfLoader.h" +#include "BaseElf.h" #include "CustomElf.h" #include "Mappable.h" #include "Logging.h" @@ -38,6 +39,10 @@ extern "C" MOZ_EXPORT const void * __gnu_Unwind_Find_exidx(void *pc, int *pcount) __attribute__((weak)); #endif +/* Pointer to the PT_DYNAMIC section of the executable or library + * containing this code. */ +extern "C" Elf::Dyn _DYNAMIC[]; + using namespace mozilla; /** @@ -331,6 +336,10 @@ ElfLoader::Load(const char *path, int flags, LibHandle *parent) /* Ensure logging is initialized or refresh if environment changed. */ Logging::Init(); + /* Ensure self_elf initialization. */ + if (!self_elf) + Init(); + RefPtr handle; /* Handle dlopen(nullptr) directly. */ @@ -466,6 +475,54 @@ ElfLoader::Forget(LibHandle *handle) } } +void +ElfLoader::Init() +{ + Dl_info info; + /* On Android < 4.1 can't reenter dl* functions. So when the library + * containing this code is dlopen()ed, it can't call dladdr from a + * static initializer. */ + if (dladdr(_DYNAMIC, &info) != 0) { + /* Ideally, we wouldn't be initializing self_elf this way, but until + * SystemElf actually inherits from BaseElf, we'll just do it this + * (gross) way. */ + UniquePtr elf = mozilla::MakeUnique(info.dli_fname); + elf->base.Assign(info.dli_fbase, -1); + size_t symnum = 0; + for (const Elf::Dyn *dyn = _DYNAMIC; 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 = \ + elf->GetPtr(dyn->d_un.d_ptr); + symnum = hash_table_header[1]; + elf->buckets.Init(&hash_table_header[2], hash_table_header[0]); + elf->chains.Init(&*elf->buckets.end()); + } + break; + case DT_STRTAB: + DEBUG_LOG("%s 0x%08" PRIxAddr, "DT_STRTAB", dyn->d_un.d_val); + elf->strtab.Init(elf->GetPtr(dyn->d_un.d_ptr)); + break; + case DT_SYMTAB: + DEBUG_LOG("%s 0x%08" PRIxAddr, "DT_SYMTAB", dyn->d_un.d_val); + elf->symtab.Init(elf->GetPtr(dyn->d_un.d_ptr)); + break; + } + } + if (!elf->buckets || !symnum) { + ERROR("%s: Missing or broken DT_HASH", info.dli_fname); + } else if (!elf->strtab) { + ERROR("%s: Missing DT_STRTAB", info.dli_fname); + } else if (!elf->symtab) { + ERROR("%s: Missing DT_SYMTAB", info.dli_fname); + } else { + self_elf = Move(elf); + } + } +} + ElfLoader::~ElfLoader() { LibHandleList list; @@ -507,6 +564,10 @@ ElfLoader::~ElfLoader() } } } + /* Avoid self_elf->base destructor unmapping something that doesn't actually + * belong to it. */ + if (self_elf) + self_elf->base.release(); } void diff --git a/mozglue/linker/ElfLoader.h b/mozglue/linker/ElfLoader.h index aa2f98cdbc6..c3c8adb8ee8 100644 --- a/mozglue/linker/ElfLoader.h +++ b/mozglue/linker/ElfLoader.h @@ -9,6 +9,7 @@ #include #include #include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" #include "Zip.h" #include "Elfxx.h" #include "Mappable.h" @@ -63,6 +64,9 @@ IsSignalHandlingBroken(); } +/* Forward declaration because BaseElf.h includes ElfLoader.h */ +class BaseElf; + /** * Specialize RefCounted template for LibHandle. We may get references to * LibHandles during the execution of their destructor, so we need @@ -439,6 +443,13 @@ protected: private: ~ElfLoader(); + /* Initialization code that can't run during static initialization. */ + void Init(); + + /* System loader handle for the library/program containing our code. This + * is used to resolve wrapped functions. */ + mozilla::UniquePtr self_elf; + /* Bookkeeping */ typedef std::vector LibHandleList; LibHandleList handles; @@ -557,6 +568,12 @@ private: } r_state; }; + /* Memory representation of ELF Auxiliary Vectors */ + struct AuxVector { + Elf::Addr type; + Elf::Addr value; + }; + /* Helper class used to integrate libraries loaded by this linker in * r_debug */ class DebuggerHelper @@ -564,6 +581,8 @@ private: public: DebuggerHelper(); + void Init(AuxVector *auvx); + operator bool() { return dbg; diff --git a/mozglue/linker/Utils.h b/mozglue/linker/Utils.h index b652d16f68b..881f2834376 100644 --- a/mozglue/linker/Utils.h +++ b/mozglue/linker/Utils.h @@ -290,6 +290,10 @@ public: static_cast(this)->munmap(get(), GetLength()); } + void release() + { + MemoryRange::Assign(MAP_FAILED, 0); + } }; struct MappedPtr: public GenericMappedPtr diff --git a/mozglue/linker/moz.build b/mozglue/linker/moz.build index c44bd429fd2..25d71d91079 100644 --- a/mozglue/linker/moz.build +++ b/mozglue/linker/moz.build @@ -5,6 +5,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. SOURCES += [ + 'BaseElf.cpp', 'CustomElf.cpp', 'ElfLoader.cpp', 'Mappable.cpp',