More dynamic linker cleanup.

I still want to break linker_format out into its own library so we can reuse
it for malloc debugging and so forth. (There are many similar pieces of code
in bionic, but the linker's one seems to be the most complete/functional.)

Change-Id: If3721853d28937c8e821ca1d23cf200e228a409a
diff --git a/linker/Android.mk b/linker/Android.mk
index 503da86..e3ac7e9 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -3,20 +3,19 @@
 
 LOCAL_SRC_FILES:= \
 	arch/$(TARGET_ARCH)/begin.S \
-	debugger.c \
+	debugger.cpp \
 	dlfcn.cpp \
 	linker.cpp \
-	linker_environ.c \
-	linker_format.c \
-	linker_phdr.c \
-	rt.c
+	linker_environ.cpp \
+	linker_format.cpp \
+	linker_phdr.cpp \
+	rt.cpp
 
 LOCAL_LDFLAGS := -shared
 
 LOCAL_CFLAGS += -fno-stack-protector \
         -Wstrict-overflow=5 \
         -fvisibility=hidden \
-        -std=gnu99 \
         -Wall -Wextra
 
 # Set LINKER_DEBUG to either 1 or 0
diff --git a/linker/debugger.c b/linker/debugger.cpp
similarity index 84%
rename from linker/debugger.c
rename to linker/debugger.cpp
index e4d4ae9..bba89b8 100644
--- a/linker/debugger.c
+++ b/linker/debugger.cpp
@@ -26,6 +26,9 @@
  * SUCH DAMAGE.
  */
 
+#include "linker.h"
+#include "linker_format.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -36,45 +39,35 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 
-extern int tgkill(int tgid, int tid, int sig);
+#include <private/logd.h>
 
-void notify_gdb_of_libraries();
+extern "C" int tgkill(int tgid, int tid, int sig);
 
 #define DEBUGGER_SOCKET_NAME "android:debuggerd"
 
-typedef enum {
+enum debugger_action_t {
     // dump a crash
     DEBUGGER_ACTION_CRASH,
     // dump a tombstone file
     DEBUGGER_ACTION_DUMP_TOMBSTONE,
     // dump a backtrace only back to the socket
     DEBUGGER_ACTION_DUMP_BACKTRACE,
-} debugger_action_t;
+};
 
 /* message sent over the socket */
-typedef struct {
+struct debugger_msg_t {
     debugger_action_t action;
     pid_t tid;
-} debugger_msg_t;
-
-#define  RETRY_ON_EINTR(ret,cond) \
-    do { \
-        ret = (cond); \
-    } while (ret < 0 && errno == EINTR)
+};
 
 // see man(2) prctl, specifically the section about PR_GET_NAME
 #define MAX_TASK_NAME_LEN (16)
 
-static int socket_abstract_client(const char *name, int type)
-{
-    struct sockaddr_un addr;
-    size_t namelen;
-    socklen_t alen;
-    int s, err;
-
-    namelen  = strlen(name);
+static int socket_abstract_client(const char* name, int type) {
+    sockaddr_un addr;
 
     // Test with length +1 for the *initial* '\0'.
+    size_t namelen = strlen(name);
     if ((namelen + 1) > sizeof(addr.sun_path)) {
         errno = EINVAL;
         return -1;
@@ -86,18 +79,20 @@
      * Note: The path in this case is *not* supposed to be
      * '\0'-terminated. ("man 7 unix" for the gory details.)
      */
-    memset (&addr, 0, sizeof addr);
+    memset(&addr, 0, sizeof(addr));
     addr.sun_family = AF_LOCAL;
     addr.sun_path[0] = 0;
     memcpy(addr.sun_path + 1, name, namelen);
 
-    alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
+    socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
 
-    s = socket(AF_LOCAL, type, 0);
-    if(s < 0) return -1;
+    int s = socket(AF_LOCAL, type, 0);
+    if (s == -1) {
+        return -1;
+    }
 
-    RETRY_ON_EINTR(err,connect(s, (struct sockaddr *) &addr, alen));
-    if (err < 0) {
+    int err = TEMP_FAILURE_RETRY(connect(s, (sockaddr*) &addr, alen));
+    if (err == -1) {
         close(s);
         s = -1;
     }
@@ -105,9 +100,6 @@
     return s;
 }
 
-#include "linker_format.h"
-#include <../libc/private/logd.h>
-
 /*
  * Writes a summary of the signal to the log file.  We do this so that, if
  * for some reason we're not able to contact debuggerd, there is still some
@@ -116,15 +108,9 @@
  * We could be here as a result of native heap corruption, or while a
  * mutex is being held, so we don't want to use any libc functions that
  * could allocate memory or hold a lock.
- *
- * "info" will be NULL if the siginfo_t information was not available.
  */
-static void logSignalSummary(int signum, const siginfo_t* info)
-{
-    char buffer[128];
-    char threadname[MAX_TASK_NAME_LEN + 1]; // one more for termination
-
-    char* signame;
+static void logSignalSummary(int signum, const siginfo_t* info) {
+    const char* signame;
     switch (signum) {
         case SIGILL:    signame = "SIGILL";     break;
         case SIGABRT:   signame = "SIGABRT";    break;
@@ -138,6 +124,7 @@
         default:        signame = "???";        break;
     }
 
+    char threadname[MAX_TASK_NAME_LEN + 1]; // one more for termination
     if (prctl(PR_GET_NAME, (unsigned long)threadname, 0, 0, 0) != 0) {
         strcpy(threadname, "<name unknown>");
     } else {
@@ -145,6 +132,9 @@
         // implies that 16 byte names are not.
         threadname[MAX_TASK_NAME_LEN] = 0;
     }
+
+    char buffer[128];
+    // "info" will be NULL if the siginfo_t information was not available.
     if (info != NULL) {
         format_buffer(buffer, sizeof(buffer),
             "Fatal signal %d (%s) at 0x%08x (code=%d), thread %d (%s)",
@@ -161,8 +151,7 @@
 /*
  * Returns true if the handler for signal "signum" has SA_SIGINFO set.
  */
-static bool haveSiginfo(int signum)
-{
+static bool haveSiginfo(int signum) {
     struct sigaction oldact, newact;
 
     memset(&newact, 0, sizeof(newact));
@@ -188,11 +177,8 @@
  * Catches fatal signals so we can ask debuggerd to ptrace us before
  * we crash.
  */
-void debugger_signal_handler(int n, siginfo_t* info, void* unused __attribute__((unused)))
-{
+void debugger_signal_handler(int n, siginfo_t* info, void*) {
     char msgbuf[128];
-    unsigned tid;
-    int s;
 
     /*
      * It's possible somebody cleared the SA_SIGINFO flag, which would mean
@@ -204,8 +190,8 @@
 
     logSignalSummary(n, info);
 
-    tid = gettid();
-    s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM);
+    pid_t tid = gettid();
+    int s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM);
 
     if (s >= 0) {
         /* debugger knows our pid from the credentials on the
@@ -217,14 +203,14 @@
         debugger_msg_t msg;
         msg.action = DEBUGGER_ACTION_CRASH;
         msg.tid = tid;
-        RETRY_ON_EINTR(ret, write(s, &msg, sizeof(msg)));
+        ret = TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg)));
         if (ret == sizeof(msg)) {
             /* if the write failed, there is no point to read on
              * the file descriptor. */
-            RETRY_ON_EINTR(ret, read(s, &tid, 1));
-            int savedErrno = errno;
+            ret = TEMP_FAILURE_RETRY(read(s, &tid, 1));
+            int saved_errno = errno;
             notify_gdb_of_libraries();
-            errno = savedErrno;
+            errno = saved_errno;
         }
 
         if (ret < 0) {
@@ -266,8 +252,7 @@
     }
 }
 
-void debugger_init()
-{
+void debugger_init() {
     struct sigaction act;
     memset(&act, 0, sizeof(act));
     act.sa_sigaction = debugger_signal_handler;
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 4ffa1e7..4c52f3d 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -93,7 +93,6 @@
 static soinfo *somain; /* main process, always the one after libdl_info */
 #endif
 
-
 static char ldpaths_buf[LDPATH_BUFSIZE];
 static const char *ldpaths[LDPATH_MAX + 1];
 
@@ -108,9 +107,6 @@
 
 static int pid;
 
-/* This boolean is set if the program being loaded is setuid */
-static bool program_is_setuid;
-
 enum RelocationKind {
     kRelocAbsolute = 0,
     kRelocRelative,
@@ -257,8 +253,7 @@
     rtld_db_dlactivity();
 }
 
-extern "C" void notify_gdb_of_libraries()
-{
+void notify_gdb_of_libraries() {
     _r_debug.r_state = RT_ADD;
     rtld_db_dlactivity();
     _r_debug.r_state = RT_CONSISTENT;
@@ -1689,7 +1684,7 @@
     ftp://ftp.freebsd.org/pub/FreeBSD/CERT/advisories/FreeBSD-SA-02:23.stdio.asc
 
      */
-    if (program_is_setuid) {
+    if (get_AT_SECURE()) {
         nullify_closed_stdio();
     }
     notify_gdb_of_load(si);
@@ -1750,11 +1745,6 @@
     int argc = (int) *elfdata;
     char **argv = (char**) (elfdata + 1);
     unsigned *vecs = (unsigned*) (argv + argc + 1);
-    unsigned *v;
-    soinfo *si;
-    int i;
-    const char *ldpath_env = NULL;
-    const char *ldpreload_env = NULL;
 
     /* NOTE: we store the elfdata pointer on a special location
      *       of the temporary TLS area in order to pass it to
@@ -1773,53 +1763,36 @@
     gettimeofday(&t0, 0);
 #endif
 
-    /* Initialize environment functions, and get to the ELF aux vectors table */
+    // Initialize environment functions, and get to the ELF aux vectors table.
     vecs = linker_env_init(vecs);
 
-    /* Check auxv for AT_SECURE first to see if program is setuid, setgid,
-       has file caps, or caused a SELinux/AppArmor domain transition. */
-    for (v = vecs; v[0]; v += 2) {
-        if (v[0] == AT_SECURE) {
-            /* kernel told us whether to enable secure mode */
-            program_is_setuid = v[1];
-            goto sanitize;
-        }
-    }
-
-    /* Kernel did not provide AT_SECURE - fall back on legacy test. */
-    program_is_setuid = (getuid() != geteuid()) || (getgid() != getegid());
-
-sanitize:
-    /* Sanitize environment if we're loading a setuid program */
-    if (program_is_setuid) {
-        linker_env_secure();
-    }
-
     debugger_init();
 
-    /* Get a few environment variables */
-    {
+    // Get a few environment variables.
 #if LINKER_DEBUG
-        const char* env;
-        env = linker_env_get("DEBUG"); /* XXX: TODO: Change to LD_DEBUG */
-        if (env)
+    {
+        const char* env = linker_env_get("LD_DEBUG");
+        if (env != NULL) {
             debug_verbosity = atoi(env);
+        }
+    }
 #endif
 
-        /* Normally, these are cleaned by linker_env_secure, but the test
-         * against program_is_setuid doesn't cost us anything */
-        if (!program_is_setuid) {
-            ldpath_env = linker_env_get("LD_LIBRARY_PATH");
-            ldpreload_env = linker_env_get("LD_PRELOAD");
-        }
+    // Normally, these are cleaned by linker_env_init, but the test
+    // doesn't cost us anything.
+    const char* ldpath_env = NULL;
+    const char* ldpreload_env = NULL;
+    if (!get_AT_SECURE()) {
+      ldpath_env = linker_env_get("LD_LIBRARY_PATH");
+      ldpreload_env = linker_env_get("LD_PRELOAD");
     }
 
     INFO("[ android linker & debugger ]\n");
     DEBUG("%5d elfdata @ 0x%08x\n", pid, (unsigned)elfdata);
 
-    si = soinfo_alloc(argv[0]);
-    if(si == 0) {
-        exit(-1);
+    soinfo* si = soinfo_alloc(argv[0]);
+    if (si == NULL) {
+        exit(EXIT_FAILURE);
     }
 
     /* bootstrap the link map, the main exe always needs to be first */
@@ -1858,7 +1831,7 @@
     insert_soinfo_into_debug_map(&linker_soinfo);
 
     /* extract information passed from the kernel */
-    while(vecs[0] != 0){
+    while (vecs[0] != 0){
         switch(vecs[0]){
         case AT_PHDR:
             si->phdr = (Elf32_Phdr*) vecs[1];
@@ -1899,12 +1872,12 @@
         char errmsg[] = "CANNOT LINK EXECUTABLE\n";
         write(2, __linker_dl_err_buf, strlen(__linker_dl_err_buf));
         write(2, errmsg, sizeof(errmsg));
-        exit(-1);
+        exit(EXIT_FAILURE);
     }
 
     soinfo_call_preinit_constructors(si);
 
-    for(i = 0; preloads[i] != NULL; i++) {
+    for (size_t i = 0; preloads[i] != NULL; ++i) {
         soinfo_call_constructors(preloads[i]);
     }
 
@@ -2049,7 +2022,7 @@
         //
         // This situation should never occur unless the linker itself
         // is corrupt.
-        exit(-1);
+        exit(EXIT_FAILURE);
     }
 
     // We have successfully fixed our own relocations. It's safe to run
diff --git a/linker/linker.h b/linker/linker.h
index 8ed433c..54563bb 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -34,9 +34,6 @@
 #include <elf.h>
 #include <sys/exec_elf.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
 #include <link.h>
 
 #undef PAGE_MASK
@@ -89,8 +86,6 @@
     uintptr_t r_ldbase;
 };
 
-typedef struct soinfo soinfo;
-
 #define FLAG_LINKED     0x00000001
 #define FLAG_ERROR      0x00000002
 #define FLAG_EXE        0x00000004 // The main executable
@@ -98,8 +93,7 @@
 
 #define SOINFO_NAME_LEN 128
 
-struct soinfo
-{
+struct soinfo {
     char name[SOINFO_NAME_LEN];
     const Elf32_Phdr *phdr;
     int phnum;
@@ -232,8 +226,6 @@
 Elf32_Sym *soinfo_lookup(soinfo *si, const char *name);
 void soinfo_call_constructors(soinfo *si);
 
-#ifdef __cplusplus
-};
-#endif
+extern "C" void notify_gdb_of_libraries();
 
 #endif
diff --git a/linker/linker_debug.h b/linker/linker_debug.h
index 48a7abf..b9dfe34 100644
--- a/linker/linker_debug.h
+++ b/linker/linker_debug.h
@@ -62,10 +62,6 @@
 #if LINKER_DEBUG
 #include "linker_format.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 extern int debug_verbosity;
 #if LINKER_DEBUG_TO_LOG
 extern int format_log(int, const char *, const char *, ...);
@@ -81,10 +77,6 @@
     } while (0)
 #endif /* !LINKER_DEBUG_TO_LOG */
 
-#ifdef __cplusplus
-};
-#endif
-
 #else /* !LINKER_DEBUG */
 #define _PRINTVF(v,f,x...)   do {} while(0)
 #endif /* LINKER_DEBUG */
diff --git a/linker/linker_environ.c b/linker/linker_environ.c
deleted file mode 100644
index fadcb60..0000000
--- a/linker/linker_environ.c
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2010 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 "linker_environ.h"
-#include <stddef.h>
-
-static char** _envp;
-
-/* Returns 1 if 'str' points to a valid environment variable definition.
- * For now, we check that:
- *  - It is smaller than MAX_ENV_LEN (to detect non-zero terminated strings)
- *  - It contains at least one equal sign that is not the first character
- */
-static int
-_is_valid_definition(const char*  str)
-{
-    int   pos = 0;
-    int   first_equal_pos = -1;
-
-    /* According to its sources, the kernel uses 32*PAGE_SIZE by default
-     * as the maximum size for an env. variable definition.
-     */
-    const int MAX_ENV_LEN = 32*4096;
-
-    if (str == NULL)
-        return 0;
-
-    /* Parse the string, looking for the first '=' there, and its size */
-    do {
-        if (str[pos] == '\0')
-            break;
-        if (str[pos] == '=' && first_equal_pos < 0)
-            first_equal_pos = pos;
-        pos++;
-    } while (pos < MAX_ENV_LEN);
-
-    if (pos >= MAX_ENV_LEN)  /* Too large */
-        return 0;
-
-    if (first_equal_pos < 1)  /* No equal sign, or it is the first character */
-        return 0;
-
-    return 1;
-}
-
-unsigned*
-linker_env_init(unsigned* vecs)
-{
-    /* Store environment pointer - can't be NULL */
-    _envp = (char**) vecs;
-
-    /* Skip over all definitions */
-    while (vecs[0] != 0)
-        vecs++;
-    /* The end of the environment block is marked by two NULL pointers */
-    vecs++;
-
-    /* As a sanity check, we're going to remove all invalid variable
-     * definitions from the environment array.
-     */
-    {
-        char** readp  = _envp;
-        char** writep = _envp;
-        for ( ; readp[0] != NULL; readp++ ) {
-            if (!_is_valid_definition(readp[0]))
-                continue;
-            writep[0] = readp[0];
-            writep++;
-        }
-        writep[0] = NULL;
-    }
-
-    /* Return the address of the aux vectors table */
-    return vecs;
-}
-
-/* Check if the environment variable definition at 'envstr'
- * starts with '<name>=', and if so return the address of the
- * first character after the equal sign. Otherwise return NULL.
- */
-static char*
-env_match(char* envstr, const char* name)
-{
-    size_t  cnt = 0;
-
-    while (envstr[cnt] == name[cnt] && name[cnt] != '\0')
-        cnt++;
-
-    if (name[cnt] == '\0' && envstr[cnt] == '=')
-        return envstr + cnt + 1;
-
-    return NULL;
-}
-
-#define MAX_ENV_LEN  (16*4096)
-
-const char*
-linker_env_get(const char* name)
-{
-    char** readp = _envp;
-
-    if (name == NULL || name[0] == '\0')
-        return NULL;
-
-    for ( ; readp[0] != NULL; readp++ ) {
-        char* val = env_match(readp[0], name);
-        if (val != NULL) {
-            /* Return NULL for empty strings, or if it is too large */
-            if (val[0] == '\0')
-                val = NULL;
-            return val;
-        }
-    }
-    return NULL;
-}
-
-
-void
-linker_env_unset(const char* name)
-{
-    char**  readp = _envp;
-    char**  writep = readp;
-
-    if (name == NULL || name[0] == '\0')
-        return;
-
-    for ( ; readp[0] != NULL; readp++ ) {
-        if (env_match(readp[0], name))
-            continue;
-        writep[0] = readp[0];
-        writep++;
-    }
-    /* end list with a NULL */
-    writep[0] = NULL;
-}
-
-
-
-/* Remove unsafe environment variables. This should be used when
- * running setuid programs. */
-void
-linker_env_secure(void)
-{
-    /* The same list than GLibc at this point */
-    static const char* const unsec_vars[] = {
-        "GCONV_PATH",
-        "GETCONF_DIR",
-        "HOSTALIASES",
-        "LD_AUDIT",
-        "LD_DEBUG",
-        "LD_DEBUG_OUTPUT",
-        "LD_DYNAMIC_WEAK",
-        "LD_LIBRARY_PATH",
-        "LD_ORIGIN_PATH",
-        "LD_PRELOAD",
-        "LD_PROFILE",
-        "LD_SHOW_AUXV",
-        "LD_USE_LOAD_BIAS",
-        "LOCALDOMAIN",
-        "LOCPATH",
-        "MALLOC_TRACE",
-        "MALLOC_CHECK_",
-        "NIS_PATH",
-        "NLSPATH",
-        "RESOLV_HOST_CONF",
-        "RES_OPTIONS",
-        "TMPDIR",
-        "TZDIR",
-        "LD_AOUT_LIBRARY_PATH",
-        "LD_AOUT_PRELOAD",
-        NULL
-    };
-
-    int count;
-    for (count = 0; unsec_vars[count] != NULL; count++) {
-        linker_env_unset(unsec_vars[count]);
-    }
-}
diff --git a/linker/linker_environ.cpp b/linker/linker_environ.cpp
new file mode 100644
index 0000000..357be6d
--- /dev/null
+++ b/linker/linker_environ.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2010 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 "linker_environ.h"
+
+#include <linux/auxvec.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static char** _envp;
+static bool _AT_SECURE_value = true;
+
+bool get_AT_SECURE() {
+  return _AT_SECURE_value;
+}
+
+/* Returns 1 if 'str' points to a valid environment variable definition.
+ * For now, we check that:
+ *  - It is smaller than MAX_ENV_LEN (to detect non-zero terminated strings)
+ *  - It contains at least one equal sign that is not the first character
+ */
+static int _is_valid_definition(const char* str) {
+  int pos = 0;
+  int first_equal_pos = -1;
+
+  // According to its sources, the kernel uses 32*PAGE_SIZE by default
+  // as the maximum size for an env. variable definition.
+  const int MAX_ENV_LEN = 32*4096;
+
+  if (str == NULL) {
+    return 0;
+  }
+
+  // Parse the string, looking for the first '=' there, and its size.
+  while (pos < MAX_ENV_LEN) {
+    if (str[pos] == '\0') {
+      break;
+    }
+    if (str[pos] == '=' && first_equal_pos < 0) {
+      first_equal_pos = pos;
+    }
+    pos++;
+  }
+
+  if (pos >= MAX_ENV_LEN) {
+    return 0; // Too large.
+  }
+
+  if (first_equal_pos < 1) {
+    return 0; // No equals sign, or it's the first character.
+  }
+
+  return 1;
+}
+
+static void __init_AT_SECURE(unsigned* auxv) {
+  // Check auxv for AT_SECURE first to see if program is setuid, setgid,
+  // has file caps, or caused a SELinux/AppArmor domain transition.
+  for (unsigned* v = auxv; v[0]; v += 2) {
+    if (v[0] == AT_SECURE) {
+      // Kernel told us whether to enable secure mode.
+      _AT_SECURE_value = v[1];
+      return;
+    }
+  }
+
+  // We don't support ancient kernels.
+  const char* msg = "FATAL: kernel did not supply AT_SECURE\n";
+  write(2, msg, strlen(msg));
+  exit(EXIT_FAILURE);
+}
+
+static void __remove_unsafe_environment_variables() {
+  // None of these should be allowed in setuid programs.
+  static const char* const UNSAFE_VARIABLE_NAMES[] = {
+      "GCONV_PATH",
+      "GETCONF_DIR",
+      "HOSTALIASES",
+      "LD_AOUT_LIBRARY_PATH",
+      "LD_AOUT_PRELOAD",
+      "LD_AUDIT",
+      "LD_DEBUG",
+      "LD_DEBUG_OUTPUT",
+      "LD_DYNAMIC_WEAK",
+      "LD_LIBRARY_PATH",
+      "LD_ORIGIN_PATH",
+      "LD_PRELOAD",
+      "LD_PROFILE",
+      "LD_SHOW_AUXV",
+      "LD_USE_LOAD_BIAS",
+      "LOCALDOMAIN",
+      "LOCPATH",
+      "MALLOC_CHECK_",
+      "MALLOC_TRACE",
+      "NIS_PATH",
+      "NLSPATH",
+      "RESOLV_HOST_CONF",
+      "RES_OPTIONS",
+      "TMPDIR",
+      "TZDIR",
+      NULL
+  };
+  for (size_t i = 0; UNSAFE_VARIABLE_NAMES[i] != NULL; ++i) {
+    linker_env_unset(UNSAFE_VARIABLE_NAMES[i]);
+  }
+}
+
+static void __remove_invalid_environment_variables() {
+  char** src  = _envp;
+  char** dst = _envp;
+  for (; src[0] != NULL; ++src) {
+    if (!_is_valid_definition(src[0])) {
+      continue;
+    }
+    dst[0] = src[0];
+    ++dst;
+  }
+  dst[0] = NULL;
+}
+
+unsigned* linker_env_init(unsigned* environment_and_aux_vectors) {
+  // Store environment pointer - can't be NULL.
+  _envp = reinterpret_cast<char**>(environment_and_aux_vectors);
+
+  // Skip over all environment variable definitions.
+  // The end of the environment block is marked by two NULL pointers.
+  unsigned* aux_vectors = environment_and_aux_vectors;
+  while (aux_vectors[0] != 0) {
+    ++aux_vectors;
+  }
+  ++aux_vectors;
+
+  __remove_invalid_environment_variables();
+  __init_AT_SECURE(aux_vectors);
+
+  // Sanitize environment if we're loading a setuid program.
+  if (get_AT_SECURE()) {
+    __remove_unsafe_environment_variables();
+  }
+
+  return aux_vectors;
+}
+
+/* Check if the environment variable definition at 'envstr'
+ * starts with '<name>=', and if so return the address of the
+ * first character after the equal sign. Otherwise return NULL.
+ */
+static char* env_match(char* envstr, const char* name) {
+  size_t i = 0;
+
+  while (envstr[i] == name[i] && name[i] != '\0') {
+    ++i;
+  }
+
+  if (name[i] == '\0' && envstr[i] == '=') {
+    return envstr + i + 1;
+  }
+
+  return NULL;
+}
+
+const char* linker_env_get(const char* name) {
+  if (name == NULL || name[0] == '\0') {
+    return NULL;
+  }
+
+  for (char** p = _envp; p[0] != NULL; ++p) {
+    char* val = env_match(p[0], name);
+    if (val != NULL) {
+      if (val[0] == '\0') {
+        return NULL; // Return NULL for empty strings.
+      }
+      return val;
+    }
+  }
+  return NULL;
+}
+
+void linker_env_unset(const char* name) {
+  char** readp = _envp;
+  char** writep = readp;
+
+  if (name == NULL || name[0] == '\0') {
+    return;
+  }
+
+  for ( ; readp[0] != NULL; readp++ ) {
+    if (env_match(readp[0], name)) {
+      continue;
+    }
+    writep[0] = readp[0];
+    writep++;
+  }
+  /* end list with a NULL */
+  writep[0] = NULL;
+}
diff --git a/linker/linker_environ.h b/linker/linker_environ.h
index d5f75a1..a0bd69f 100644
--- a/linker/linker_environ.h
+++ b/linker/linker_environ.h
@@ -25,38 +25,27 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
 #ifndef LINKER_ENVIRON_H
 #define LINKER_ENVIRON_H
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+// Call this function before anything else. 'environment_and_aux_vectors'
+// must point to the environment block in the ELF data block. The function
+// returns the start of the aux vectors after the environment block.
+extern unsigned* linker_env_init(unsigned* environment_and_aux_vectors);
 
-/* Call this function before anything else. 'vecs' must be the pointer
- * to the environment block in the ELF data block. The function returns
- * the start of the aux vectors after the env block.
- */
-extern unsigned*   linker_env_init(unsigned* vecs);
+// Unset a given environment variable. In case the variable is defined
+// multiple times, unset all instances. This modifies the environment
+// block, so any pointer returned by linker_env_get() after this call
+// might become invalid.
+extern void linker_env_unset(const char* name);
 
-/* Unset a given environment variable. In case the variable is defined
- * multiple times, unset all instances. This modifies the environment
- * block, so any pointer returned by linker_env_get() after this call
- * might become invalid */
-extern void        linker_env_unset(const char* name);
-
-
-/* Returns the value of environment variable 'name' if defined and not
- * empty, or NULL otherwise. Note that the returned pointer may become
- * invalid if linker_env_unset() or linker_env_secure() are called
- * after this function. */
+// Returns the value of environment variable 'name' if defined and not
+// empty, or NULL otherwise. Note that the returned pointer may become
+// invalid if linker_env_unset() is called after this function.
 extern const char* linker_env_get(const char* name);
 
-/* Remove insecure environment variables. This should be used when
- * running setuid programs. */
-extern void        linker_env_secure(void);
-
-#ifdef __cplusplus
-};
-#endif
+// Returns the value of this program's AT_SECURE variable.
+extern bool get_AT_SECURE();
 
 #endif /* LINKER_ENVIRON_H */
diff --git a/linker/linker_format.c b/linker/linker_format.cpp
similarity index 95%
rename from linker/linker_format.c
rename to linker/linker_format.cpp
index f60e259..cc70a03 100644
--- a/linker/linker_format.c
+++ b/linker/linker_format.cpp
@@ -26,6 +26,7 @@
  * SUCH DAMAGE.
  */
 
+#include <assert.h>
 #include <stdarg.h>
 #include <string.h>
 #include <errno.h>
@@ -43,14 +44,12 @@
 /*** Generic output sink
  ***/
 
-typedef struct {
-    void *opaque;
-    void (*send)(void *opaque, const char *data, int len);
-} Out;
+struct Out {
+  void *opaque;
+  void (*send)(void *opaque, const char *data, int len);
+};
 
-static void
-out_send(Out *o, const void *data, size_t len)
-{
+static void out_send(Out *o, const char *data, size_t len) {
     o->send(o->opaque, data, (int)len);
 }
 
@@ -72,27 +71,25 @@
 }
 
 /* forward declaration */
-static void
-out_vformat(Out *o, const char *format, va_list args);
+static void out_vformat(Out* o, const char* format, va_list args);
 
 /*** Bounded buffer output
  ***/
 
-typedef struct {
-    Out out[1];
-    char *buffer;
-    char *pos;
-    char *end;
-    int total;
-} BufOut;
+struct BufOut {
+  Out out[1];
+  char *buffer;
+  char *pos;
+  char *end;
+  int total;
+};
 
-static void
-buf_out_send(void *opaque, const char *data, int len)
-{
-    BufOut *bo = opaque;
+static void buf_out_send(void *opaque, const char *data, int len) {
+    BufOut *bo = reinterpret_cast<BufOut*>(opaque);
 
-    if (len < 0)
+    if (len < 0) {
         len = strlen(data);
+    }
 
     bo->total += len;
 
@@ -194,11 +191,11 @@
 /*** File descriptor output
  ***/
 
-typedef struct {
-    Out out[1];
-    int fd;
-    int total;
-} FdOut;
+struct FdOut {
+  Out out[1];
+  int fd;
+  int total;
+};
 
 static void
 fd_out_send(void *opaque, const char *data, int len)
@@ -593,6 +590,10 @@
 
         slen = strlen(str);
 
+        if (sign != '\0' || prec != -1) {
+            __assert(__FILE__, __LINE__, "sign/precision unsupported");
+        }
+
         if (slen < width && !padLeft) {
             char padChar = padZero ? '0' : ' ';
             out_send_repeat(o, padChar, width - slen);
diff --git a/linker/linker_format.h b/linker/linker_format.h
index 3766b62..b8873c0 100644
--- a/linker/linker_format.h
+++ b/linker/linker_format.h
@@ -25,25 +25,17 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
 #ifndef _LINKER_FORMAT_H
 #define _LINKER_FORMAT_H
 
 #include <stdarg.h>
 #include <stddef.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Formatting routines for the dynamic linker's debug traces */
-/* We want to avoid dragging the whole C library fprintf()   */
-/* implementation into the dynamic linker since this creates */
-/* issues (it uses malloc()/free()) and increases code size  */
-
+// Formatting routines for the dynamic linker's debug traces
+// We want to avoid dragging the whole C library fprintf()
+// implementation into the dynamic linker since this creates
+// issues (it uses malloc()/free()) and increases code size.
 int format_buffer(char *buffer, size_t bufsize, const char *format, ...);
 
-#ifdef __cplusplus
-};
-#endif
-
 #endif /* _LINKER_FORMAT_H */
diff --git a/linker/linker_phdr.c b/linker/linker_phdr.cpp
similarity index 99%
rename from linker/linker_phdr.c
rename to linker/linker_phdr.cpp
index 250ca20..1990366 100644
--- a/linker/linker_phdr.c
+++ b/linker/linker_phdr.cpp
@@ -301,7 +301,6 @@
         Elf32_Addr file_end   = file_start + phdr->p_filesz;
 
         Elf32_Addr file_page_start = PAGE_START(file_start);
-        Elf32_Addr file_page_end   = PAGE_END(file_end);
 
         seg_addr = mmap((void*)seg_page_start,
                         file_end - file_page_start,
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h
index a759262..2d4d735 100644
--- a/linker/linker_phdr.h
+++ b/linker/linker_phdr.h
@@ -37,12 +37,6 @@
 
 #include "linker.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* See linker_phdr.c for all usage documentation */
-
 int
 phdr_table_load(int                fd,
                 Elf32_Addr         phdr_offset,
@@ -107,8 +101,4 @@
                                Elf32_Addr**      dynamic,
                                size_t*           dynamic_count);
 
-#ifdef __cplusplus
-};
-#endif
-
 #endif /* LINKER_PHDR_H */
diff --git a/linker/rt.c b/linker/rt.cpp
similarity index 89%
rename from linker/rt.c
rename to linker/rt.cpp
index afbd651..710892a 100644
--- a/linker/rt.c
+++ b/linker/rt.cpp
@@ -28,9 +28,8 @@
 
 /*
  * This function is an empty stub where GDB locates a breakpoint to get notified
- * about linker activity.  It canʼt be inlined away, canʼt be hidden.
+ * about linker activity.  It canʼt be inlined away, can't be hidden.
  */
-void __attribute__((noinline)) __attribute__((visibility("default"))) rtld_db_dlactivity(void)
-{
+extern "C" void __attribute__((noinline)) __attribute__((visibility("default"))) rtld_db_dlactivity() {
 }