Add realpath for soinfo

  This change adds realpath to soinfo and
  extends limit on filenames from 128 to PATH_MAX.

  It also removes soinfo::name field, linker uses
  dt_soname instead.

Bug: http://b/19818481
Bug: https://code.google.com/p/android/issues/detail?id=80336
Change-Id: I9cff4cb5bda3ee2bc74e1bbded9594ea7fbe2a08
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 700abff..7012418 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -156,9 +156,9 @@
   ASSERT_SUBSTR("dlopen failed: file offset for the library \"libname_placeholder\" is negative", dlerror());
 
   extinfo.library_fd_offset = PAGE_SIZE;
-  handle_ = android_dlopen_ext("libname_placeholder", RTLD_NOW, &extinfo);
+  handle_ = android_dlopen_ext("libname_ignored", RTLD_NOW, &extinfo);
   ASSERT_TRUE(handle_ == nullptr);
-  ASSERT_STREQ("dlopen failed: \"libname_placeholder\" has bad ELF magic", dlerror());
+  ASSERT_EQ("dlopen failed: \"" + lib_path + "\" has bad ELF magic", dlerror());
 
   close(extinfo.library_fd);
 }
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index a63c070..1d62428 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -26,9 +26,12 @@
 
 #include <string>
 
+#include "utils.h"
+
 #define ASSERT_SUBSTR(needle, haystack) \
     ASSERT_PRED_FORMAT2(::testing::IsSubstring, needle, haystack)
 
+
 static bool g_called = false;
 extern "C" void DlSymTestFunction() {
   g_called = true;
@@ -699,7 +702,7 @@
   ASSERT_EQ(0, dlclose(self));
 }
 
-TEST(dlfcn, dladdr) {
+TEST(dlfcn, dladdr_executable) {
   dlerror(); // Clear any pending errors.
   void* self = dlopen(NULL, RTLD_NOW);
   ASSERT_TRUE(self != NULL);
@@ -720,13 +723,11 @@
   rc = readlink("/proc/self/exe", executable_path, sizeof(executable_path));
   ASSERT_NE(rc, -1);
   executable_path[rc] = '\0';
-  std::string executable_name(basename(executable_path));
 
   // The filename should be that of this executable.
-  // Note that we don't know whether or not we have the full path, so we want an "ends_with" test.
-  std::string dli_fname(info.dli_fname);
-  dli_fname = basename(&dli_fname[0]);
-  ASSERT_EQ(dli_fname, executable_name);
+  char dli_realpath[PATH_MAX];
+  ASSERT_TRUE(realpath(info.dli_fname, dli_realpath) != nullptr);
+  ASSERT_STREQ(executable_path, dli_realpath);
 
   // The symbol name should be the symbol we looked up.
   ASSERT_STREQ(info.dli_sname, "DlSymTestFunction");
@@ -734,22 +735,16 @@
   // The address should be the exact address of the symbol.
   ASSERT_EQ(info.dli_saddr, sym);
 
-  // Look in /proc/pid/maps to find out what address we were loaded at.
-  // TODO: factor /proc/pid/maps parsing out into a class and reuse all over bionic.
-  void* base_address = NULL;
-  char line[BUFSIZ];
-  FILE* fp = fopen("/proc/self/maps", "r");
-  ASSERT_TRUE(fp != NULL);
-  while (fgets(line, sizeof(line), fp) != NULL) {
-    uintptr_t start = strtoul(line, 0, 16);
-    line[strlen(line) - 1] = '\0'; // Chomp the '\n'.
-    char* path = strchr(line, '/');
-    if (path != NULL && strcmp(executable_path, path) == 0) {
-      base_address = reinterpret_cast<void*>(start);
+  std::vector<map_record> maps;
+  ASSERT_TRUE(Maps::parse_maps(&maps));
+
+  void* base_address = nullptr;
+  for (const map_record& rec : maps) {
+    if (executable_path == rec.pathname) {
+      base_address = reinterpret_cast<void*>(rec.addr_start);
       break;
     }
   }
-  fclose(fp);
 
   // The base address should be the address we were loaded at.
   ASSERT_EQ(info.dli_fbase, base_address);
@@ -757,6 +752,28 @@
   ASSERT_EQ(0, dlclose(self));
 }
 
+#if defined(__LP64__)
+#define BIONIC_PATH_TO_LIBC "/system/lib64/libc.so"
+#else
+#define BIONIC_PATH_TO_LIBC "/system/lib/libc.so"
+#endif
+
+TEST(dlfcn, dladdr_libc) {
+#if defined(__BIONIC__)
+  Dl_info info;
+  void* addr = reinterpret_cast<void*>(puts); // well-known libc function
+  ASSERT_TRUE(dladdr(addr, &info) != 0);
+
+  ASSERT_STREQ(BIONIC_PATH_TO_LIBC, info.dli_fname);
+  // TODO: add check for dfi_fbase
+  ASSERT_STREQ("puts", info.dli_sname);
+  ASSERT_EQ(addr, info.dli_saddr);
+#else
+  GTEST_LOG_(INFO) << "This test does nothing for glibc. Glibc returns path from ldconfig "
+      "for libc.so, which is symlink itself (not a realpath).\n";
+#endif
+}
+
 TEST(dlfcn, dladdr_invalid) {
   Dl_info info;
 
diff --git a/tests/utils.h b/tests/utils.h
new file mode 100644
index 0000000..bad5d89
--- /dev/null
+++ b/tests/utils.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TEST_UTILS_H
+#define __TEST_UTILS_H
+#include <inttypes.h>
+#include <sys/mman.h>
+
+#include "private/ScopeGuard.h"
+
+struct map_record {
+  uintptr_t addr_start;
+  uintptr_t addr_end;
+
+  int perms;
+
+  size_t offset;
+
+  dev_t device;
+  ino_t inode;
+
+  std::string pathname;
+};
+
+class Maps {
+ public:
+  static bool parse_maps(std::vector<map_record>* maps) {
+    char path[64];
+    snprintf(path, sizeof(path), "/proc/self/task/%d/maps", getpid());
+    FILE* fp = fopen(path, "re");
+    if (fp == nullptr) {
+      return false;
+    }
+
+    auto fp_guard = make_scope_guard([&]() {
+      fclose(fp);
+    });
+
+    char line[BUFSIZ];
+    while (fgets(line, sizeof(line), fp) != nullptr) {
+      map_record record;
+      dev_t dev_major, dev_minor;
+      char pathstr[BUFSIZ];
+      char prot[5]; // sizeof("rwxp")
+      if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %" SCNxPTR " %x:%x %lu %s",
+            &record.addr_start, &record.addr_end, prot, &record.offset,
+            &dev_major, &dev_minor, &record.inode, pathstr) == 8) {
+        record.perms = 0;
+        if (prot[0] == 'r') {
+          record.perms |= PROT_READ;
+        }
+        if (prot[1] == 'w') {
+          record.perms |= PROT_WRITE;
+        }
+        if (prot[2] == 'x') {
+          record.perms |= PROT_EXEC;
+        }
+
+        // TODO: parse shared/private?
+
+        record.device = makedev(dev_major, dev_minor);
+        record.pathname = pathstr;
+        maps->push_back(record);
+      }
+    }
+
+    return true;
+  }
+};
+
+#endif