| Index: third_party/crazy_linker/crazy_linker/src/crazy_linker_rdebug.h
|
| diff --git a/third_party/crazy_linker/crazy_linker/src/crazy_linker_rdebug.h b/third_party/crazy_linker/crazy_linker/src/crazy_linker_rdebug.h
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..91fd93255328d12685b2077118340f191f9b9144
|
| --- /dev/null
|
| +++ b/third_party/crazy_linker/crazy_linker/src/crazy_linker_rdebug.h
|
| @@ -0,0 +1,168 @@
|
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#ifndef CRAZY_LINKER_RDEBUG_H
|
| +#define CRAZY_LINKER_RDEBUG_H
|
| +
|
| +#include <stdint.h>
|
| +
|
| +// The system linker maintains two lists of libraries at runtime:
|
| +//
|
| +// - A list that is used by GDB and other tools to search for the
|
| +// binaries that are loaded in the process.
|
| +//
|
| +// This list is accessible by looking at the DT_DEBUG field of the
|
| +// dynamic section of any ELF binary loaded by the linker (including
|
| +// itself). The field contains the address of a global '_r_debug'
|
| +// variable. More on this later.
|
| +//
|
| +// - A list that is used internally to implement library and symbol
|
| +// lookup. The list head and tail are called 'solist' and 'sonext'
|
| +// in the linker sources, and their address is unknown (and randomized
|
| +// by ASLR), so there is no point trying to change it.
|
| +//
|
| +// This also means that you cannot call the linker's dlsym() function to
|
| +// lookup symbols in libraries that are not loaded through it, i.e. in
|
| +// other words, any custom dynamic linker needs its own dlopen() / dlsym()
|
| +// and other related functions, and ensure the loaded code only uses its
|
| +// own version.
|
| +//
|
| +// The global '_r_debug' variable is a r_debug structure, which layout
|
| +// must be known by GDB, with the following fields:
|
| +//
|
| +// r_version -> version of the structure (must be 1)
|
| +// r_map -> head of a linked list of 'link_map_t' entries,
|
| +// one per ELF 'binary' in the process address space.
|
| +// r_brk -> pointer to a specific debugging function (see below).
|
| +// r_state -> state variable to be read in *r_brk
|
| +// r_ldbase -> unused by the system linker, should be 0. (?)
|
| +//
|
| +// The 'r_brk' field points to an empty function in the system linker
|
| +// that is used to notify GDB of changes in the list of shared libraries,
|
| +// this works as follows:
|
| +//
|
| +// - When the linker wants to add a new shared library to the list,
|
| +// it first writes RT_ADD to 'r_state', then calls 'r_brk()'.
|
| +//
|
| +// It modifies the list, then writes RT_CONSISTENT to 'r_state' and
|
| +// calls 'r_brk()' again.
|
| +//
|
| +// - When unloading a library, RT_DELETE + RT_CONSISTENT are used
|
| +// instead.
|
| +//
|
| +// GDB will always place a breakpoint on the function pointed to by
|
| +// 'r_brk', and will be able to synchronize with the linker's
|
| +// modifications.
|
| +//
|
| +// The 'r_map' field is a list of nodes with the following structure
|
| +// describing each loaded shared library for GDB:
|
| +//
|
| +// l_addr -> load address of the first PT_LOAD segment in a
|
| +// shared library. Note that this is 0 for the linker itself
|
| +// and the load-bias for an executable.
|
| +// l_name -> name of the executable. This is _always_ a basename!!
|
| +// l_ld -> address of the dynamic table for this binary.
|
| +// l_next -> pointer to next item in 'r_map' list or NULL.
|
| +// l_prev -> pointer to previous item in 'r_map' list.
|
| +//
|
| +// Note that the system linker ensures that there are always at least
|
| +// two items in this list:
|
| +//
|
| +// - The first item always describes the linker itself, the fields
|
| +// actually point to a specially crafted fake entry for it called
|
| +// 'libdl_info' in the linker sources.
|
| +//
|
| +// - The second item describes the executable that was started by
|
| +// the kernel. For Android applications, it will always be 'app_process'
|
| +// and completely uninteresting.
|
| +//
|
| +// - Eventually, another entry for VDSOs on platforms that support them.
|
| +//
|
| +// When implementing a custom linker, being able to debug the process
|
| +// unfortunately requires modifying the 'r_map' list to also account
|
| +// for libraries loading through it.
|
| +//
|
| +// One issue with this is that the linker also uses another internal
|
| +// variable, called '_r_debut_tail' that points to the last item in
|
| +// the list. And there is no way to access it directly. This can lead
|
| +// to problems when calling APIs that actually end up using the system's
|
| +// own dlopen(). Consider this example:
|
| +//
|
| +// 1/ Program loads crazy_linker
|
| +//
|
| +// 2/ Program uses crazy_linker to load libfoo.so, this adds
|
| +// a new entry at the end of the '_r_debug.r_map' list, but
|
| +// '_r_debug.tail' is unmodified.
|
| +//
|
| +// 3/ libfoo.so or the Java portion of the program calls a system API
|
| +// that ends up loading another library (e.g. libGLESv2_vendor.so),
|
| +// this calls the system dlopen().
|
| +//
|
| +// 4/ The system dlopen() adds a new entry to the "_r_debug.r_map"
|
| +// list by updating the l_next / l_prev fields of the entry pointed
|
| +// to by '_r_debug_tail', and this removes 'libfoo.so' from the list!
|
| +//
|
| +// There is a simple work-around for this issue: Always insert our
|
| +// libraries at the _start_ of the 'r_map' list, instead of appending
|
| +// them to the end. The system linker doesn't know about custom-loaded
|
| +// libraries and thus will never try to unload them.
|
| +//
|
| +// Note that the linker never uses the 'r_map' list (except or updating
|
| +// it for GDB), it only uses 'solist / sonext' to actually perform its
|
| +// operations. That's ok if our custom linker completely wraps and
|
| +// re-implements these.
|
| +namespace crazy {
|
| +
|
| +struct link_map_t {
|
| + uintptr_t l_addr;
|
| + char* l_name;
|
| + uintptr_t l_ld;
|
| + link_map_t* l_next;
|
| + link_map_t* l_prev;
|
| +};
|
| +
|
| +// Values for r_debug->r_state
|
| +enum {
|
| + RT_CONSISTENT,
|
| + RT_ADD,
|
| + RT_DELETE
|
| +};
|
| +
|
| +struct r_debug {
|
| + int32_t r_version;
|
| + link_map_t* r_map;
|
| + void (*r_brk)(void);
|
| + int32_t r_state;
|
| + uintptr_t r_ldbase;
|
| +};
|
| +
|
| +class RDebug {
|
| + public:
|
| + RDebug() : r_debug_(NULL), init_(false), readonly_entries_(false) {}
|
| + ~RDebug() {}
|
| +
|
| + // Add a new entry at the head of the list.
|
| + void AddEntry(link_map_t* entry);
|
| +
|
| + // Remove an entry from the list.
|
| + void DelEntry(link_map_t* entry);
|
| +
|
| + r_debug* GetAddress() { return r_debug_; }
|
| +
|
| + private:
|
| + // Try to find the address of the global _r_debug variable, even
|
| + // though there is no symbol for it. Returns true on success.
|
| + bool Init();
|
| +
|
| + RDebug(const RDebug&);
|
| + RDebug& operator=(const RDebug&);
|
| +
|
| + r_debug* r_debug_;
|
| + bool init_;
|
| + bool readonly_entries_;
|
| +};
|
| +
|
| +} // namespace crazy
|
| +
|
| +#endif // CRAZY_LINKER_REDUG_H
|
|
|