1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef CRAZY_LINKER_RDEBUG_H 6 #define CRAZY_LINKER_RDEBUG_H 7 8 #include <stdint.h> 9 10 // The system linker maintains two lists of libraries at runtime: 11 // 12 // - A list that is used by GDB and other tools to search for the 13 // binaries that are loaded in the process. 14 // 15 // This list is accessible by looking at the DT_DEBUG field of the 16 // dynamic section of any ELF binary loaded by the linker (including 17 // itself). The field contains the address of a global '_r_debug' 18 // variable. More on this later. 19 // 20 // - A list that is used internally to implement library and symbol 21 // lookup. The list head and tail are called 'solist' and 'sonext' 22 // in the linker sources, and their address is unknown (and randomized 23 // by ASLR), and there is no point trying to change it. 24 // 25 // This means that you cannot call the linker's dlsym() function to 26 // lookup symbols in libraries that are not loaded through it, i.e. 27 // any custom dynamic linker needs its own dlopen() / dlsym() and other 28 // related functions, and ensure the loaded code only uses its own version. 29 // (See support code in crazy_linker_wrappers.cpp) 30 // 31 // The global '_r_debug' variable is a r_debug structure, whose layout 32 // must be known by GDB, with the following fields: 33 // 34 // r_version -> version of the structure (must be 1) 35 // r_map -> head of a linked list of 'link_map_t' entries, 36 // one per ELF 'binary' in the process address space. 37 // r_brk -> pointer to a specific debugging function (see below). 38 // r_state -> state variable to be read in r_brk(). 39 // r_ldbase -> unused by the system linker, should be 0. (?) 40 // 41 // The 'r_brk' field points to an empty function in the system linker 42 // that is used to notify GDB of changes in the list of shared libraries, 43 // this works as follows: 44 // 45 // - When the linker wants to add a new shared library to the list, 46 // it first writes RT_ADD to 'r_state', then calls 'r_brk()'. 47 // 48 // It modifies the list, then writes RT_CONSISTENT to 'r_state' and 49 // calls 'r_brk()' again. 50 // 51 // - When unloading a library, RT_DELETE + RT_CONSISTENT are used 52 // instead. 53 // 54 // GDB will always place a breakpoint on the function pointed to by 55 // 'r_brk', and will be able to synchronize with the linker's 56 // modifications. 57 // 58 // The 'r_map' field is a list of nodes with the following structure 59 // describing each loaded shared library for GDB: 60 // 61 // l_addr -> Load address of the first PT_LOAD segment in a 62 // shared library. Note that this is 0 for the linker itself 63 // and the load-bias for an executable. 64 // l_name -> Name of the executable. This is _always_ a basename!! 65 // l_ld -> Address of the dynamic table for this binary. 66 // l_next -> Pointer to next item in 'r_map' list or NULL. 67 // l_prev -> Pointer to previous item in 'r_map' list. 68 // 69 // Note that the system linker ensures that there are always at least 70 // two items in this list: 71 // 72 // - The first item always describes the linker itself, the fields 73 // actually point to a specially crafted fake entry for it called 74 // 'libdl_info' in the linker sources. 75 // 76 // - The second item describes the executable that was started by 77 // the kernel. For Android applications, it will always be 'app_process' 78 // and completely uninteresting. 79 // 80 // - Eventually, another entry for VDSOs on platforms that support them. 81 // 82 // When implementing a custom linker, being able to debug the process 83 // unfortunately requires modifying the 'r_map' list to also account 84 // for libraries loading through it. 85 // 86 // One issue with this is that the linker also uses another internal 87 // variable, called '_r_debut_tail' that points to the last item in 88 // the list. And there is no way to access it directly. This can lead 89 // to problems when calling APIs that actually end up using the system's 90 // own dlopen(). Consider this example: 91 // 92 // 1/ Program loads crazy_linker 93 // 94 // 2/ Program uses crazy_linker to load libfoo.so, this adds 95 // a new entry at the end of the '_r_debug.r_map' list, but 96 // '_r_debug.tail' is unmodified. 97 // 98 // 3/ libfoo.so or the Java portion of the program calls a system API 99 // that ends up loading another library (e.g. libGLESv2_vendor.so), 100 // this calls the system dlopen(). 101 // 102 // 4/ The system dlopen() adds a new entry to the "_r_debug.r_map" 103 // list by updating the l_next / l_prev fields of the entry pointed 104 // to by '_r_debug_tail', and this removes 'libfoo.so' from the list! 105 // 106 // There is a simple work-around for this issue: Always insert our 107 // libraries at the _start_ of the 'r_map' list, instead of appending 108 // them to the end. The system linker doesn't know about custom-loaded 109 // libraries and thus will never try to unload them. 110 // 111 // Note that the linker never uses the 'r_map' list (except or updating 112 // it for GDB), it only uses 'solist / sonext' to actually perform its 113 // operations. That's ok if our custom linker completely wraps and 114 // re-implements these. 115 // 116 // The system linker expects to be the only item modifying the 'r_map' 117 // list, and as such it may set the pages that contain the list readonly 118 // outside of its own modifications. In threaded environments where the 119 // system linker and the crazy linker are operating simultaneously on 120 // different threads this may be a problem; we need these pages to be 121 // writable when we have to update the list. 122 // 123 // TECHNICAL NOTE: If CRAZY_DISABLE_R_BRK is defined at compile time, 124 // then the crazy linker will never try to call the r_brk() GDB Hook 125 // function. This can be useful to avoid runtime crashes on certain 126 // Android devices with x86 processors, running ARM binaries with 127 // a machine translator like Houdini. See http://crbug.com/796938 128 // 129 namespace crazy { 130 131 struct link_map_t { 132 uintptr_t l_addr; 133 char* l_name; 134 uintptr_t l_ld; 135 link_map_t* l_next; 136 link_map_t* l_prev; 137 }; 138 139 // Values for r_debug->r_state 140 enum { 141 RT_CONSISTENT, 142 RT_ADD, 143 RT_DELETE 144 }; 145 146 struct r_debug { 147 int32_t r_version; 148 link_map_t* r_map; 149 void (*r_brk)(void); 150 int32_t r_state; 151 uintptr_t r_ldbase; 152 }; 153 154 class RDebug { 155 public: 156 RDebug() = default; 157 ~RDebug() = default; 158 159 RDebug(const RDebug&) = delete; 160 RDebug& operator=(const RDebug&) = delete; 161 162 // Add entries to and remove entries from the list. 163 void AddEntry(link_map_t* entry); 164 void DelEntry(link_map_t* entry); 165 166 // Return address of current global _r_debug variable, or nullptr if not 167 // available. 168 r_debug* GetAddress(); 169 170 private: 171 // Try to find the address of the global _r_debug variable, even 172 // though there is no symbol for it. Returns true on success. 173 bool Init(); 174 175 // Call the debugger hook function |r_debug_->r_brk|. 176 // |state| is the value to write to |r_debug_->r_state| 177 // before that. This is done to coordinate with the 178 // debugger when modifications of the global |r_debug_| 179 // list are performed. 180 void CallRBrk(int state); 181 182 r_debug* r_debug_ = nullptr; 183 bool init_ = false; 184 }; 185 186 } // namespace crazy 187 188 #endif // CRAZY_LINKER_REDUG_H 189