Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1513)

Unified Diff: third_party/android_crazy_linker/src/src/crazy_linker_rdebug.h

Issue 322433006: Fork of the Android NDK crazy linker. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add a required license header to a cpp module, missing in the original. Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/android_crazy_linker/src/src/crazy_linker_rdebug.h
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_rdebug.h b/third_party/android_crazy_linker/src/src/crazy_linker_rdebug.h
new file mode 100644
index 0000000000000000000000000000000000000000..886e021b1ae7359027c00f8b7fc585f2d4a56be1
--- /dev/null
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_rdebug.h
@@ -0,0 +1,231 @@
+// 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), and there is no point trying to change it.
+//
+// This means that you cannot call the linker's dlsym() function to
+// lookup symbols in libraries that are not loaded through it, i.e.
+// any custom dynamic linker needs its own dlopen() / dlsym() and other
+// related functions, and ensure the loaded code only uses its own version.
+// (See support code in crazy_linker_wrappers.cpp)
+//
+// The global '_r_debug' variable is a r_debug structure, whose 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.
+//
+// The system linker expects to be the only item modifying the 'r_map'
+// list, and as such it may set the pages that contain the list readonly
+// outside of its own modifications. In threaded environments where the
+// system linker and the crazy linker are operating simultaneously on
+// different threads this may be a problem; we need these pages to be
+// writable when we have to update the list. We cannot track the system
+// linker's actions, so to avoid clashing with it we may need to try and
+// move 'r_map' updates to a different thread, to serialize them with
+// other system linker activity.
+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;
+};
+
+// A callback poster is a function that can be called to request a later
+// callback. Poster arguments are: an opaque pointer to the caller's
+// context, a pointer to a function with a single void* argument that will
+// handle the callback, and the opaque void* argument value to send with
+// the callback.
+typedef void (*crazy_callback_handler_t)(void* opaque);
+typedef bool (*rdebug_callback_poster_t)(void* context,
+ crazy_callback_handler_t,
+ void* opaque);
+
+// A callback handler is a static function, either AddEntryInternal() or
+// DelEntryInternal(). It calls the appropriate r_map update member
+// function, AddEntryImpl() or DelEntryImpl().
+class RDebug;
+typedef void (*rdebug_callback_handler_t)(RDebug*, link_map_t*);
+
+class RDebug {
+ public:
+ RDebug() : r_debug_(NULL), init_(false),
+ readonly_entries_(false), post_for_later_execution_(NULL),
+ post_for_later_execution_context_(NULL) {}
+ ~RDebug() {}
+
+ // Add entries to and remove entries from the list. If post for later
+ // execution is enabled, schedule callbacks and return. Otherwise
+ // action immediately.
+ void AddEntry(link_map_t* entry) { RunOrDelay(&AddEntryInternal, entry); }
+ void DelEntry(link_map_t* entry) { RunOrDelay(&DelEntryInternal, entry); }
+
+ // Assign the function used to request a callback from another thread.
+ // The context here is opaque, but is the API's crazy_context.
+ void SetDelayedCallbackPoster(rdebug_callback_poster_t poster,
+ void* context) {
+ post_for_later_execution_ = poster;
+ post_for_later_execution_context_ = context;
+ }
+
+ 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();
+
+ // Support for scheduling list manipulation through callbacks.
+ // AddEntry() and DelEntry() pass the addresses of static functions to
+ // to RunOrDelay(). This requests a callback if later execution
+ // is enabled, otherwise it runs immediately on the current thread.
+ // AddEntryImpl() and DelEntryImpl() are the member functions called
+ // by the static ones to do the actual work.
+ void AddEntryImpl(link_map_t* entry);
+ void DelEntryImpl(link_map_t* entry);
+ static void AddEntryInternal(RDebug* rdebug, link_map_t* entry) {
+ rdebug->AddEntryImpl(entry);
+ }
+ static void DelEntryInternal(RDebug* rdebug, link_map_t* entry) {
+ rdebug->DelEntryImpl(entry);
+ }
+
+ // Post handler for delayed execution. Return true if delayed execution
+ // is enabled and posting succeeded.
+ bool PostCallback(rdebug_callback_handler_t handler, link_map_t* entry);
+
+ // Run handler as a callback if enabled, otherwise immediately.
+ void RunOrDelay(rdebug_callback_handler_t handler, link_map_t* entry) {
+ if (!PostCallback(handler, entry))
+ (*handler)(this, entry);
+ }
+
+ RDebug(const RDebug&);
+ RDebug& operator=(const RDebug&);
+
+ r_debug* r_debug_;
+ bool init_;
+ bool readonly_entries_;
+ rdebug_callback_poster_t post_for_later_execution_;
+ void* post_for_later_execution_context_;
+};
+
+} // namespace crazy
+
+#endif // CRAZY_LINKER_REDUG_H

Powered by Google App Engine
This is Rietveld 408576698