Merge "Add tagged pointers to bionic."
diff --git a/docs/status.md b/docs/status.md
index 5470668..fb88dd2 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -33,7 +33,11 @@
 Missing functionality:
   * `<aio.h>`
   * `<wordexp.h>`
-  * Thread cancellation (`pthread_cancel`).
+  * Thread cancellation (`pthread_cancel`). Unlikely to ever be implemented
+    because of the difficulty and cost of implementing it, and the difficulty
+    of using it correctly. See
+    [This is why we can't have safe cancellation points](https://lwn.net/Articles/683118/)
+    for more about thread cancellation.
   * Robust mutexes
 
 Run `./libc/tools/check-symbols-glibc.py` in bionic/ for the current
diff --git a/libc/Android.bp b/libc/Android.bp
index c764cb9..f1cca49 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -359,7 +359,6 @@
     defaults: ["libc_defaults"],
     srcs: [
         "upstream-freebsd/lib/libc/gen/glob.c",
-        "upstream-freebsd/lib/libc/stdlib/realpath.c",
     ],
 
     cflags: [
@@ -1179,6 +1178,7 @@
         "bionic/raise.cpp",
         "bionic/rand.cpp",
         "bionic/readlink.cpp",
+        "bionic/realpath.cpp",
         "bionic/reboot.cpp",
         "bionic/recv.cpp",
         "bionic/rename.cpp",
diff --git a/libc/NOTICE b/libc/NOTICE
index e2afdbf..87d39c9 100644
--- a/libc/NOTICE
+++ b/libc/NOTICE
@@ -1096,6 +1096,34 @@
 
 -------------------------------------------------------------------
 
+Copyright (C) 2020 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.
+
+-------------------------------------------------------------------
+
 Copyright (c) 1980, 1983, 1988, 1993
    The Regents of the University of California.  All rights reserved.
 
@@ -3781,34 +3809,6 @@
 
 -------------------------------------------------------------------
 
-Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. 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.
-3. The names of the authors may not be used to endorse or promote
-   products derived from this software without specific prior written
-   permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
-
--------------------------------------------------------------------
-
 Copyright (c) 2003 Dag-Erling Smørgrav
 All rights reserved.
 
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index c8138ce..070c45f 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -95,7 +95,7 @@
   // Initialize various globals.
   environ = __libc_shared_globals()->init_environ;
   errno = 0;
-  __progname = __libc_shared_globals()->init_progname ?: "<unknown>";
+  setprogname(__libc_shared_globals()->init_progname ?: "<unknown>");
 
 #if !defined(__LP64__)
   __check_max_thread_id();
diff --git a/libc/bionic/realpath.cpp b/libc/bionic/realpath.cpp
new file mode 100644
index 0000000..e43d8e2
--- /dev/null
+++ b/libc/bionic/realpath.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 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 <stdlib.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "private/FdPath.h"
+#include "private/ScopedFd.h"
+
+// This function needs a 4KiB (PATH_MAX) buffer.
+// The alternative is to heap allocate and then trim, but that's 2x the code.
+// (Remember that readlink(2) won't tell you the needed size, so the multi-pass
+// algorithm isn't even an option unless you want to just guess, in which case
+// you're back needing to trim again.)
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+
+char* realpath(const char* path, char* result) {
+  // Weird special case.
+  if (!path) {
+    errno = EINVAL;
+    return nullptr;
+  }
+
+  // Get an O_PATH fd, and...
+  ScopedFd fd(open(path, O_PATH | O_CLOEXEC));
+  if (fd.get() == -1) return nullptr;
+
+  // (...remember the device/inode that we're talking about and...)
+  struct stat sb;
+  if (fstat(fd.get(), &sb) == -1) return nullptr;
+  dev_t st_dev = sb.st_dev;
+  ino_t st_ino = sb.st_ino;
+
+  // ...ask the kernel to do the hard work for us.
+  FdPath fd_path(fd.get());
+  char dst[PATH_MAX];
+  ssize_t l = readlink(fd_path.c_str(), dst, sizeof(dst) - 1);
+  if (l == -1) return nullptr;
+  dst[l] = '\0';
+
+  // What if the file was removed in the meantime? readlink(2) will have
+  // returned "/a/b/c (deleted)", and we want to return ENOENT instead.
+  if (stat(dst, &sb) == -1 || st_dev != sb.st_dev || st_ino != sb.st_ino) {
+    errno = ENOENT;
+    return nullptr;
+  }
+
+  return result ? strcpy(result, dst) : strdup(dst);
+}
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index c405c7f..1298df7 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -1532,7 +1532,8 @@
     BacktraceUnwindFake(
       std::vector<unwindstack::LocalFrameData>{{nullptr, 0x1100, 0x100, "fake1", 10},
                                                {nullptr, 0x1200, 0x200, "fake2", 20}});
-    unwindstack::MapInfo map_info{nullptr, 0x10000, 0x20000, 0, PROT_READ | PROT_EXEC, "/data/fake.so"};
+    unwindstack::MapInfo map_info{nullptr, nullptr, 0x10000, 0x20000, 0,
+                                  PROT_READ | PROT_EXEC, "/data/fake.so"};
     BacktraceUnwindFake(
       std::vector<unwindstack::LocalFrameData>{{&map_info, 0x1a000, 0xa000, "level1", 0},
                                                {&map_info, 0x1b000, 0xb000, "level2", 10}});
diff --git a/libc/upstream-freebsd/lib/libc/stdlib/realpath.c b/libc/upstream-freebsd/lib/libc/stdlib/realpath.c
deleted file mode 100644
index c4bd953..0000000
--- a/libc/upstream-freebsd/lib/libc/stdlib/realpath.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. The names of the authors may not be used to endorse or promote
- *    products derived from this software without specific prior written
- *    permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
- */
-
-#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)realpath.c	8.1 (Berkeley) 2/16/94";
-#endif /* LIBC_SCCS and not lint */
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include "namespace.h"
-#include <sys/param.h>
-#include <sys/stat.h>
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include "un-namespace.h"
-
-/*
- * Find the real name of path, by removing all ".", ".." and symlink
- * components.  Returns (resolved) on success, or (NULL) on failure,
- * in which case the path which caused trouble is left in (resolved).
- */
-char *
-realpath(const char * __restrict path, char * __restrict resolved)
-{
-	struct stat sb;
-	char *p, *q, *s;
-	size_t left_len, resolved_len;
-	unsigned symlinks;
-	int m, slen;
-	char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
-
-	if (path == NULL) {
-		errno = EINVAL;
-		return (NULL);
-	}
-	if (path[0] == '\0') {
-		errno = ENOENT;
-		return (NULL);
-	}
-	if (resolved == NULL) {
-		resolved = malloc(PATH_MAX);
-		if (resolved == NULL)
-			return (NULL);
-		m = 1;
-	} else
-		m = 0;
-	symlinks = 0;
-	if (path[0] == '/') {
-		resolved[0] = '/';
-		resolved[1] = '\0';
-		if (path[1] == '\0')
-			return (resolved);
-		resolved_len = 1;
-		left_len = strlcpy(left, path + 1, sizeof(left));
-	} else {
-		if (getcwd(resolved, PATH_MAX) == NULL) {
-			if (m)
-				free(resolved);
-			else {
-				resolved[0] = '.';
-				resolved[1] = '\0';
-			}
-			return (NULL);
-		}
-		resolved_len = strlen(resolved);
-		left_len = strlcpy(left, path, sizeof(left));
-	}
-	if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
-		if (m)
-			free(resolved);
-		errno = ENAMETOOLONG;
-		return (NULL);
-	}
-
-	/*
-	 * Iterate over path components in `left'.
-	 */
-	while (left_len != 0) {
-		/*
-		 * Extract the next path component and adjust `left'
-		 * and its length.
-		 */
-		p = strchr(left, '/');
-		s = p ? p : left + left_len;
-		if (s - left >= sizeof(next_token)) {
-			if (m)
-				free(resolved);
-			errno = ENAMETOOLONG;
-			return (NULL);
-		}
-		memcpy(next_token, left, s - left);
-		next_token[s - left] = '\0';
-		left_len -= s - left;
-		if (p != NULL)
-			memmove(left, s + 1, left_len + 1);
-		if (resolved[resolved_len - 1] != '/') {
-			if (resolved_len + 1 >= PATH_MAX) {
-				if (m)
-					free(resolved);
-				errno = ENAMETOOLONG;
-				return (NULL);
-			}
-			resolved[resolved_len++] = '/';
-			resolved[resolved_len] = '\0';
-		}
-		if (next_token[0] == '\0') {
-			/* Handle consequential slashes. */
-			continue;
-		}
-		else if (strcmp(next_token, ".") == 0)
-			continue;
-		else if (strcmp(next_token, "..") == 0) {
-			/*
-			 * Strip the last path component except when we have
-			 * single "/"
-			 */
-			if (resolved_len > 1) {
-				resolved[resolved_len - 1] = '\0';
-				q = strrchr(resolved, '/') + 1;
-				*q = '\0';
-				resolved_len = q - resolved;
-			}
-			continue;
-		}
-
-		/*
-		 * Append the next path component and lstat() it.
-		 */
-		resolved_len = strlcat(resolved, next_token, PATH_MAX);
-		if (resolved_len >= PATH_MAX) {
-			if (m)
-				free(resolved);
-			errno = ENAMETOOLONG;
-			return (NULL);
-		}
-		if (lstat(resolved, &sb) != 0) {
-			if (m)
-				free(resolved);
-			return (NULL);
-		}
-		if (S_ISLNK(sb.st_mode)) {
-			if (symlinks++ > MAXSYMLINKS) {
-				if (m)
-					free(resolved);
-				errno = ELOOP;
-				return (NULL);
-			}
-			slen = readlink(resolved, symlink, sizeof(symlink) - 1);
-			if (slen < 0) {
-				if (m)
-					free(resolved);
-				return (NULL);
-			}
-			symlink[slen] = '\0';
-			if (symlink[0] == '/') {
-				resolved[1] = 0;
-				resolved_len = 1;
-			} else if (resolved_len > 1) {
-				/* Strip the last path component. */
-				resolved[resolved_len - 1] = '\0';
-				q = strrchr(resolved, '/') + 1;
-				*q = '\0';
-				resolved_len = q - resolved;
-			}
-
-			/*
-			 * If there are any path components left, then
-			 * append them to symlink. The result is placed
-			 * in `left'.
-			 */
-			if (p != NULL) {
-				if (symlink[slen - 1] != '/') {
-					if (slen + 1 >= sizeof(symlink)) {
-						if (m)
-							free(resolved);
-						errno = ENAMETOOLONG;
-						return (NULL);
-					}
-					symlink[slen] = '/';
-					symlink[slen + 1] = 0;
-				}
-				left_len = strlcat(symlink, left,
-				    sizeof(symlink));
-				if (left_len >= sizeof(left)) {
-					if (m)
-						free(resolved);
-					errno = ENAMETOOLONG;
-					return (NULL);
-				}
-			}
-			left_len = strlcpy(left, symlink, sizeof(left));
-		} else if (!S_ISDIR(sb.st_mode) && p != NULL) {
-			if (m)
-				free(resolved);
-			errno = ENOTDIR;
-			return (NULL);
-		}
-	}
-
-	/*
-	 * Remove trailing slash except when the resolved pathname
-	 * is a single "/".
-	 */
-	if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
-		resolved[resolved_len - 1] = '\0';
-	return (resolved);
-}
diff --git a/linker/arch/arm_neon/linker_gnu_hash_neon.cpp b/linker/arch/arm_neon/linker_gnu_hash_neon.cpp
index f4127ce..11cf5a3 100644
--- a/linker/arch/arm_neon/linker_gnu_hash_neon.cpp
+++ b/linker/arch/arm_neon/linker_gnu_hash_neon.cpp
@@ -156,7 +156,7 @@
   // Reverse the is-NUL vector so we can use clz to count the number of remaining bytes.
   is_nul = vrev64_u8(is_nul);
   const uint64_t is_nul_u64 = vget_lane_u64(vreinterpret_u64_u8(is_nul), 0);
-  const uint32_t num_valid_bits = is_nul_u64 == 0 ? 64 : __builtin_clzll(is_nul_u64);
+  const uint32_t num_valid_bits = __builtin_clzll(is_nul_u64);
 
   const uint32_t name_len = reinterpret_cast<const char*>(chunk_ptr) - name + (num_valid_bits >> 3);
 
diff --git a/tests/dl_test.cpp b/tests/dl_test.cpp
index 05bba05..2b963c7 100644
--- a/tests/dl_test.cpp
+++ b/tests/dl_test.cpp
@@ -135,7 +135,7 @@
   std::string expected_output =
       "ctor: argc=1 argv[0]=" + helper + "\n" +
       "main: argc=1 argv[0]=" + helper + "\n" +
-      "__progname=" + helper + "\n" +
+      "__progname=exec_linker_helper\n" +
       "helper_func called\n";
   ExecTestHelper eth;
   eth.SetArgs({ path_to_linker, helper.c_str(), nullptr });
@@ -151,7 +151,7 @@
   std::string expected_output =
       "ctor: argc=1 argv[0]=" + helper + "\n" +
       "main: argc=1 argv[0]=" + helper + "\n" +
-      "__progname=" + helper + "\n" +
+      "__progname=exec_linker_helper\n" +
       "helper_func called\n";
   ExecTestHelper eth;
   eth.SetArgs({ path_to_linker, helper.c_str(), nullptr });
diff --git a/tests/stdlib_test.cpp b/tests/stdlib_test.cpp
index ff4cb71..3f1ec86 100644
--- a/tests/stdlib_test.cpp
+++ b/tests/stdlib_test.cpp
@@ -29,6 +29,7 @@
 #include <limits>
 #include <string>
 
+#include <android-base/file.h>
 #include <android-base/macros.h>
 #include <gtest/gtest.h>
 
@@ -36,6 +37,8 @@
 #include "math_data_test.h"
 #include "utils.h"
 
+using namespace std::string_literals;
+
 template <typename T = int (*)(char*)>
 class GenericTemporaryFile {
  public:
@@ -322,6 +325,17 @@
   ASSERT_EQ(ENOENT, errno);
 }
 
+TEST(stdlib, realpath__ELOOP) {
+  TemporaryDir td;
+  std::string link = std::string(td.path) + "/loop";
+  ASSERT_EQ(0, symlink(link.c_str(), link.c_str()));
+
+  errno = 0;
+  char* p = realpath(link.c_str(), nullptr);
+  ASSERT_TRUE(p == nullptr);
+  ASSERT_EQ(ELOOP, errno);
+}
+
 TEST(stdlib, realpath__component_after_non_directory) {
   errno = 0;
   char* p = realpath("/dev/null/.", nullptr);
@@ -350,6 +364,47 @@
   free(p);
 }
 
+TEST(stdlib, realpath__dot) {
+  char* p = realpath("/proc/./version", nullptr);
+  ASSERT_STREQ("/proc/version", p);
+  free(p);
+}
+
+TEST(stdlib, realpath__dot_dot) {
+  char* p = realpath("/dev/../proc/version", nullptr);
+  ASSERT_STREQ("/proc/version", p);
+  free(p);
+}
+
+TEST(stdlib, realpath__deleted) {
+  TemporaryDir td;
+
+  // Create a file "A".
+  std::string A_path = td.path + "/A"s;
+  ASSERT_TRUE(android::base::WriteStringToFile("test\n", A_path));
+
+  // Get an O_PATH fd for it.
+  android::base::unique_fd fd(open(A_path.c_str(), O_PATH));
+  ASSERT_NE(fd, -1);
+
+  // Create a file "A (deleted)".
+  android::base::unique_fd fd2(open((td.path + "/A (deleted)"s).c_str(),
+                                    O_CREAT | O_TRUNC | O_WRONLY, 0644));
+  ASSERT_NE(fd2, -1);
+
+  // Delete "A".
+  ASSERT_EQ(0, unlink(A_path.c_str()));
+
+  // Now realpath() on the O_PATH fd, and check we *don't* get "A (deleted)".
+  std::string path = android::base::StringPrintf("/proc/%d/fd/%d", static_cast<int>(getpid()),
+                                                 fd.get());
+  errno = 0;
+  char* result = realpath(path.c_str(), nullptr);
+  ASSERT_EQ(nullptr, result) << result;
+  ASSERT_EQ(ENOENT, errno);
+  free(result);
+}
+
 TEST(stdlib, qsort) {
   struct s {
     char name[16];
@@ -871,3 +926,24 @@
   ASSERT_TRUE(fabs(expected[1] - load[1]) < 0.5) << expected[1] << ' ' << load[1];
   ASSERT_TRUE(fabs(expected[2] - load[2]) < 0.5) << expected[2] << ' ' << load[2];
 }
+
+TEST(stdlib, getprogname) {
+#if defined(__GLIBC__)
+  GTEST_SKIP() << "glibc doesn't have getprogname()";
+#else
+  // You should always have a name.
+  ASSERT_TRUE(getprogname() != nullptr);
+  // The name should never have a slash in it.
+  ASSERT_TRUE(strchr(getprogname(), '/') == nullptr);
+#endif
+}
+
+TEST(stdlib, setprogname) {
+#if defined(__GLIBC__)
+  GTEST_SKIP() << "glibc doesn't have setprogname()";
+#else
+  // setprogname() only takes the basename of what you give it.
+  setprogname("/usr/bin/muppet");
+  ASSERT_STREQ("muppet", getprogname());
+#endif
+}