Allow sharing the RELRO section via a file.

Add flags and a file descriptor to android_dlopen_ext() to allow writing
the RELRO section of the loaded library to a file after relocation
processing, and to allow mapping identical pages from the file over the
top of relocated memory in another process. Explicitly comparing the
pages is required in case a page contains a reference to a symbol
defined in another library loaded at a random base address.

Bug: 13005501
Change-Id: Ibb5b2d384edfaa5acf3e97a5f8b6115c10497a1e
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 299b408..14dff2b 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -17,8 +17,14 @@
 #include <gtest/gtest.h>
 
 #include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
 #include <android/dlext.h>
 #include <sys/mman.h>
+#include <sys/wait.h>
 
 
 #define ASSERT_DL_NOTNULL(ptr) \
@@ -27,10 +33,14 @@
 #define ASSERT_DL_ZERO(i) \
     ASSERT_EQ(0, i) << "dlerror: " << dlerror()
 
+#define ASSERT_NOERROR(i) \
+    ASSERT_NE(-1, i) << "errno: " << strerror(errno)
+
 
 typedef int (*fn)(void);
 #define LIBNAME "libdlext_test.so"
 #define LIBSIZE 1024*1024 // how much address space to reserve for it
+#define RELRO_FILE "/data/local/tmp/libdlext_test.relro"
 
 
 class DlExtTest : public ::testing::Test {
@@ -134,3 +144,49 @@
                             reinterpret_cast<char*>(start) + PAGE_SIZE));
   EXPECT_EQ(4, f());
 }
+
+TEST_F(DlExtTest, RelroShareChildWrites) {
+  void* start = mmap(NULL, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
+                     -1, 0);
+  ASSERT_TRUE(start != MAP_FAILED);
+  android_dlextinfo extinfo;
+  extinfo.reserved_addr = start;
+  extinfo.reserved_size = LIBSIZE;
+
+  int relro_fd;
+  relro_fd = open(RELRO_FILE, O_CREAT | O_RDWR | O_TRUNC, 0644);
+  extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_WRITE_RELRO;
+  ASSERT_NOERROR(relro_fd);
+  extinfo.relro_fd = relro_fd;
+
+  pid_t pid = fork();
+  if (pid == 0) {
+    // child process
+    void* handle = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo);
+    if (handle == NULL) {
+      fprintf(stderr, "in child: %s\n", dlerror());
+      exit(1);
+    }
+    exit(0);
+  }
+
+  // continuing in parent
+  ASSERT_NOERROR(close(relro_fd));
+  ASSERT_NOERROR(pid);
+  int status;
+  ASSERT_EQ(pid, waitpid(pid, &status, 0));
+  ASSERT_TRUE(WIFEXITED(status));
+  ASSERT_EQ(0, WEXITSTATUS(status));
+
+  relro_fd = open(RELRO_FILE, O_RDONLY);
+  ASSERT_NOERROR(relro_fd);
+  extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_USE_RELRO;
+  extinfo.relro_fd = relro_fd;
+  handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo);
+  ASSERT_NOERROR(close(relro_fd));
+
+  ASSERT_DL_NOTNULL(handle_);
+  fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber"));
+  ASSERT_DL_NOTNULL(f);
+  EXPECT_EQ(4, f());
+}