gecko-dev/mozglue/linker/ElfLoader.cpp

568 lines
17 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 <fcntl.h>
#include "ElfLoader.h"
#include "CustomElf.h"
#include "Mappable.h"
#include "Logging.h"
using namespace mozilla;
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
#ifndef PAGE_MASK
#define PAGE_MASK (~ (PAGE_SIZE - 1))
#endif
/**
* 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()
{
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) {
SystemElf *elf = new SystemElf(path, handle);
ElfLoader::Singleton.Register(elf);
return elf;
}
return NULL;
}
SystemElf::~SystemElf()
{
if (!dlhandle)
return;
debug("dlclose(%p [\"%s\"])", dlhandle, GetPath());
dlclose(dlhandle);
ElfLoader::Singleton.lastError = dlerror();
ElfLoader::Singleton.Forget(this);
}
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);
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;
const char *requested_path = path;
/* 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;
}
/* Create a mappable object for the given path. Paths in the form
* /foo/bar/baz/archive!/directory/lib.so
* try to load the directory/lib.so in /foo/bar/baz/archive, provided
* that file is a Zip archive. */
Mappable *mappable = NULL;
RefPtr<Zip> zip;
const char *subpath;
if ((subpath = strchr(path, '!'))) {
char *zip_path = strndup(path, subpath - path);
while (*(++subpath) == '/') { }
zip = zips.GetZip(zip_path);
Zip::Stream s;
if (zip && zip->GetStream(subpath, &s)) {
if (s.GetType() == Zip::Stream::DEFLATE) {
/* When the MOZ_LINKER_EXTRACT environment variable is set to "1",
* compressed libraries are going to be (temporarily) extracted as
* files, in the directory pointed by the MOZ_LINKER_CACHE
* environment variable. */
const char *extract = getenv("MOZ_LINKER_EXTRACT");
if (extract && !strncmp(extract, "1", 2 /* Including '\0' */))
mappable = MappableExtractFile::Create(name, &s);
/* The above may fail in some cases. */
if (!mappable)
mappable = MappableDeflate::Create(name, zip, &s);
}
}
}
/* If we couldn't load above, try with a MappableFile */
if (!mappable && !zip)
mappable = MappableFile::Create(path);
/* Try loading with the custom linker if we have a Mappable */
if (mappable)
handle = CustomElf::Load(mappable, path, flags);
/* Try loading with the system linker if everything above failed */
if (!handle)
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", requested_path, flags,
reinterpret_cast<void *>(parent), parent ? parent->GetPath() : "",
static_cast<void *>(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::Register(LibHandle *handle)
{
handles.push_back(handle);
if (dbg && !handle->IsSystemElf())
dbg->Add(static_cast<CustomElf *>(handle));
}
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());
if (dbg && !handle->IsSystemElf())
dbg->Remove(static_cast<CustomElf *>(handle));
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());
} 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 */
}
}
}
}
#ifdef __ARM_EABI__
int
ElfLoader::__wrap_aeabi_atexit(void *that, ElfLoader::Destructor destructor,
void *dso_handle)
{
Singleton.destructors.push_back(
DestructorCaller(destructor, that, dso_handle));
return 0;
}
#else
int
ElfLoader::__wrap_cxa_atexit(ElfLoader::Destructor destructor, void *that,
void *dso_handle)
{
Singleton.destructors.push_back(
DestructorCaller(destructor, that, dso_handle));
return 0;
}
#endif
void
ElfLoader::__wrap_cxa_finalize(void *dso_handle)
{
/* Call all destructors for the given DSO handle in reverse order they were
* registered. */
std::vector<DestructorCaller>::reverse_iterator it;
for (it = Singleton.destructors.rbegin();
it < Singleton.destructors.rend(); ++it) {
if (it->IsForHandle(dso_handle)) {
it->Call();
}
}
}
void
ElfLoader::DestructorCaller::Call()
{
if (destructor) {
debug("ElfLoader::DestructorCaller::Call(%p, %p, %p)",
FunctionPtr(destructor), object, dso_handle);
destructor(object);
destructor = NULL;
}
}
void
ElfLoader::InitDebugger()
{
/* Find ELF auxiliary vectors.
*
* The kernel stores the following data on the stack when starting a
* program:
* argc
* argv[0] (pointer into argv strings defined below)
* argv[1] (likewise)
* ...
* argv[argc - 1] (likewise)
* NULL
* envp[0] (pointer into environment strings defined below)
* envp[1] (likewise)
* ...
* envp[n] (likewise)
* NULL
* auxv[0] (first ELF auxiliary vector)
* auxv[1] (second ELF auxiliary vector)
* ...
* auxv[p] (last ELF auxiliary vector)
* (AT_NULL, NULL)
* padding
* argv strings, separated with '\0'
* environment strings, separated with '\0'
* NULL
*
* What we are after are the auxv values defined by the following struct.
*/
struct AuxVector {
Elf::Addr type;
Elf::Addr value;
};
/* Pointer to the environment variables list */
extern char **environ;
/* The environment may have changed since the program started, in which
* case the environ variables list isn't the list the kernel put on stack
* anymore. But in this new list, variables that didn't change still point
* to the strings the kernel put on stack. It is quite unlikely that two
* modified environment variables point to two consecutive strings in memory,
* so we assume that if two consecutive environment variables point to two
* consecutive strings, we found strings the kernel put on stack. */
char **env;
for (env = environ; *env; env++)
if (*env + strlen(*env) + 1 == env[1])
break;
if (!*env)
return;
/* Next, we scan the stack backwards to find a pointer to one of those
* strings we found above, which will give us the location of the original
* envp list. As we are looking for pointers, we need to look at 32-bits or
* 64-bits aligned values, depening on the architecture. */
char **scan = reinterpret_cast<char **>(
reinterpret_cast<uintptr_t>(*env) & ~(sizeof(void *) - 1));
while (*env != *scan)
scan--;
/* Finally, scan forward to find the last environment variable pointer and
* thus the first auxiliary vector. */
while (*scan++);
AuxVector *auxv = reinterpret_cast<AuxVector *>(scan);
/* The two values of interest in the auxiliary vectors are AT_PHDR and
* AT_PHNUM, which gives us the the location and size of the ELF program
* headers. */
Array<Elf::Phdr> phdrs;
char *base = NULL;
while (auxv->type) {
if (auxv->type == AT_PHDR) {
phdrs.Init(reinterpret_cast<Elf::Phdr*>(auxv->value));
/* Assume the base address is the first byte of the same page */
base = reinterpret_cast<char *>(auxv->value & PAGE_MASK);
}
if (auxv->type == AT_PHNUM)
phdrs.Init(auxv->value);
auxv++;
}
if (!phdrs) {
debug("Couldn't find program headers");
return;
}
/* In some cases, the address for the program headers we get from the
* auxiliary vectors is not mapped, because of the PT_LOAD segments
* definitions in the program executable. Trying to map anonymous memory
* with a hint giving the base address will return a different address
* if something is mapped there, and the base address otherwise. */
MappedPtr mem(mmap(base, PAGE_SIZE, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), PAGE_SIZE);
if (mem == base) {
/* If program headers aren't mapped, try to map them */
int fd = open("/proc/self/exe", O_RDONLY);
if (fd == -1) {
debug("Failed to open /proc/self/exe");
return;
}
mem.Assign(mmap(base, PAGE_SIZE, PROT_READ, MAP_PRIVATE, fd, 0), PAGE_SIZE);
/* If we don't manage to map at the right address, just give up. */
if (mem != base) {
debug("Couldn't read program headers");
return;
}
}
/* Sanity check: the first bytes at the base address should be an ELF
* header. */
if (!Elf::Ehdr::validate(base)) {
debug("Couldn't find program base");
return;
}
/* Search for the program PT_DYNAMIC segment */
Array<Elf::Dyn> dyns;
for (Array<Elf::Phdr>::iterator phdr = phdrs.begin(); phdr < phdrs.end();
++phdr) {
/* While the program headers are expected within the first mapped page of
* the program executable, the executable PT_LOADs may actually make them
* loaded at an address that is not the wanted base address of the
* library. We thus need to adjust the base address, compensating for the
* virtual address of the PT_LOAD segment corresponding to offset 0. */
if (phdr->p_type == PT_LOAD && phdr->p_offset == 0)
base -= phdr->p_vaddr;
if (phdr->p_type == PT_DYNAMIC)
dyns.Init(base + phdr->p_vaddr, phdr->p_filesz);
}
if (!dyns) {
debug("Failed to find PT_DYNAMIC section in program");
return;
}
/* Search for the DT_DEBUG information */
for (Array<Elf::Dyn>::iterator dyn = dyns.begin(); dyn < dyns.end(); ++dyn) {
if (dyn->d_tag == DT_DEBUG) {
dbg = reinterpret_cast<r_debug *>(dyn->d_un.d_ptr);
break;
}
}
debug("DT_DEBUG points at %p", dbg);
}
/**
* The system linker maintains a doubly linked list of library it loads
* for use by the debugger. Unfortunately, it also uses the list pointers
* in a lot of operations and adding our data in the list is likely to
* trigger crashes when the linker tries to use data we don't provide or
* that fall off the amount data we allocated. Fortunately, the linker only
* traverses the list forward and accesses the head of the list from a
* private pointer instead of using the value in the r_debug structure.
* This means we can safely add members at the beginning of the list.
* Unfortunately, gdb checks the coherency of l_prev values, so we have
* to adjust the l_prev value for the first element the system linker
* knows about. Fortunately, it doesn't use l_prev, and the first element
* is not ever going to be released before our elements, since it is the
* program executable, so the system linker should not be changing
* r_debug::r_map.
*/
void
ElfLoader::r_debug::Add(ElfLoader::link_map *map)
{
if (!r_brk)
return;
r_state = RT_ADD;
r_brk();
map->l_prev = NULL;
map->l_next = r_map;
r_map->l_prev = map;
r_map = map;
r_state = RT_CONSISTENT;
r_brk();
}
void
ElfLoader::r_debug::Remove(ElfLoader::link_map *map)
{
if (!r_brk)
return;
r_state = RT_DELETE;
r_brk();
if (r_map == map)
r_map = map->l_next;
else
map->l_prev->l_next = map->l_next;
map->l_next->l_prev = map->l_prev;
r_state = RT_CONSISTENT;
r_brk();
}