Merge "Revert "Workaround app compat issue introduced by global ThinLTO optimization"" into main
diff --git a/benchmarks/README.md b/benchmarks/README.md
index 3819b1a..6b6c448 100644
--- a/benchmarks/README.md
+++ b/benchmarks/README.md
@@ -19,6 +19,12 @@
By default, `bionic-benchmarks` runs all of the benchmarks in alphabetical order. Pass
`--benchmark_filter=getpid` to run just the benchmarks with "getpid" in their name.
+Note that we also build _static_ benchmark binaries.
+They're useful for testing on devices running different versions of Android, or running non-Android OSes.
+Those binaries are called `bionic-benchmarks-static` instead.
+Copy from `out/target/product/<device>/symbols/data/benchmarktest64/bionic-benchmarks-static` instead of
+`out/target/product/<device>/data/benchmarktest64/bionic-benchmarks-static` if you want symbols for perf(1).
+
### Host benchmarks
See the `benchmarks/run-on-host.sh` script. The host benchmarks can be run with 32-bit or 64-bit
diff --git a/docs/status.md b/docs/status.md
index e0364a8..7ebd195 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -283,23 +283,31 @@
libc function count over time:
-| OS | API level | Function count |
-|-------|-----------|----------------|
-| J | 16 | 842 |
-| J MR1 | 17 | 870 |
-| J MR2 | 18 | 878 |
-| K | 19 | 893 |
-| L | 21 | 1118 |
-| M | 23 | 1183 |
-| N | 24 | 1228 |
-| O | 26 | 1280 |
-| P | 28 | 1378 |
-| Q | 29 | 1394 |
+| API level | Function count |
+|-----------|----------------|
+| 16 | 842 |
+| 17 | 870 |
+| 18 | 878 |
+| 19 | 893 |
+| 21 | 1016 |
+| 22 | 1038 |
+| 23 | 1103 |
+| 24 | 1147 |
+| 25 | 1147 |
+| 26 | 1199 |
+| 27 | 1199 |
+| 28 | 1298 |
+| 29 | 1312 |
+| 30 | 1368 |
+| 31 | 1379 |
+| 32 | 1379 |
+| 33 | 1386 |
+| 34 | 1392 |
Data collected by:
```
-ndk-r21$ for i in `ls -1v platforms/android-*/arch-arm/usr/lib/libc.so` ; do \
- echo $i; nm $i | grep -w T | wc -l ; done
+ndk-r26c$ for i in `ls -1v toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/*/libc.so` ; \
+ do echo $i; nm $i | grep -w T | wc -l ; done
```
### libm
diff --git a/libc/bionic/wctype.cpp b/libc/bionic/wctype.cpp
index be0f878..b37f17b 100644
--- a/libc/bionic/wctype.cpp
+++ b/libc/bionic/wctype.cpp
@@ -109,10 +109,7 @@
}
wint_t towlower(wint_t wc) {
- if (wc < 0x80) {
- if (wc >= 'A' && wc <= 'Z') return wc | 0x20;
- return wc;
- }
+ if (wc < 0x80) return tolower(wc);
typedef UChar32 (*FnT)(UChar32);
static auto u_tolower = reinterpret_cast<FnT>(__find_icu_symbol("u_tolower"));
@@ -120,12 +117,7 @@
}
wint_t towupper(wint_t wc) {
- if (wc < 0x80) {
- // Using EOR rather than AND makes no difference on arm, but saves an
- // instruction on arm64.
- if (wc >= 'a' && wc <= 'z') return wc ^ 0x20;
- return wc;
- }
+ if (wc < 0x80) return toupper(wc);
typedef UChar32 (*FnT)(UChar32);
static auto u_toupper = reinterpret_cast<FnT>(__find_icu_symbol("u_toupper"));
@@ -158,6 +150,7 @@
wctrans_t wctrans(const char* name) {
if (strcmp(name, "tolower") == 0) return wctrans_tolower;
if (strcmp(name, "toupper") == 0) return wctrans_toupper;
+ errno = EINVAL;
return nullptr;
}
@@ -169,7 +162,7 @@
if (t == wctrans_tolower) return towlower(c);
if (t == wctrans_toupper) return towupper(c);
errno = EINVAL;
- return 0;
+ return c;
}
wint_t towctrans_l(wint_t c, wctrans_t t, locale_t) {
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..0bc7003 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -333,11 +333,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.
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/Android.bp b/tests/Android.bp
index ee34666..d2a3110 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -361,8 +361,7 @@
cc_test_library {
name: "clang_diagnostic_tests",
cflags: [
- "-Xclang",
- "-verify",
+ "-Xclang -verify",
],
srcs: ["sys_ioctl_diag_test.cpp"],
}
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 0f07956..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));
@@ -218,34 +226,64 @@
EXPECT_EQ(0, iswctype_l(WEOF, wctype_l("alnum", l.l), l.l));
}
-TEST(wctype, towctrans) {
+TEST(wctype, wctrans) {
EXPECT_TRUE(wctrans("tolower") != nullptr);
EXPECT_TRUE(wctrans("toupper") != nullptr);
+ errno = 0;
EXPECT_TRUE(wctrans("monkeys") == nullptr);
-}
-
-TEST(wctype, towctrans_l) {
- UtfLocale l;
- EXPECT_TRUE(wctrans_l("tolower", l.l) != nullptr);
- EXPECT_TRUE(wctrans_l("toupper", l.l) != nullptr);
-
- EXPECT_TRUE(wctrans_l("monkeys", l.l) == nullptr);
-}
-
-TEST(wctype, wctrans) {
- EXPECT_EQ(wint_t('a'), towctrans(L'A', wctrans("tolower")));
- EXPECT_EQ(WEOF, towctrans(WEOF, wctrans("tolower")));
-
- EXPECT_EQ(wint_t('A'), towctrans(L'a', wctrans("toupper")));
- EXPECT_EQ(WEOF, towctrans(WEOF, wctrans("toupper")));
+ #if defined(__BIONIC__)
+ // Android/FreeBSD/iOS set errno, but musl/glibc don't.
+ EXPECT_ERRNO(EINVAL);
+ #endif
}
TEST(wctype, wctrans_l) {
UtfLocale l;
- EXPECT_EQ(wint_t('a'), towctrans_l(L'A', wctrans_l("tolower", l.l), l.l));
- EXPECT_EQ(WEOF, towctrans_l(WEOF, wctrans_l("tolower", l.l), l.l));
+ EXPECT_TRUE(wctrans_l("tolower", l.l) != nullptr);
+ EXPECT_TRUE(wctrans_l("toupper", l.l) != nullptr);
- EXPECT_EQ(wint_t('A'), towctrans_l(L'a', wctrans_l("toupper", l.l), l.l));
- EXPECT_EQ(WEOF, towctrans_l(WEOF, wctrans_l("toupper", l.l), l.l));
+ errno = 0;
+ EXPECT_TRUE(wctrans_l("monkeys", l.l) == nullptr);
+ #if defined(__BIONIC__)
+ // Android/FreeBSD/iOS set errno, but musl/glibc don't.
+ EXPECT_ERRNO(EINVAL);
+ #endif
+}
+
+TEST(wctype, towctrans) {
+ wctrans_t lower = wctrans("tolower");
+ EXPECT_EQ(wint_t('a'), towctrans(L'A', lower));
+ EXPECT_EQ(WEOF, towctrans(WEOF, lower));
+
+ wctrans_t upper = wctrans("toupper");
+ EXPECT_EQ(wint_t('A'), towctrans(L'a', upper));
+ EXPECT_EQ(WEOF, towctrans(WEOF, upper));
+
+ wctrans_t invalid = wctrans("monkeys");
+ errno = 0;
+ EXPECT_EQ(wint_t('a'), towctrans(L'a', invalid));
+ #if defined(__BIONIC__)
+ // Android/FreeBSD/iOS set errno, but musl/glibc don't.
+ EXPECT_ERRNO(EINVAL);
+ #endif
+}
+
+TEST(wctype, towctrans_l) {
+ UtfLocale l;
+ wctrans_t lower = wctrans_l("tolower", l.l);
+ EXPECT_EQ(wint_t('a'), towctrans_l(L'A', lower, l.l));
+ EXPECT_EQ(WEOF, towctrans_l(WEOF, lower, l.l));
+
+ wctrans_t upper = wctrans_l("toupper", l.l);
+ EXPECT_EQ(wint_t('A'), towctrans_l(L'a', upper, l.l));
+ EXPECT_EQ(WEOF, towctrans_l(WEOF, upper, l.l));
+
+ wctrans_t invalid = wctrans_l("monkeys", l.l);
+ errno = 0;
+ EXPECT_EQ(wint_t('a'), towctrans_l(L'a', invalid, l.l));
+ #if defined(__BIONIC__)
+ // Android/FreeBSD/iOS set errno, but musl/glibc don't.
+ EXPECT_ERRNO(EINVAL);
+ #endif
}