mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
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:
parent
66e8daff0b
commit
9e7e4fb6e1
55
mozglue/linker/BaseElf.cpp
Normal file
55
mozglue/linker/BaseElf.cpp
Normal file
@ -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
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',
|
||||
|
Loading…
Reference in New Issue
Block a user