Index: chrome/nacl/nacl_helper_linux.cc |
diff --git a/chrome/nacl/nacl_helper_linux.cc b/chrome/nacl/nacl_helper_linux.cc |
index a90b589b9151439c86577cdbc1df8acb6db2aceb..df25c8bc8c8a8f75fe59a995a9aa640688d69315 100644 |
--- a/chrome/nacl/nacl_helper_linux.cc |
+++ b/chrome/nacl/nacl_helper_linux.cc |
@@ -7,6 +7,7 @@ |
#include "chrome/common/nacl_helper_linux.h" |
#include <errno.h> |
+#include <link.h> |
#include <stdlib.h> |
#include <sys/socket.h> |
#include <sys/types.h> |
@@ -118,6 +119,53 @@ void HandleForkRequest(const std::vector<int>& child_fds) { |
} // namespace |
static const char kNaClHelperAtZero[] = "at-zero"; |
+static const char kNaClHelperRDebug[] = "r_debug"; |
+ |
+/* |
+ * Since we were started by the bootstrap program rather than in the |
+ * usual way, the debugger cannot figure out where our executable |
+ * or the dynamic linker or the shared libraries are in memory, |
+ * so it won't find any symbols. But we can fake it out to find us. |
+ * |
+ * The zygote passes --r_debug=0xXXXXXXXXXXXXXXXX. The bootstrap |
+ * program replaces the Xs with the address of its _r_debug |
+ * structure. The debugger will look for that symbol by name to |
+ * discover the addresses of key dynamic linker data structures. |
+ * Since all it knows about is the original main executable, which |
+ * is the bootstrap program, it finds the symbol defined there. The |
+ * dynamic linker's structure is somewhere else, but it is filled in |
+ * after initialization. The parts that really matter to the |
+ * debugger never change. So we just copy the contents of the |
+ * dynamic linker's structure into the address provided by the option. |
+ * Hereafter, if someone attaches a debugger (or examines a core dump), |
+ * the debugger will find all the symbols in the normal way. |
+ */ |
+static void check_r_debug(char *argv0) { |
+ std::string r_debug_switch_value = |
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kNaClHelperRDebug); |
+ if (!r_debug_switch_value.empty()) { |
+ char *endp = NULL; |
+ uintptr_t r_debug_addr = strtoul(r_debug_switch_value.c_str(), &endp, 0); |
+ if (r_debug_addr != 0 && *endp == '\0') { |
+ struct r_debug *bootstrap_r_debug = (struct r_debug *) r_debug_addr; |
+ *bootstrap_r_debug = _r_debug; |
eaeltsin
2011/11/12 01:35:23
Neat.
A very useful complement would be to introd
eaeltsin
2011/11/12 01:52:19
Related note: if we want to support subsequent inv
Mark Seaborn
2011/11/14 20:15:41
Is the symbol _r_debug part of the public ABI such
|
+ |
+ /* |
+ * Since the main executable (the bootstrap program) does not |
+ * have a dynamic section, the debugger will not skip the |
+ * first element of the link_map list as it usually would for |
+ * an executable or PIE that was loaded normally. But the |
+ * dynamic linker has set l_name for the PIE to "" as is |
+ * normal for the main executable. So the debugger doesn't |
+ * know which file it is. Fill in the actual file name, which |
+ * came in as our argv[0]. |
+ */ |
+ struct link_map *l = _r_debug.r_map; |
+ if (l->l_name[0] == '\0') |
+ l->l_name = argv0; |
Mark Seaborn
2011/11/14 20:15:41
I find modifying the dynamic linker's data structu
|
+ } |
+ } |
+} |
int main(int argc, char *argv[]) { |
CommandLine::Init(argc, argv); |
@@ -125,6 +173,8 @@ int main(int argc, char *argv[]) { |
base::RandUint64(); // acquire /dev/urandom fd before sandbox is raised |
std::vector<int> empty; // for SendMsg() calls |
+ check_r_debug(argv[0]); |
+ |
g_suid_sandbox_active = (NULL != getenv("SBX_D")); |
if (CommandLine::ForCurrentProcess()->HasSwitch(kNaClHelperAtZero)) { |