mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 683127 part 10 - Allow debug symbols to be found under gdb without extracted libraries. r=tglek,r=mwu
This commit is contained in:
parent
da0affaf1e
commit
aef323bb1b
@ -176,7 +176,7 @@ CustomElf::Load(Mappable *mappable, const char *path, int flags)
|
||||
|
||||
/* Reserve enough memory to map the complete virtual address space for this
|
||||
* library. */
|
||||
elf->base.Init(mmap(NULL, max_vaddr, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
elf->base.Assign(mmap(NULL, max_vaddr, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
-1, 0), max_vaddr);
|
||||
if (elf->base == MAP_FAILED) {
|
||||
log("%s: Failed to mmap", elf->GetPath());
|
||||
@ -195,6 +195,11 @@ CustomElf::Load(Mappable *mappable, const char *path, int flags)
|
||||
report_mapping(const_cast<char *>(elf->GetName()), elf->base,
|
||||
(max_vaddr + PAGE_SIZE - 1) & PAGE_MASK, 0);
|
||||
|
||||
elf->l_addr = elf->base;
|
||||
elf->l_name = elf->GetPath();
|
||||
elf->l_ld = elf->GetPtr<Dyn>(dyn->p_vaddr);
|
||||
ElfLoader::Singleton.Register(elf);
|
||||
|
||||
if (!elf->InitDyn(dyn))
|
||||
return NULL;
|
||||
|
||||
@ -213,6 +218,7 @@ CustomElf::~CustomElf()
|
||||
* calls destructors once, so call it in all cases. */
|
||||
ElfLoader::__wrap_cxa_finalize(this);
|
||||
delete mappable;
|
||||
ElfLoader::Singleton.Forget(this);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -208,8 +208,9 @@ class Mappable;
|
||||
* Library Handle class for ELF libraries we don't let the system linker
|
||||
* handle.
|
||||
*/
|
||||
class CustomElf: public LibHandle
|
||||
class CustomElf: public LibHandle, private ElfLoader::link_map
|
||||
{
|
||||
friend class ElfLoader;
|
||||
public:
|
||||
/**
|
||||
* Returns a new CustomElf using the given file descriptor to map ELF
|
||||
|
@ -15,6 +15,14 @@
|
||||
|
||||
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
|
||||
*/
|
||||
@ -92,7 +100,6 @@ LeafName(const char *path)
|
||||
*/
|
||||
LibHandle::~LibHandle()
|
||||
{
|
||||
ElfLoader::Singleton.Forget(this);
|
||||
free(path);
|
||||
}
|
||||
|
||||
@ -118,8 +125,11 @@ SystemElf::Load(const char *path, int flags)
|
||||
void *handle = dlopen(path, flags);
|
||||
debug("dlopen(\"%s\", %x) = %p", path, flags, handle);
|
||||
ElfLoader::Singleton.lastError = dlerror();
|
||||
if (handle)
|
||||
return new SystemElf(path, handle);
|
||||
if (handle) {
|
||||
SystemElf *elf = new SystemElf(path, handle);
|
||||
ElfLoader::Singleton.Register(elf);
|
||||
return elf;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -130,6 +140,7 @@ SystemElf::~SystemElf()
|
||||
debug("dlclose(%p [\"%s\"])", dlhandle, GetPath());
|
||||
dlclose(dlhandle);
|
||||
ElfLoader::Singleton.lastError = dlerror();
|
||||
ElfLoader::Singleton.Forget(this);
|
||||
}
|
||||
|
||||
void *
|
||||
@ -156,7 +167,6 @@ ElfLoader::Load(const char *path, int flags, LibHandle *parent)
|
||||
/* Handle dlopen(NULL) directly. */
|
||||
if (!path) {
|
||||
handle = SystemElf::Load(NULL, flags);
|
||||
handles.push_back(handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
@ -239,10 +249,6 @@ ElfLoader::Load(const char *path, int flags, LibHandle *parent)
|
||||
reinterpret_cast<void *>(parent), parent ? parent->GetPath() : "",
|
||||
static_cast<void *>(handle));
|
||||
|
||||
/* Bookkeeping */
|
||||
if (handle)
|
||||
handles.push_back(handle);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
@ -257,6 +263,14 @@ ElfLoader::GetHandleByPtr(void *addr)
|
||||
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)
|
||||
{
|
||||
@ -264,6 +278,8 @@ ElfLoader::Forget(LibHandle *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",
|
||||
@ -359,3 +375,194 @@ ElfLoader::DestructorCaller::Call()
|
||||
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();
|
||||
}
|
||||
|
@ -205,10 +205,15 @@ public:
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Forget about the given handle. This method is meant to be called by
|
||||
* the LibHandle destructor.
|
||||
* Registers the given handle. This method is meant to be called by
|
||||
* LibHandle subclass creators.
|
||||
*/
|
||||
void Register(LibHandle *handle);
|
||||
|
||||
/**
|
||||
* Forget about the given handle. This method is meant to be called by
|
||||
* LibHandle subclass destructors.
|
||||
*/
|
||||
friend LibHandle::~LibHandle();
|
||||
void Forget(LibHandle *handle);
|
||||
|
||||
/* Last error. Used for dlerror() */
|
||||
@ -219,7 +224,7 @@ protected:
|
||||
const char *lastError;
|
||||
|
||||
private:
|
||||
ElfLoader() { }
|
||||
ElfLoader() { InitDebugger(); }
|
||||
~ElfLoader();
|
||||
|
||||
/* Bookkeeping */
|
||||
@ -292,6 +297,58 @@ private:
|
||||
|
||||
/* Keep track of Zips used for library loading */
|
||||
ZipCollection zips;
|
||||
|
||||
public:
|
||||
/* Loaded object descriptor for the debugger interface below*/
|
||||
struct link_map {
|
||||
/* Base address of the loaded object. */
|
||||
const void *l_addr;
|
||||
/* File name */
|
||||
const char *l_name;
|
||||
/* Address of the PT_DYNAMIC segment. */
|
||||
const void *l_ld;
|
||||
/* Double linked list of loaded objects. */
|
||||
link_map *l_next, *l_prev;
|
||||
};
|
||||
|
||||
private:
|
||||
/* Data structure used by the linker to give details about shared objects it
|
||||
* loaded to debuggers. This is normally defined in link.h, but Android
|
||||
* headers lack this file. This also gives the opportunity to make it C++. */
|
||||
class r_debug {
|
||||
public:
|
||||
/* Make the debugger aware of a new loaded object */
|
||||
void Add(link_map *map);
|
||||
|
||||
/* Make the debugger aware of the unloading of an object */
|
||||
void Remove(link_map *map);
|
||||
|
||||
private:
|
||||
/* Version number of the protocol. */
|
||||
int r_version;
|
||||
|
||||
/* Head of the linked list of loaded objects. */
|
||||
struct link_map *r_map;
|
||||
|
||||
/* Function to be called when updates to the linked list of loaded objects
|
||||
* are going to occur. The function is to be called before and after
|
||||
* changes. */
|
||||
void (*r_brk)(void);
|
||||
|
||||
/* Indicates to the debugger what state the linked list of loaded objects
|
||||
* is in when the function above is called. */
|
||||
enum {
|
||||
RT_CONSISTENT, /* Changes are complete */
|
||||
RT_ADD, /* Beginning to add a new object */
|
||||
RT_DELETE /* Beginning to remove an object */
|
||||
} r_state;
|
||||
};
|
||||
r_debug *dbg;
|
||||
|
||||
/**
|
||||
* Initializes the pointer to the debugger data structure.
|
||||
*/
|
||||
void InitDebugger();
|
||||
};
|
||||
|
||||
#endif /* ElfLoader_h */
|
||||
|
@ -183,7 +183,9 @@ public:
|
||||
GenericMappedPtr(void *buf, size_t length): buf(buf), length(length) { }
|
||||
GenericMappedPtr(): buf(MAP_FAILED), length(0) { }
|
||||
|
||||
void Init(void *b, size_t len) {
|
||||
void Assign(void *b, size_t len) {
|
||||
if (buf != MAP_FAILED)
|
||||
static_cast<T *>(this)->munmap(buf, length);
|
||||
buf = b;
|
||||
length = len;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user