mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-27 04:05:32 +00:00
276 lines
7.3 KiB
C++
276 lines
7.3 KiB
C++
/* 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 */
|
|
}
|
|
}
|
|
}
|
|
}
|