Merge "Remove unnecessary indirection for execve() and _exit()/_Exit()." into main
diff --git a/libc/include/dlfcn.h b/libc/include/dlfcn.h
index a90c4f8..d65a409 100644
--- a/libc/include/dlfcn.h
+++ b/libc/include/dlfcn.h
@@ -146,7 +146,11 @@
  */
 #define RTLD_LOCAL    0
 
-/** Not supported on Android; Android always uses RTLD_NOW. */
+/**
+ * Not supported on Android. Android always uses RTLD_NOW for security reasons.
+ * Resolving all undefined symbols before dlopen() returns means that RELRO
+ * protections can be applied to the PLT before dlopen() returns.
+ */
 #define RTLD_LAZY     0x00001
 
 /** A dlopen() flag to resolve all undefined symbols before dlopen() returns. */
diff --git a/libc/malloc_debug/Config.cpp b/libc/malloc_debug/Config.cpp
index 0d442b4..6be899d 100644
--- a/libc/malloc_debug/Config.cpp
+++ b/libc/malloc_debug/Config.cpp
@@ -212,6 +212,10 @@
         "log_allocator_stats_on_signal",
         {LOG_ALLOCATOR_STATS_ON_SIGNAL, &Config::VerifyValueEmpty},
     },
+    {
+        "log_allocator_stats_on_exit",
+        {LOG_ALLOCATOR_STATS_ON_EXIT, &Config::VerifyValueEmpty},
+    },
 };
 
 bool Config::ParseValue(const std::string& option, const std::string& value, size_t min_value,
diff --git a/libc/malloc_debug/Config.h b/libc/malloc_debug/Config.h
index 8551712..4840d43 100644
--- a/libc/malloc_debug/Config.h
+++ b/libc/malloc_debug/Config.h
@@ -49,6 +49,7 @@
 constexpr uint64_t CHECK_UNREACHABLE_ON_SIGNAL = 0x2000;
 constexpr uint64_t BACKTRACE_SPECIFIC_SIZES = 0x4000;
 constexpr uint64_t LOG_ALLOCATOR_STATS_ON_SIGNAL = 0x8000;
+constexpr uint64_t LOG_ALLOCATOR_STATS_ON_EXIT = 0x10000;
 
 // In order to guarantee posix compliance, set the minimum alignment
 // to 8 bytes for 32 bit systems and 16 bytes for 64 bit systems.
diff --git a/libc/malloc_debug/LogAllocatorStats.cpp b/libc/malloc_debug/LogAllocatorStats.cpp
index 6d1434e..ee6bfdf 100644
--- a/libc/malloc_debug/LogAllocatorStats.cpp
+++ b/libc/malloc_debug/LogAllocatorStats.cpp
@@ -43,13 +43,17 @@
   g_call_mallopt = true;
 }
 
+void Log() {
+  info_log("Logging allocator stats...");
+  if (mallopt(M_LOG_STATS, 0) == 0) {
+    error_log("mallopt(M_LOG_STATS, 0) call failed.");
+  }
+}
+
 void CheckIfShouldLog() {
   bool expected = true;
   if (g_call_mallopt.compare_exchange_strong(expected, false)) {
-    info_log("Logging allocator stats...");
-    if (mallopt(M_LOG_STATS, 0) == 0) {
-      error_log("mallopt(M_LOG_STATS, 0) call failed.");
-    }
+    Log();
   }
 }
 
diff --git a/libc/malloc_debug/LogAllocatorStats.h b/libc/malloc_debug/LogAllocatorStats.h
index 99e0738..ded4f94 100644
--- a/libc/malloc_debug/LogAllocatorStats.h
+++ b/libc/malloc_debug/LogAllocatorStats.h
@@ -35,6 +35,8 @@
 
 bool Initialize(const Config& config);
 
+void Log();
+
 void CheckIfShouldLog();
 
 }  // namespace LogAllocatorStats
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index 6d88092..3743852 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -461,6 +461,10 @@
                                                 getpid()).c_str());
   }
 
+  if (g_debug->config().options() & LOG_ALLOCATOR_STATS_ON_EXIT) {
+    LogAllocatorStats::Log();
+  }
+
   backtrace_shutdown();
 
   // In order to prevent any issues of threads freeing previous pointers
diff --git a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
index c79d052..d33f9cd 100644
--- a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
@@ -861,6 +861,24 @@
   ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
 }
 
+TEST_F(MallocDebugConfigTest, log_allocator_stats_on_exit) {
+  ASSERT_TRUE(InitConfig("log_allocator_stats_on_exit")) << getFakeLogPrint();
+  ASSERT_EQ(LOG_ALLOCATOR_STATS_ON_EXIT, config->options());
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, trigger_log_allocator_stats_on_exit_fail) {
+  ASSERT_FALSE(InitConfig("log_allocator_stats_on_exit=200")) << getFakeLogPrint();
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string log_msg(
+      "6 malloc_debug malloc_testing: value set for option 'log_allocator_stats_on_exit' "
+      "which does not take a value\n");
+  ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
 TEST_F(MallocDebugConfigTest, size) {
   ASSERT_TRUE(InitConfig("backtrace_size=37")) << getFakeLogPrint();
   ASSERT_EQ(BACKTRACE_SPECIFIC_SIZES, config->options());
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index ef8d235..c808dc0 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -426,7 +426,7 @@
   Init(
       "guard backtrace backtrace_enable_on_signal fill expand_alloc free_track leak_track "
       "record_allocs verify_pointers abort_on_error verbose check_unreachable_on_signal "
-      "log_allocator_stats_on_signal");
+      "log_allocator_stats_on_signal log_allocator_stats_on_exit");
   VerifyAllocCalls(true);
 }
 
@@ -2844,6 +2844,25 @@
   }
 }
 
+TEST_F(MallocDebugTest, log_allocator_stats_on_exit) {
+  Init("log_allocator_stats_on_exit");
+
+  void* pointer = debug_malloc(110);
+  ASSERT_TRUE(pointer != nullptr);
+  debug_free(pointer);
+
+  debug_finalize();
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  if (!running_with_hwasan()) {
+    // Do an exact match because the mallopt should not fail in normal operation.
+    ASSERT_STREQ("4 malloc_debug Logging allocator stats...\n", getFakeLogPrint().c_str());
+  } else {
+    // mallopt fails with hwasan, so just verify that the message is present.
+    ASSERT_MATCH(getFakeLogPrint(), "4 malloc_debug Logging allocator stats...\\n");
+  }
+}
+
 TEST_F(MallocDebugTest, backtrace_only_some_sizes_with_backtrace_size) {
   Init("leak_track backtrace backtrace_size=120");
 
diff --git a/libm/significandl.c b/libm/significandl.c
index c5d7dd4..b672110 100644
--- a/libm/significandl.c
+++ b/libm/significandl.c
@@ -22,11 +22,13 @@
  * 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 <math.h>
 
+// This function is only in glibc.
+// musl and NetBSD/OpenBSD just have the double and float variants,
+// while FreeBSD and iOS/macOS have none.
 long double significandl(long double x) {
   return scalbnl(x, -ilogbl(x));
 }
diff --git a/linker/Android.bp b/linker/Android.bp
index 694d1f5..563cf3d 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -14,6 +14,16 @@
     ],
 }
 
+linker_common_flags = [
+    "-fno-stack-protector",
+    "-Wstrict-overflow=5",
+    "-fvisibility=hidden",
+    "-Wall",
+    "-Wextra",
+    "-Wunused",
+    "-Werror",
+]
+
 // ========================================================
 // linker_wrapper - Linux Bionic (on the host)
 // ========================================================
@@ -36,15 +46,7 @@
         },
     },
 
-    cflags: [
-        "-fno-stack-protector",
-        "-Wstrict-overflow=5",
-        "-fvisibility=hidden",
-        "-Wall",
-        "-Wextra",
-        "-Wno-unused",
-        "-Werror",
-    ],
+    cflags: linker_common_flags,
 
     srcs: [
         "linker_wrapper.cpp",
@@ -83,26 +85,8 @@
         },
     },
 
-    cflags: [
-        "-fno-stack-protector",
-        "-Wstrict-overflow=5",
-        "-fvisibility=hidden",
-        "-Wall",
-        "-Wextra",
-        "-Wunused",
-        "-Werror",
-    ],
-
-    // TODO: split out the asflags.
-    asflags: [
-        "-fno-stack-protector",
-        "-Wstrict-overflow=5",
-        "-fvisibility=hidden",
-        "-Wall",
-        "-Wextra",
-        "-Wunused",
-        "-Werror",
-    ],
+    cflags: linker_common_flags,
+    asflags: linker_common_flags,
 
     product_variables: {
         debuggable: {
diff --git a/linker/linker_debug.cpp b/linker/linker_debug.cpp
index b0aae79..e6211f7 100644
--- a/linker/linker_debug.cpp
+++ b/linker/linker_debug.cpp
@@ -30,13 +30,14 @@
 
 #include <unistd.h>
 
-void linker_log_va_list(int prio __unused, const char* fmt, va_list ap) {
-#if LINKER_DEBUG_TO_LOG
-  async_safe_format_log_va_list(5 - prio, "linker", fmt, ap);
-#else
-  async_safe_format_fd_va_list(STDOUT_FILENO, fmt, ap);
-  write(STDOUT_FILENO, "\n", 1);
-#endif
+void linker_log_va_list(int prio, const char* fmt, va_list ap) {
+  va_list ap2;
+  va_copy(ap2, ap);
+  async_safe_format_log_va_list(5 - prio, "linker", fmt, ap2);
+  va_end(ap2);
+
+  async_safe_format_fd_va_list(STDERR_FILENO, fmt, ap);
+  write(STDERR_FILENO, "\n", 1);
 }
 
 void linker_log(int prio, const char* fmt, ...) {
diff --git a/linker/linker_debug.h b/linker/linker_debug.h
index 477b009..3aab185 100644
--- a/linker/linker_debug.h
+++ b/linker/linker_debug.h
@@ -33,11 +33,6 @@
 // INFO, TRACE, and DEBUG calls in the source). This will only
 // affect new processes being launched.
 
-// By default, traces are sent to logcat, with the "linker" tag. You can
-// change this to go to stdout instead by setting the definition of
-// LINKER_DEBUG_TO_LOG to 0.
-#define LINKER_DEBUG_TO_LOG  1
-
 #define TRACE_DEBUG          1
 #define DO_TRACE_LOOKUP      1
 #define DO_TRACE_RELO        1
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index 2b230a8..a625af7 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -245,6 +245,7 @@
 
   va_start(ap, fmt);
   async_safe_format_fd_va_list(STDERR_FILENO, fmt, ap);
+  write(STDERR_FILENO, "\n", 1);
   va_end(ap);
 
   va_start(ap, fmt);
@@ -255,7 +256,7 @@
 }
 
 static void __linker_cannot_link(const char* argv0) {
-  __linker_error("CANNOT LINK EXECUTABLE \"%s\": %s\n",
+  __linker_error("CANNOT LINK EXECUTABLE \"%s\": %s",
                  argv0,
                  linker_get_error_buffer());
 }
@@ -267,26 +268,26 @@
   ExecutableInfo result = {};
 
   if (orig_path[0] != '/') {
-    __linker_error("error: expected absolute path: \"%s\"\n", orig_path);
+    __linker_error("error: expected absolute path: \"%s\"", orig_path);
   }
 
   off64_t file_offset;
   android::base::unique_fd fd(open_executable(orig_path, &file_offset, &result.path));
   if (fd.get() == -1) {
-    __linker_error("error: unable to open file \"%s\"\n", orig_path);
+    __linker_error("error: unable to open file \"%s\"", orig_path);
   }
 
   if (TEMP_FAILURE_RETRY(fstat(fd.get(), &result.file_stat)) == -1) {
-    __linker_error("error: unable to stat \"%s\": %s\n", result.path.c_str(), strerror(errno));
+    __linker_error("error: unable to stat \"%s\": %m", result.path.c_str());
   }
 
   ElfReader elf_reader;
   if (!elf_reader.Read(result.path.c_str(), fd.get(), file_offset, result.file_stat.st_size)) {
-    __linker_error("error: %s\n", linker_get_error_buffer());
+    __linker_error("error: %s", linker_get_error_buffer());
   }
   address_space_params address_space;
   if (!elf_reader.Load(&address_space)) {
-    __linker_error("error: %s\n", linker_get_error_buffer());
+    __linker_error("error: %s", linker_get_error_buffer());
   }
 
   result.phdr = elf_reader.loaded_phdr();
@@ -333,11 +334,7 @@
 
   if (getenv("LD_SHOW_AUXV") != nullptr) ld_show_auxv(args.auxv);
 
-#if defined(__LP64__)
-  INFO("[ Android dynamic linker (64-bit) ]");
-#else
-  INFO("[ Android dynamic linker (32-bit) ]");
-#endif
+  INFO("[ Android dynamic linker (" ABI_STRING ") ]");
 
   // These should have been sanitized by __libc_init_AT_SECURE, but the test
   // doesn't cost us anything.
@@ -401,8 +398,7 @@
     if (note_gnu_property.IsBTICompatible() &&
         (phdr_table_protect_segments(somain->phdr, somain->phnum, somain->load_bias,
                                      somain->should_pad_segments(), &note_gnu_property) < 0)) {
-      __linker_error("error: can't protect segments for \"%s\": %s", exe_info.path.c_str(),
-                     strerror(errno));
+      __linker_error("error: can't protect segments for \"%s\": %m", exe_info.path.c_str());
     }
   }
 #endif
@@ -427,7 +423,7 @@
   // and the NDK no longer supports earlier API levels.
   if (elf_hdr->e_type != ET_DYN) {
     __linker_error("error: %s: Android only supports position-independent "
-                   "executables (-fPIE)\n", exe_info.path.c_str());
+                   "executables (-fPIE)", exe_info.path.c_str());
   }
 
   // Use LD_LIBRARY_PATH and LD_PRELOAD (but only if we aren't setuid/setgid).
@@ -695,7 +691,7 @@
   // fallback.
   __libc_sysinfo = reinterpret_cast<void*>(__libc_int0x80);
 #endif
-  __linker_error("error: linker cannot load itself\n");
+  __linker_error("error: linker cannot load itself");
 }
 
 static ElfW(Addr) __attribute__((noinline))
diff --git a/linker/linker_relocate.cpp b/linker/linker_relocate.cpp
index 3e36114..5f993ba 100644
--- a/linker/linker_relocate.cpp
+++ b/linker/linker_relocate.cpp
@@ -627,7 +627,7 @@
         android_relocs_[1] == 'P' &&
         android_relocs_[2] == 'S' &&
         android_relocs_[3] == '2') {
-      DEBUG("[ android relocating %s ]", get_realpath());
+      DEBUG("[ relocating %s android rel/rela ]", get_realpath());
 
       const uint8_t* packed_relocs = android_relocs_ + 4;
       const size_t packed_relocs_size = android_relocs_size_ - 4;
diff --git a/tests/ctype_test.cpp b/tests/ctype_test.cpp
index 826d39a..18fbfc0 100644
--- a/tests/ctype_test.cpp
+++ b/tests/ctype_test.cpp
@@ -293,40 +293,57 @@
 }
 
 TEST(ctype, toascii) {
-  EXPECT_EQ('a', toascii('a'));
-  EXPECT_EQ('a', toascii(0x80 | 'a'));
+  // POSIX explicitly says that toascii() returns (c & 0x7f),
+  // so there's no EOF-preserving behavior here and we start from 0.
+  for (int i = 0; i < kMax; ++i) {
+    if (i <= 0x7f) {
+      EXPECT_EQ(i, toascii(i));
+    } else {
+      EXPECT_EQ(i & 0x7f, toascii(i));
+    }
+  }
 }
 
 TEST(ctype, tolower) {
   EXPECT_EQ('!', tolower('!'));
   EXPECT_EQ('a', tolower('a'));
   EXPECT_EQ('a', tolower('A'));
+  EXPECT_EQ('z', tolower('z'));
+  EXPECT_EQ('z', tolower('Z'));
 }
 
 TEST(ctype, tolower_l) {
   EXPECT_EQ('!', tolower_l('!', LC_GLOBAL_LOCALE));
   EXPECT_EQ('a', tolower_l('a', LC_GLOBAL_LOCALE));
   EXPECT_EQ('a', tolower_l('A', LC_GLOBAL_LOCALE));
+  EXPECT_EQ('z', tolower_l('z', LC_GLOBAL_LOCALE));
+  EXPECT_EQ('z', tolower_l('Z', LC_GLOBAL_LOCALE));
 }
 
 TEST(ctype, _tolower) {
   // _tolower may mangle characters for which isupper is false.
   EXPECT_EQ('a', _tolower('A'));
+  EXPECT_EQ('z', _tolower('Z'));
 }
 
 TEST(ctype, toupper) {
   EXPECT_EQ('!', toupper('!'));
   EXPECT_EQ('A', toupper('a'));
   EXPECT_EQ('A', toupper('A'));
+  EXPECT_EQ('Z', toupper('z'));
+  EXPECT_EQ('Z', toupper('Z'));
 }
 
 TEST(ctype, toupper_l) {
   EXPECT_EQ('!', toupper_l('!', LC_GLOBAL_LOCALE));
   EXPECT_EQ('A', toupper_l('a', LC_GLOBAL_LOCALE));
   EXPECT_EQ('A', toupper_l('A', LC_GLOBAL_LOCALE));
+  EXPECT_EQ('Z', toupper_l('z', LC_GLOBAL_LOCALE));
+  EXPECT_EQ('Z', toupper_l('Z', LC_GLOBAL_LOCALE));
 }
 
 TEST(ctype, _toupper) {
   // _toupper may mangle characters for which islower is false.
   EXPECT_EQ('A', _toupper('a'));
+  EXPECT_EQ('Z', _toupper('z'));
 }
diff --git a/tests/prebuilt-elf-files/arm64/libtest_empty.so b/tests/prebuilt-elf-files/arm64/libtest_empty.so
index d8775b6..76c569b 100755
--- a/tests/prebuilt-elf-files/arm64/libtest_empty.so
+++ b/tests/prebuilt-elf-files/arm64/libtest_empty.so
Binary files differ
diff --git a/tests/prebuilt-elf-files/x86_64/libtest_empty.so b/tests/prebuilt-elf-files/x86_64/libtest_empty.so
index c3f3638..ce519b2 100755
--- a/tests/prebuilt-elf-files/x86_64/libtest_empty.so
+++ b/tests/prebuilt-elf-files/x86_64/libtest_empty.so
Binary files differ
diff --git a/tests/wctype_test.cpp b/tests/wctype_test.cpp
index 8a254bc..f4b7a8f 100644
--- a/tests/wctype_test.cpp
+++ b/tests/wctype_test.cpp
@@ -109,6 +109,8 @@
   EXPECT_EQ(wint_t('!'), towlower(L'!'));
   EXPECT_EQ(wint_t('a'), towlower(L'a'));
   EXPECT_EQ(wint_t('a'), towlower(L'A'));
+  EXPECT_EQ(wint_t('z'), towlower(L'z'));
+  EXPECT_EQ(wint_t('z'), towlower(L'Z'));
   if (have_dl()) {
     EXPECT_EQ(wint_t(L'ç'), towlower(L'ç'));
     EXPECT_EQ(wint_t(L'ç'), towlower(L'Ç'));
@@ -125,6 +127,8 @@
   EXPECT_EQ(wint_t('!'), towlower_l(L'!', l.l));
   EXPECT_EQ(wint_t('a'), towlower_l(L'a', l.l));
   EXPECT_EQ(wint_t('a'), towlower_l(L'A', l.l));
+  EXPECT_EQ(wint_t('z'), towlower_l(L'z', l.l));
+  EXPECT_EQ(wint_t('z'), towlower_l(L'Z', l.l));
   if (have_dl()) {
     EXPECT_EQ(wint_t(L'ç'), towlower_l(L'ç', l.l));
     EXPECT_EQ(wint_t(L'ç'), towlower_l(L'Ç', l.l));
@@ -140,6 +144,8 @@
   EXPECT_EQ(wint_t('!'), towupper(L'!'));
   EXPECT_EQ(wint_t('A'), towupper(L'a'));
   EXPECT_EQ(wint_t('A'), towupper(L'A'));
+  EXPECT_EQ(wint_t('Z'), towupper(L'z'));
+  EXPECT_EQ(wint_t('Z'), towupper(L'Z'));
   if (have_dl()) {
     EXPECT_EQ(wint_t(L'Ç'), towupper(L'ç'));
     EXPECT_EQ(wint_t(L'Ç'), towupper(L'Ç'));
@@ -156,6 +162,8 @@
   EXPECT_EQ(wint_t('!'), towupper_l(L'!', l.l));
   EXPECT_EQ(wint_t('A'), towupper_l(L'a', l.l));
   EXPECT_EQ(wint_t('A'), towupper_l(L'A', l.l));
+  EXPECT_EQ(wint_t('Z'), towupper_l(L'z', l.l));
+  EXPECT_EQ(wint_t('Z'), towupper_l(L'Z', l.l));
   if (have_dl()) {
     EXPECT_EQ(wint_t(L'Ç'), towupper_l(L'ç', l.l));
     EXPECT_EQ(wint_t(L'Ç'), towupper_l(L'Ç', l.l));