1 // Copyright (c) 2013 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.
5 #ifndef CRAZY_LINKER_RDEBUG_H
6 #define CRAZY_LINKER_RDEBUG_H
10 // The system linker maintains two lists of libraries at runtime:
12 // - A list that is used by GDB and other tools to search for the
13 // binaries that are loaded in the process.
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.
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.
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)
31 // The global '_r_debug' variable is a r_debug structure, whose layout
32 // must be known by GDB, with the following fields:
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. (?)
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:
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()'.
48 // It modifies the list, then writes RT_CONSISTENT to 'r_state' and
49 // calls 'r_brk()' again.
51 // - When unloading a library, RT_DELETE + RT_CONSISTENT are used
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
58 // The 'r_map' field is a list of nodes with the following structure
59 // describing each loaded shared library for GDB:
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.
69 // Note that the system linker ensures that there are always at least
70 // two items in this list:
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.
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.
80 // - Eventually, another entry for VDSOs on platforms that support them.
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.
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:
92 // 1/ Program loads crazy_linker
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.
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().
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!
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.
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.
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. We cannot track the system
122 // linker's actions, so to avoid clashing with it we may need to try and
123 // move 'r_map' updates to a different thread, to serialize them with
124 // other system linker activity.
135 // Values for r_debug->r_state
150 // A callback poster is a function that can be called to request a later
151 // callback. Poster arguments are: an opaque pointer to the caller's
152 // context, a pointer to a function with a single void* argument that will
153 // handle the callback, and the opaque void* argument value to send with
155 typedef void (*crazy_callback_handler_t
)(void* opaque
);
156 typedef bool (*rdebug_callback_poster_t
)(void* context
,
157 crazy_callback_handler_t
,
160 // A callback handler is a static function, either AddEntryInternal() or
161 // DelEntryInternal(). It calls the appropriate r_map update member
162 // function, AddEntryImpl() or DelEntryImpl().
164 typedef void (*rdebug_callback_handler_t
)(RDebug
*, link_map_t
*);
168 RDebug() : r_debug_(NULL
), init_(false),
169 readonly_entries_(false), post_for_later_execution_(NULL
),
170 post_for_later_execution_context_(NULL
) {}
173 // Add entries to and remove entries from the list. If post for later
174 // execution is enabled, schedule callbacks and return. Otherwise
175 // action immediately.
176 void AddEntry(link_map_t
* entry
) { RunOrDelay(&AddEntryInternal
, entry
); }
177 void DelEntry(link_map_t
* entry
) { RunOrDelay(&DelEntryInternal
, entry
); }
179 // Assign the function used to request a callback from another thread.
180 // The context here is opaque, but is the API's crazy_context.
181 void SetDelayedCallbackPoster(rdebug_callback_poster_t poster
,
183 post_for_later_execution_
= poster
;
184 post_for_later_execution_context_
= context
;
187 r_debug
* GetAddress() { return r_debug_
; }
190 // Try to find the address of the global _r_debug variable, even
191 // though there is no symbol for it. Returns true on success.
194 // Support for scheduling list manipulation through callbacks.
195 // AddEntry() and DelEntry() pass the addresses of static functions to
196 // to RunOrDelay(). This requests a callback if later execution
197 // is enabled, otherwise it runs immediately on the current thread.
198 // AddEntryImpl() and DelEntryImpl() are the member functions called
199 // by the static ones to do the actual work.
200 void AddEntryImpl(link_map_t
* entry
);
201 void DelEntryImpl(link_map_t
* entry
);
202 static void AddEntryInternal(RDebug
* rdebug
, link_map_t
* entry
) {
203 rdebug
->AddEntryImpl(entry
);
205 static void DelEntryInternal(RDebug
* rdebug
, link_map_t
* entry
) {
206 rdebug
->DelEntryImpl(entry
);
209 // Post handler for delayed execution. Return true if delayed execution
210 // is enabled and posting succeeded.
211 bool PostCallback(rdebug_callback_handler_t handler
, link_map_t
* entry
);
213 // Run handler as a callback if enabled, otherwise immediately.
214 void RunOrDelay(rdebug_callback_handler_t handler
, link_map_t
* entry
) {
215 if (!PostCallback(handler
, entry
))
216 (*handler
)(this, entry
);
219 RDebug(const RDebug
&);
220 RDebug
& operator=(const RDebug
&);
224 bool readonly_entries_
;
225 rdebug_callback_poster_t post_for_later_execution_
;
226 void* post_for_later_execution_context_
;
231 #endif // CRAZY_LINKER_REDUG_H