Merge "linker: Enable debug logging via properties"
diff --git a/linker/Android.mk b/linker/Android.mk
index 29520f9..0faf70b 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -24,6 +24,7 @@
     linker_block_allocator.cpp \
     linker_gdb_support.cpp \
     linker_libc_support.c \
+    linker_logger.cpp \
     linker_mapped_file_fragment.cpp \
     linker_phdr.cpp \
     linker_sdk_versions.cpp \
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index a3ebcd6..110846d 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -68,6 +68,7 @@
 static void* dlopen_ext(const char* filename, int flags,
                         const android_dlextinfo* extinfo, void* caller_addr) {
   ScopedPthreadMutexLocker locker(&g_dl_mutex);
+  g_linker_logger.ResetState();
   void* result = do_dlopen(filename, flags, extinfo, caller_addr);
   if (result == nullptr) {
     __bionic_format_dlerror("dlopen failed", linker_get_error_buffer());
@@ -88,6 +89,7 @@
 
 void* dlsym_impl(void* handle, const char* symbol, const char* version, void* caller_addr) {
   ScopedPthreadMutexLocker locker(&g_dl_mutex);
+  g_linker_logger.ResetState();
   void* result;
   if (!do_dlsym(handle, symbol, version, caller_addr, &result)) {
     __bionic_format_dlerror(linker_get_error_buffer(), nullptr);
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 0fe0c38..4e8a33c 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -61,6 +61,7 @@
 #include "linker_utils.h"
 
 #include "android-base/strings.h"
+#include "android-base/stringprintf.h"
 #include "debuggerd/client.h"
 #include "ziparchive/zip_archive.h"
 
@@ -192,9 +193,9 @@
 static bool g_public_namespace_initialized;
 static soinfo::soinfo_list_t g_public_namespace;
 
-__LIBC_HIDDEN__ int g_ld_debug_verbosity;
-
-__LIBC_HIDDEN__ abort_msg_t* g_abort_message = nullptr; // For debuggerd.
+int g_ld_debug_verbosity;
+abort_msg_t* g_abort_message = nullptr; // For debuggerd.
+const char* g_argv0 = nullptr;
 
 static std::string dirname(const char *path) {
   const char* last_slash = strrchr(path, '/');
@@ -2226,17 +2227,54 @@
   parse_LD_LIBRARY_PATH(ld_library_path);
 }
 
+static std::string android_dlextinfo_to_string(const android_dlextinfo* info) {
+  if (info == nullptr) {
+    return "(null)";
+  }
+
+  return android::base::StringPrintf("[flags=0x%" PRIx64 ","
+                                     " reserved_addr=%p,"
+                                     " reserved_size=0x%zx,"
+                                     " relro_fd=%d,"
+                                     " library_fd=%d,"
+                                     " library_fd_offset=0x%" PRIx64 ","
+                                     " library_namespace=%s@%p]",
+                                     info->flags,
+                                     info->reserved_addr,
+                                     info->reserved_size,
+                                     info->relro_fd,
+                                     info->library_fd,
+                                     info->library_fd_offset,
+                                     (info->flags & ANDROID_DLEXT_USE_NAMESPACE) != 0 ?
+                                        (info->library_namespace != nullptr ?
+                                          info->library_namespace->get_name() : "(null)") : "(n/a)",
+                                     (info->flags & ANDROID_DLEXT_USE_NAMESPACE) != 0 ?
+                                        info->library_namespace : nullptr);
+}
+
 void* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo,
                   void* caller_addr) {
   soinfo* const caller = find_containing_library(caller_addr);
+  android_namespace_t* ns = get_caller_namespace(caller);
+
+  LD_LOG(kLogDlopen,
+         "dlopen(name=\"%s\", flags=0x%x, extinfo=%s, caller=\"%s\", caller_ns=%s@%p) ...",
+         name,
+         flags,
+         android_dlextinfo_to_string(extinfo).c_str(),
+         caller == nullptr ? "(null)" : caller->get_realpath(),
+         ns == nullptr ? "(null)" : ns->get_name(),
+         ns);
+
+  auto failure_guard = make_scope_guard([&]() {
+    LD_LOG(kLogDlopen, "... dlopen failed: %s", linker_get_error_buffer());
+  });
 
   if ((flags & ~(RTLD_NOW|RTLD_LAZY|RTLD_LOCAL|RTLD_GLOBAL|RTLD_NODELETE|RTLD_NOLOAD)) != 0) {
     DL_ERR("invalid flags to dlopen: %x", flags);
     return nullptr;
   }
 
-  android_namespace_t* ns = get_caller_namespace(caller);
-
   if (extinfo != nullptr) {
     if ((extinfo->flags & ~(ANDROID_DLEXT_VALID_FLAG_BITS)) != 0) {
       DL_ERR("invalid extended flags to android_dlopen_ext: 0x%" PRIx64, extinfo->flags);
@@ -2269,8 +2307,13 @@
   ProtectedDataGuard guard;
   soinfo* si = find_library(ns, name, flags, extinfo, caller);
   if (si != nullptr) {
+    failure_guard.disable();
     si->call_constructors();
-    return si->to_handle();
+    void* handle = si->to_handle();
+    LD_LOG(kLogDlopen,
+           "... dlopen successful: realpath=\"%s\", soname=\"%s\", handle=%p",
+           si->get_realpath(), si->get_soname(), handle);
+    return handle;
   }
 
   return nullptr;
@@ -4070,6 +4113,8 @@
   };
   debuggerd_init(&callbacks);
 
+  g_linker_logger.ResetState();
+
   // Get a few environment variables.
   const char* LD_DEBUG = getenv("LD_DEBUG");
   if (LD_DEBUG != nullptr) {
@@ -4143,7 +4188,7 @@
   ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(si->base);
   if (elf_hdr->e_type != ET_DYN) {
     __libc_fatal("\"%s\": error: only position independent executables (PIE) are supported.",
-                 args.argv[0]);
+                 g_argv0);
   }
 
   // Use LD_LIBRARY_PATH and LD_PRELOAD (but only if we aren't setuid/setgid).
@@ -4155,7 +4200,7 @@
   init_default_namespace();
 
   if (!si->prelink_image()) {
-    __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", args.argv[0], linker_get_error_buffer());
+    __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv0, linker_get_error_buffer());
   }
 
   // add somain to global group
@@ -4186,10 +4231,10 @@
       !find_libraries(&g_default_namespace, si, needed_library_names, needed_libraries_count,
                       nullptr, &g_ld_preloads, ld_preloads_count, RTLD_GLOBAL, nullptr,
                       /* add_as_children */ true)) {
-    __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", args.argv[0], linker_get_error_buffer());
+    __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv0, linker_get_error_buffer());
   } else if (needed_libraries_count == 0) {
     if (!si->link_image(g_empty_list, soinfo::soinfo_list_t::make_list(si), nullptr)) {
-      __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", args.argv[0], linker_get_error_buffer());
+      __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv0, linker_get_error_buffer());
     }
     si->increment_ref_count();
   }
@@ -4212,12 +4257,12 @@
 
 #if TIMING
   gettimeofday(&t1, nullptr);
-  PRINT("LINKER TIME: %s: %d microseconds", args.argv[0], (int) (
+  PRINT("LINKER TIME: %s: %d microseconds", g_argv0, (int) (
            (((long long)t1.tv_sec * 1000000LL) + (long long)t1.tv_usec) -
            (((long long)t0.tv_sec * 1000000LL) + (long long)t0.tv_usec)));
 #endif
 #if STATS
-  PRINT("RELO STATS: %s: %d abs, %d rel, %d copy, %d symbol", args.argv[0],
+  PRINT("RELO STATS: %s: %d abs, %d rel, %d copy, %d symbol", g_argv0,
          linker_stats.count[kRelocAbsolute],
          linker_stats.count[kRelocRelative],
          linker_stats.count[kRelocCopy],
@@ -4243,7 +4288,7 @@
         }
       }
     }
-    PRINT("PAGES MODIFIED: %s: %d (%dKB)", args.argv[0], count, count * 4);
+    PRINT("PAGES MODIFIED: %s: %d (%dKB)", g_argv0, count, count * 4);
   }
 #endif
 
@@ -4280,8 +4325,8 @@
   return 0;
 }
 
-static void __linker_cannot_link(KernelArgumentBlock& args) {
-  __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", args.argv[0], linker_get_error_buffer());
+static void __linker_cannot_link() {
+  __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv0, linker_get_error_buffer());
 }
 
 /*
@@ -4296,6 +4341,8 @@
 extern "C" ElfW(Addr) __linker_init(void* raw_args) {
   KernelArgumentBlock args(raw_args);
 
+  g_argv0 = args.argv[0];
+
   ElfW(Addr) linker_addr = args.getauxval(AT_BASE);
   ElfW(Addr) entry_point = args.getauxval(AT_ENTRY);
   ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_addr);
@@ -4312,7 +4359,7 @@
   if (reinterpret_cast<ElfW(Addr)>(&_start) == entry_point) {
     __libc_format_fd(STDOUT_FILENO,
                      "This is %s, the helper program for shared library executables.\n",
-                     args.argv[0]);
+                     g_argv0);
     exit(0);
   }
 
@@ -4325,7 +4372,7 @@
   linker_so.set_linker_flag();
 
   // Prelink the linker so we can access linker globals.
-  if (!linker_so.prelink_image()) __linker_cannot_link(args);
+  if (!linker_so.prelink_image()) __linker_cannot_link();
 
   // This might not be obvious... The reasons why we pass g_empty_list
   // in place of local_group here are (1) we do not really need it, because
@@ -4333,7 +4380,7 @@
   // itself without having to look into local_group and (2) allocators
   // are not yet initialized, and therefore we cannot use linked_list.push_*
   // functions at this point.
-  if (!linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link(args);
+  if (!linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link();
 
 #if defined(__i386__)
   // On x86, we can't make system calls before this point.
@@ -4349,7 +4396,7 @@
 
   // We didn't protect the linker's RELRO pages in link_image because we
   // couldn't make system calls on x86 at that point, but we can now...
-  if (!linker_so.protect_relro()) __linker_cannot_link(args);
+  if (!linker_so.protect_relro()) __linker_cannot_link();
 
   // Initialize the linker's static libc's globals
   __libc_init_globals(args);
diff --git a/linker/linker.h b/linker/linker.h
index fbd236f..ea77920 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -40,6 +40,7 @@
 #include "private/bionic_page.h"
 #include "private/libc_logging.h"
 #include "linked_list.h"
+#include "linker_logger.h"
 
 #include <string>
 #include <vector>
@@ -48,7 +49,7 @@
     do { \
       __libc_format_buffer(linker_get_error_buffer(), linker_get_error_buffer_size(), fmt, ##x); \
       /* If LD_DEBUG is set high enough, log every dlerror(3) message. */ \
-      DEBUG("%s\n", linker_get_error_buffer()); \
+      LD_LOG(kLogErrors, "%s\n", linker_get_error_buffer()); \
     } while (false)
 
 #define DL_WARN(fmt, x...) \
diff --git a/linker/linker_logger.cpp b/linker/linker_logger.cpp
new file mode 100644
index 0000000..7499a83
--- /dev/null
+++ b/linker/linker_logger.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/system_properties.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include "android-base/strings.h"
+#include "linker_logger.h"
+#include "private/libc_logging.h"
+
+LinkerLogger g_linker_logger;
+
+static const char* kSystemLdDebugProperty = "debug.ld.all";
+static const std::string kLdDebugPropertyPrefix = "debug.ld.app.";
+
+static const char* kOptionErrors = "dlerror";
+static const char* kOptionDlopen = "dlopen";
+
+static std::string property_get(const char* name) {
+  char value[PROP_VALUE_MAX] = {};
+  __system_property_get(name, value);
+  return value;
+}
+
+static uint32_t ParseProperty(const std::string& value) {
+  if (value.empty()) {
+    return 0;
+  }
+
+  std::vector<std::string> options = android::base::Split(value, ",");
+
+  uint32_t flags = 0;
+
+  for (const auto& o : options) {
+    if (o == kOptionErrors) {
+      flags |= kLogErrors;
+    } else if (o == kOptionDlopen){
+      flags |= kLogDlopen;
+    } else {
+      __libc_format_log(ANDROID_LOG_WARN, "linker", "Unknown debug.ld option \"%s\", will ignore.", o.c_str());
+    }
+  }
+
+  return flags;
+}
+
+void LinkerLogger::ResetState() {
+  // the most likely scenario app is not debuggable and
+  // is running on user build - the logging is disabled.
+  if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) {
+    return;
+  }
+
+  flags_ = 0;
+  // check flag applied to all processes first
+  std::string value = property_get(kSystemLdDebugProperty);
+  flags_ |= ParseProperty(value);
+
+  // get process basename
+  std::string process_name = basename(g_argv0);
+
+  std::string property_name = std::string(kLdDebugPropertyPrefix) + process_name;
+
+  // Property names are limited to PROP_NAME_MAX.
+
+  if (property_name.size() >= PROP_NAME_MAX) {
+    size_t count = PROP_NAME_MAX - 1;
+    // remove trailing dots...
+    while (property_name[count-1] == '.') {
+      --count;
+    }
+
+    property_name = property_name.substr(0, count);
+  }
+  value = property_get(property_name.c_str());
+  flags_ |= ParseProperty(value);
+}
+
+void LinkerLogger::Log(uint32_t type, const char* format, ...) {
+  if ((flags_ & type) == 0) {
+    return;
+  }
+
+  va_list ap;
+  va_start(ap, format);
+  __libc_format_log_va_list(ANDROID_LOG_DEBUG, "linker", format, ap);
+  va_end(ap);
+}
+
diff --git a/linker/linker_logger.h b/linker/linker_logger.h
new file mode 100644
index 0000000..6b06aff
--- /dev/null
+++ b/linker/linker_logger.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LINKER_LOGGER_H_
+#define _LINKER_LOGGER_H_
+
+#include <stdlib.h>
+#include <limits.h>
+#include "private/bionic_macros.h"
+
+#define LD_LOG(type, x...) \
+  { \
+    g_linker_logger.Log(type, x); \
+  }
+
+constexpr const uint32_t kLogErrors = 1 << 0;
+constexpr const uint32_t kLogDlopen = 1 << 1;
+
+class LinkerLogger {
+ public:
+  LinkerLogger() : flags_(0) { }
+
+  void ResetState();
+  void Log(uint32_t type, const char* format, ...);
+ private:
+  uint32_t flags_;
+
+  DISALLOW_COPY_AND_ASSIGN(LinkerLogger);
+};
+
+extern LinkerLogger g_linker_logger;
+extern const char* g_argv0;
+
+#endif /* _LINKER_LOGGER_H_ */