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.
This commit is contained in:
Mike Hommey 2014-10-07 07:42:18 +09:00
Родитель 417a2977b3
Коммит 464da0f9c2
8 изменённых файлов: 253 добавлений и 114 удалений

Просмотреть файл

@ -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<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, 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;
}

93
mozglue/linker/BaseElf.h Normal file
Просмотреть файл

@ -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<void *>(offset) > base)
return reinterpret_cast<void *>(offset);
return base + offset;
}
/**
* Like the above, but returns a typed (const) pointer
*/
template <typename T>
const T *GetPtr(const Elf::Addr offset) const
{
if (reinterpret_cast<void *>(offset) > base)
return reinterpret_cast<const T *>(offset);
return reinterpret_cast<const T *>(base + offset);
}
/* Base address where the library is loaded */
MappedPtr base;
/* Buckets and chains for the System V symbol hash table */
Array<Elf::Word> buckets;
UnsizedArray<Elf::Word> chains;
/* protected: */
/* String table */
Elf::Strtab strtab;
/* Symbol table */
UnsizedArray<Elf::Sym> symtab;
};
#endif /* BaseElf_h */

Просмотреть файл

@ -7,7 +7,9 @@
#include <vector>
#include <dlfcn.h>
#include <signal.h>
#include <string.h>
#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<const unsigned char *>(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<const void *>(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<RefPtr<LibHandle> >::const_iterator it = dependencies.begin();
it < dependencies.end(); ++it) {
if (!(*it)->IsSystemElf()) {
sym = reinterpret_cast<CustomElf *>((*it).get())->GetSymbolPtr(symbol, hash);
#ifndef __GLIBC__
sym = static_cast<BaseElf *>(
static_cast<CustomElf *>((*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
{

Просмотреть файл

@ -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 <typename T>
const T *GetPtr(const Elf::Addr offset) const
{
return reinterpret_cast<const T *>(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> mappable;
/* Base address where the library is loaded */
MappedPtr base;
/* String table */
Elf::Strtab strtab;
/* Symbol table */
UnsizedArray<Elf::Sym> symtab;
/* Buckets and chains for the System V symbol hash table */
Array<Elf::Word> buckets;
UnsizedArray<Elf::Word> chains;
/* List of dependent libraries */
std::vector<mozilla::RefPtr<LibHandle> > dependencies;

Просмотреть файл

@ -11,6 +11,7 @@
#include <algorithm>
#include <fcntl.h>
#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<LibHandle> 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<BaseElf> elf = mozilla::MakeUnique<BaseElf>(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<Elf::Word>(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

Просмотреть файл

@ -9,6 +9,7 @@
#include <dlfcn.h>
#include <signal.h>
#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<BaseElf> self_elf;
/* Bookkeeping */
typedef std::vector<LibHandle *> 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;

Просмотреть файл

@ -290,6 +290,10 @@ public:
static_cast<T *>(this)->munmap(get(), GetLength());
}
void release()
{
MemoryRange::Assign(MAP_FAILED, 0);
}
};
struct MappedPtr: public GenericMappedPtr<MappedPtr>

Просмотреть файл

@ -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',