mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 683127 part 5 - Initial Elf Loader, wrapping around dlopen/dladdr/dlsym/dlclose. r=tglek,r=sewardj
This commit is contained in:
parent
25af05dde1
commit
e108f8075e
275
mozglue/linker/ElfLoader.cpp
Normal file
275
mozglue/linker/ElfLoader.cpp
Normal file
@ -0,0 +1,275 @@
|
||||
/* 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 <cstring>
|
||||
#include <cstdlib>
|
||||
#include <dlfcn.h>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include "ElfLoader.h"
|
||||
#include "Logging.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
/**
|
||||
* dlfcn.h replacements functions
|
||||
*/
|
||||
|
||||
void *
|
||||
__wrap_dlopen(const char *path, int flags)
|
||||
{
|
||||
RefPtr<LibHandle> handle = ElfLoader::Singleton.Load(path, flags);
|
||||
if (handle)
|
||||
handle->AddDirectRef();
|
||||
return handle;
|
||||
}
|
||||
|
||||
const char *
|
||||
__wrap_dlerror(void)
|
||||
{
|
||||
const char *error = ElfLoader::Singleton.lastError;
|
||||
ElfLoader::Singleton.lastError = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
void *
|
||||
__wrap_dlsym(void *handle, const char *symbol)
|
||||
{
|
||||
if (!handle) {
|
||||
ElfLoader::Singleton.lastError = "dlsym(NULL, sym) unsupported";
|
||||
return NULL;
|
||||
}
|
||||
if (handle != RTLD_DEFAULT && handle != RTLD_NEXT) {
|
||||
LibHandle *h = reinterpret_cast<LibHandle *>(handle);
|
||||
return h->GetSymbolPtr(symbol);
|
||||
}
|
||||
return dlsym(handle, symbol);
|
||||
}
|
||||
|
||||
int
|
||||
__wrap_dlclose(void *handle)
|
||||
{
|
||||
if (!handle) {
|
||||
ElfLoader::Singleton.lastError = "No handle given to dlclose()";
|
||||
return -1;
|
||||
}
|
||||
reinterpret_cast<LibHandle *>(handle)->ReleaseDirectRef();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
__wrap_dladdr(void *addr, Dl_info *info)
|
||||
{
|
||||
RefPtr<LibHandle> handle = ElfLoader::Singleton.GetHandleByPtr(addr);
|
||||
if (!handle)
|
||||
return 0;
|
||||
info->dli_fname = handle->GetPath();
|
||||
return 1;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Returns the part after the last '/' for the given path
|
||||
*/
|
||||
const char *
|
||||
LeafName(const char *path)
|
||||
{
|
||||
const char *lastSlash = strrchr(path, '/');
|
||||
if (lastSlash)
|
||||
return lastSlash + 1;
|
||||
return path;
|
||||
}
|
||||
|
||||
} /* Anonymous namespace */
|
||||
|
||||
/**
|
||||
* LibHandle
|
||||
*/
|
||||
LibHandle::~LibHandle()
|
||||
{
|
||||
ElfLoader::Singleton.Forget(this);
|
||||
free(path);
|
||||
}
|
||||
|
||||
const char *
|
||||
LibHandle::GetName() const
|
||||
{
|
||||
return path ? LeafName(path) : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* SystemElf
|
||||
*/
|
||||
TemporaryRef<LibHandle>
|
||||
SystemElf::Load(const char *path, int flags)
|
||||
{
|
||||
/* The Android linker returns a handle when the file name matches an
|
||||
* already loaded library, even when the full path doesn't exist */
|
||||
if (path && path[0] == '/' && (access(path, F_OK) == -1)){
|
||||
debug("dlopen(\"%s\", %x) = %p", path, flags, (void *)NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *handle = dlopen(path, flags);
|
||||
debug("dlopen(\"%s\", %x) = %p", path, flags, handle);
|
||||
ElfLoader::Singleton.lastError = dlerror();
|
||||
if (handle)
|
||||
return new SystemElf(path, handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SystemElf::~SystemElf()
|
||||
{
|
||||
if (!dlhandle)
|
||||
return;
|
||||
debug("dlclose(%p [\"%s\"])", dlhandle, GetPath());
|
||||
dlclose(dlhandle);
|
||||
ElfLoader::Singleton.lastError = dlerror();
|
||||
}
|
||||
|
||||
void *
|
||||
SystemElf::GetSymbolPtr(const char *symbol) const
|
||||
{
|
||||
void *sym = dlsym(dlhandle, symbol);
|
||||
debug("dlsym(%p [\"%s\"], \"%s\") = %p", dlhandle, GetPath(), symbol, sym);
|
||||
ElfLoader::Singleton.lastError = dlerror();
|
||||
return sym;
|
||||
}
|
||||
|
||||
/**
|
||||
* ElfLoader
|
||||
*/
|
||||
|
||||
/* Unique ElfLoader instance */
|
||||
ElfLoader ElfLoader::Singleton;
|
||||
|
||||
TemporaryRef<LibHandle>
|
||||
ElfLoader::Load(const char *path, int flags, LibHandle *parent)
|
||||
{
|
||||
RefPtr<LibHandle> handle;
|
||||
|
||||
/* Handle dlopen(NULL) directly. */
|
||||
if (!path) {
|
||||
handle = SystemElf::Load(NULL, flags);
|
||||
handles.push_back(handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/* TODO: Handle relative paths correctly */
|
||||
const char *name = LeafName(path);
|
||||
|
||||
/* Search the list of handles we already have for a match. When the given
|
||||
* path is not absolute, compare file names, otherwise compare full paths. */
|
||||
if (name == path) {
|
||||
for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it)
|
||||
if ((*it)->GetName() && (strcmp((*it)->GetName(), name) == 0))
|
||||
return *it;
|
||||
} else {
|
||||
for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it)
|
||||
if ((*it)->GetPath() && (strcmp((*it)->GetPath(), path) == 0))
|
||||
return *it;
|
||||
}
|
||||
|
||||
char *abs_path = NULL;
|
||||
|
||||
/* When the path is not absolute and the library is being loaded for
|
||||
* another, first try to load the library from the directory containing
|
||||
* that parent library. */
|
||||
if ((name == path) && parent) {
|
||||
const char *parentPath = parent->GetPath();
|
||||
abs_path = new char[strlen(parentPath) + strlen(path)];
|
||||
strcpy(abs_path, parentPath);
|
||||
char *slash = strrchr(abs_path, '/');
|
||||
strcpy(slash + 1, path);
|
||||
path = abs_path;
|
||||
}
|
||||
|
||||
handle = SystemElf::Load(path, flags);
|
||||
|
||||
/* If we didn't have an absolute path and haven't been able to load
|
||||
* a library yet, try in the system search path */
|
||||
if (!handle && abs_path)
|
||||
handle = SystemElf::Load(name, flags);
|
||||
|
||||
delete [] abs_path;
|
||||
debug("ElfLoader::Load(\"%s\", 0x%x, %p [\"%s\"]) = %p", path, flags,
|
||||
reinterpret_cast<void *>(parent), parent ? parent->GetPath() : "",
|
||||
static_cast<void *>(handle));
|
||||
|
||||
/* Bookkeeping */
|
||||
if (handle)
|
||||
handles.push_back(handle);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
mozilla::TemporaryRef<LibHandle>
|
||||
ElfLoader::GetHandleByPtr(void *addr)
|
||||
{
|
||||
/* Scan the list of handles we already have for a match */
|
||||
for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it) {
|
||||
if ((*it)->Contains(addr))
|
||||
return *it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
ElfLoader::Forget(LibHandle *handle)
|
||||
{
|
||||
LibHandleList::iterator it = std::find(handles.begin(), handles.end(), handle);
|
||||
if (it != handles.end()) {
|
||||
debug("ElfLoader::Forget(%p [\"%s\"])", reinterpret_cast<void *>(handle),
|
||||
handle->GetPath());
|
||||
handles.erase(it);
|
||||
} else {
|
||||
debug("ElfLoader::Forget(%p [\"%s\"]): Handle not found",
|
||||
reinterpret_cast<void *>(handle), handle->GetPath());
|
||||
}
|
||||
}
|
||||
|
||||
ElfLoader::~ElfLoader()
|
||||
{
|
||||
LibHandleList list;
|
||||
/* Build up a list of all library handles with direct (external) references.
|
||||
* We actually skip system library handles because we want to keep at least
|
||||
* some of these open. Most notably, Mozilla codebase keeps a few libgnome
|
||||
* libraries deliberately open because of the mess that libORBit destruction
|
||||
* is. dlclose()ing these libraries actually leads to problems. */
|
||||
for (LibHandleList::reverse_iterator it = handles.rbegin();
|
||||
it < handles.rend(); ++it) {
|
||||
if ((*it)->DirectRefCount()) {
|
||||
if ((*it)->IsSystemElf()) {
|
||||
static_cast<SystemElf *>(*it)->Forget();
|
||||
} else {
|
||||
list.push_back(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Force release all external references to the handles collected above */
|
||||
for (LibHandleList::iterator it = list.begin(); it < list.end(); ++it) {
|
||||
while ((*it)->ReleaseDirectRef()) { }
|
||||
}
|
||||
/* Remove the remaining system handles. */
|
||||
if (handles.size()) {
|
||||
list = handles;
|
||||
for (LibHandleList::reverse_iterator it = list.rbegin();
|
||||
it < list.rend(); ++it) {
|
||||
if ((*it)->IsSystemElf()) {
|
||||
debug("ElfLoader::~ElfLoader(): Remaining handle for \"%s\" "
|
||||
"[%d direct refs, %d refs total]", (*it)->GetPath(),
|
||||
(*it)->DirectRefCount(), (*it)->refCount());
|
||||
delete (*it);
|
||||
} else {
|
||||
debug("ElfLoader::~ElfLoader(): Unexpected remaining handle for \"%s\" "
|
||||
"[%d direct refs, %d refs total]", (*it)->GetPath(),
|
||||
(*it)->DirectRefCount(), (*it)->refCount());
|
||||
/* Not removing, since it could have references to other libraries,
|
||||
* destroying them as a side effect, and possibly leaving dangling
|
||||
* pointers in the handle list we're scanning */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
227
mozglue/linker/ElfLoader.h
Normal file
227
mozglue/linker/ElfLoader.h
Normal file
@ -0,0 +1,227 @@
|
||||
/* 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 ElfLoader_h
|
||||
#define ElfLoader_h
|
||||
|
||||
#include <vector>
|
||||
/* Until RefPtr.h stops using JS_Assert */
|
||||
#undef DEBUG
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
/**
|
||||
* dlfcn.h replacement functions
|
||||
*/
|
||||
extern "C" {
|
||||
void *__wrap_dlopen(const char *path, int flags);
|
||||
const char *__wrap_dlerror(void);
|
||||
void *__wrap_dlsym(void *handle, const char *symbol);
|
||||
int __wrap_dlclose(void *handle);
|
||||
|
||||
#ifndef HAVE_DLADDR
|
||||
typedef struct {
|
||||
const char *dli_fname;
|
||||
void *dli_fbase;
|
||||
const char *dli_sname;
|
||||
void *dli_saddr;
|
||||
} Dl_info;
|
||||
#endif
|
||||
int __wrap_dladdr(void *addr, Dl_info *info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract class for loaded libraries. Libraries may be loaded through the
|
||||
* system linker or this linker, both cases will be derived from this class.
|
||||
*/
|
||||
class LibHandle: public mozilla::RefCounted<LibHandle>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor. Takes the path of the loaded library and will store a copy
|
||||
* of the leaf name.
|
||||
*/
|
||||
LibHandle(const char *path)
|
||||
: directRefCnt(0), path(path ? strdup(path) : NULL) { }
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~LibHandle();
|
||||
|
||||
/**
|
||||
* Returns the pointer to the address to which the given symbol resolves
|
||||
* inside the library. It is not supposed to resolve the symbol in other
|
||||
* libraries, although in practice, it will for system libraries.
|
||||
*/
|
||||
virtual void *GetSymbolPtr(const char *symbol) const = 0;
|
||||
|
||||
/**
|
||||
* Returns whether the given address is part of the virtual address space
|
||||
* covered by the loaded library.
|
||||
*/
|
||||
virtual bool Contains(void *addr) const = 0;
|
||||
|
||||
/**
|
||||
* Returns the file name of the library without the containing directory.
|
||||
*/
|
||||
const char *GetName() const;
|
||||
|
||||
/**
|
||||
* Returns the full path of the library, when available. Otherwise, returns
|
||||
* the file name.
|
||||
*/
|
||||
const char *GetPath() const
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Library handles can be referenced from other library handles or
|
||||
* externally (when dlopen()ing using this linker). We need to be
|
||||
* able to distinguish between the two kind of referencing for better
|
||||
* bookkeeping.
|
||||
*/
|
||||
void AddDirectRef()
|
||||
{
|
||||
++directRefCnt;
|
||||
mozilla::RefCounted<LibHandle>::AddRef();
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases a direct reference, and returns whether there are any direct
|
||||
* references left.
|
||||
*/
|
||||
bool ReleaseDirectRef()
|
||||
{
|
||||
bool ret = false;
|
||||
if (directRefCnt) {
|
||||
// ASSERT(directRefCnt >= mozilla::RefCounted<LibHandle>::refCount())
|
||||
if (--directRefCnt)
|
||||
ret = true;
|
||||
mozilla::RefCounted<LibHandle>::Release();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of direct references
|
||||
*/
|
||||
int DirectRefCount()
|
||||
{
|
||||
return directRefCnt;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Returns whether the handle is a SystemElf or not. (short of a better way
|
||||
* to do this without RTTI)
|
||||
*/
|
||||
friend class ElfLoader;
|
||||
virtual bool IsSystemElf() const { return false; }
|
||||
|
||||
private:
|
||||
int directRefCnt;
|
||||
char *path;
|
||||
};
|
||||
|
||||
/**
|
||||
* Class handling libraries loaded by the system linker
|
||||
*/
|
||||
class SystemElf: public LibHandle
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Returns a new SystemElf for the given path. The given flags are passed
|
||||
* to dlopen().
|
||||
*/
|
||||
static mozilla::TemporaryRef<LibHandle> Load(const char *path, int flags);
|
||||
|
||||
/**
|
||||
* Inherited from LibHandle
|
||||
*/
|
||||
virtual ~SystemElf();
|
||||
virtual void *GetSymbolPtr(const char *symbol) const;
|
||||
virtual bool Contains(void *addr) const { return false; /* UNIMPLEMENTED */ }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Returns whether the handle is a SystemElf or not. (short of a better way
|
||||
* to do this without RTTI)
|
||||
*/
|
||||
friend class ElfLoader;
|
||||
virtual bool IsSystemElf() const { return true; }
|
||||
|
||||
/**
|
||||
* Remove the reference to the system linker handle. This avoids dlclose()
|
||||
* being called when the instance is destroyed.
|
||||
*/
|
||||
void Forget()
|
||||
{
|
||||
dlhandle = NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Private constructor
|
||||
*/
|
||||
SystemElf(const char *path, void *handle)
|
||||
: LibHandle(path), dlhandle(handle) { }
|
||||
|
||||
/* Handle as returned by system dlopen() */
|
||||
void *dlhandle;
|
||||
};
|
||||
|
||||
/**
|
||||
* Elf Loader class in charge of loading and bookkeeping libraries.
|
||||
*/
|
||||
class ElfLoader
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* The Elf Loader instance
|
||||
*/
|
||||
static ElfLoader Singleton;
|
||||
|
||||
/**
|
||||
* Loads the given library with the given flags. Equivalent to dlopen()
|
||||
* The extra "parent" argument optionally gives the handle of the library
|
||||
* requesting the given library to be loaded. The loader may look in the
|
||||
* directory containing that parent library for the library to load.
|
||||
*/
|
||||
mozilla::TemporaryRef<LibHandle> Load(const char *path, int flags,
|
||||
LibHandle *parent = NULL);
|
||||
|
||||
/**
|
||||
* Returns the handle of the library containing the given address in
|
||||
* its virtual address space, i.e. the library handle for which
|
||||
* LibHandle::Contains returns true. Its purpose is to allow to
|
||||
* implement dladdr().
|
||||
*/
|
||||
mozilla::TemporaryRef<LibHandle> GetHandleByPtr(void *addr);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Forget about the given handle. This method is meant to be called by
|
||||
* the LibHandle destructor.
|
||||
*/
|
||||
friend LibHandle::~LibHandle();
|
||||
void Forget(LibHandle *handle);
|
||||
|
||||
/* Last error. Used for dlerror() */
|
||||
friend class SystemElf;
|
||||
friend const char *__wrap_dlerror(void);
|
||||
friend void *__wrap_dlsym(void *handle, const char *symbol);
|
||||
friend int __wrap_dlclose(void *handle);
|
||||
const char *lastError;
|
||||
|
||||
private:
|
||||
ElfLoader() { }
|
||||
~ElfLoader();
|
||||
|
||||
/* Bookkeeping */
|
||||
typedef std::vector<LibHandle *> LibHandleList;
|
||||
LibHandleList handles;
|
||||
};
|
||||
|
||||
#endif /* ElfLoader_h */
|
@ -18,4 +18,10 @@ CPPSRCS = \
|
||||
Zip.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifndef MOZ_OLD_LINKER
|
||||
CPPSRCS += \
|
||||
ElfLoader.cpp \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
Loading…
Reference in New Issue
Block a user