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 |