diff --git a/include/compat/bionic_host_libc_runtime.h b/include/compat/bionic_host_libc_runtime.h index be55994..0a363ff 100644 --- a/include/compat/bionic_host_libc_runtime.h +++ b/include/compat/bionic_host_libc_runtime.h @@ -11,6 +11,14 @@ namespace mocktail::compat { +// glibc's atoi is (int)strtol(), exactly like Bionic's, so the host symbol is +// used as-is there. Other libcs (musl) differ on overflow and need the shim. +#ifdef __GLIBC__ +inline constexpr bool kHostAtoiMatchesBionic = true; +#else +inline constexpr bool kHostAtoiMatchesBionic = false; +#endif + // Android uses size_t fields here; glibc uses int and musl has no equivalent. // An all-zero fallback means unknown, not zero usage. struct BionicMallinfoSnapshot { diff --git a/src/compat/bionic_host_libc_runtime.cc b/src/compat/bionic_host_libc_runtime.cc index d1d6cd6..396b6ba 100644 --- a/src/compat/bionic_host_libc_runtime.cc +++ b/src/compat/bionic_host_libc_runtime.cc @@ -150,6 +150,15 @@ Result ParseInHostCLocale(Parser parser) noexcept { return result; } +// Highest errno in Bionic's message table (EHWPOISON). Numbers 41 and 58 are +// unassigned in the generic Linux errno list, so Bionic has no text for them. +constexpr int kBionicMaxErrno = 133; + +constexpr bool HasBionicErrnoMessage(int error_number) noexcept { + return error_number >= 0 && error_number <= kBionicMaxErrno && + error_number != 41 && error_number != 58; +} + template const char* HostStrErrorResult(Result result, char* buffer) noexcept { if constexpr (std::is_integral_v) { @@ -318,18 +327,19 @@ int BionicStrError(int error_number, char* buffer, return -1; } + // Bionic's table ends at EHWPOISON. Unknown numbers get the same text on + // every host instead of whatever the host libc prints (musl has no + // "Unknown error N" form). std::array host_buffer{}; - auto host_result = - ::strerror_r(error_number, host_buffer.data(), host_buffer.size()); - const char* message = HostStrErrorResult(host_result, host_buffer.data()); + const char* message = nullptr; + if (HasBionicErrnoMessage(error_number)) { + auto host_result = + ::strerror_r(error_number, host_buffer.data(), host_buffer.size()); + message = HostStrErrorResult(host_result, host_buffer.data()); + } - std::array unknown_buffer{}; - const char* unknown_message = HostStrErrorResult( - ::strerror_r(-1, unknown_buffer.data(), unknown_buffer.size()), - unknown_buffer.data()); - if (message == nullptr || - (error_number != 0 && unknown_message != nullptr && - std::strcmp(message, unknown_message) == 0)) { + std::array unknown_buffer{}; + if (message == nullptr) { std::snprintf(unknown_buffer.data(), unknown_buffer.size(), "Unknown error %d", error_number); message = unknown_buffer.data(); diff --git a/src/linker/linker.cc b/src/linker/linker.cc index e8461cb..1ae1d22 100644 --- a/src/linker/linker.cc +++ b/src/linker/linker.cc @@ -134,7 +134,8 @@ bool IsBionicLibcOnlySymbol(std::string_view symbol) { } return symbol == "__cmsg_nxthdr" || symbol == "__cxa_thread_atexit_impl" || symbol == "__readlink_chk" || symbol == "__register_atfork" || - symbol == "arc4random_buf" || symbol == "atoi" || symbol == "geteuid" || + symbol == "arc4random_buf" || symbol == "geteuid" || + (!mocktail::compat::kHostAtoiMatchesBionic && symbol == "atoi") || symbol == "getuid" || symbol == "lseek64" || symbol == "mallinfo" || symbol == "pread64" || symbol == "pwrite64" || symbol == "sigaction" || symbol == "strerror_r" || @@ -384,8 +385,10 @@ void RegisterBionicHostLibcRuntimeForLibc() { RegisterSyntheticSymbol("libc.so", "arc4random_buf", reinterpret_cast( mocktail_bionic_arc4random_buf)); - RegisterSyntheticSymbol("libc.so", "atoi", - reinterpret_cast(mocktail_bionic_atoi)); + if (!mocktail::compat::kHostAtoiMatchesBionic) { + RegisterSyntheticSymbol("libc.so", "atoi", + reinterpret_cast(mocktail_bionic_atoi)); + } RegisterSyntheticSymbol("libc.so", "mallinfo", reinterpret_cast(mocktail_bionic_mallinfo)); RegisterSyntheticSymbol("libc.so", "sysinfo", diff --git a/tests/bionic_host_libc_runtime_test.cc b/tests/bionic_host_libc_runtime_test.cc index 2809cb0..4f889c9 100644 --- a/tests/bionic_host_libc_runtime_test.cc +++ b/tests/bionic_host_libc_runtime_test.cc @@ -234,7 +234,7 @@ TEST(BionicHostLibcRuntimeTest, UsesBionicPosixStrErrorContract) { EXPECT_STREQ(buffer.data(), std::strerror(0)); EXPECT_EQ(errno, EDOM); - for (const int error_number : {-1, 4567, INT_MIN, INT_MAX}) { + for (const int error_number : {-1, 41, 58, 134, 4567, INT_MIN, INT_MAX}) { errno = EDOM; ASSERT_EQ(mocktail_bionic_strerror_r(error_number, buffer.data(), buffer.size()), 0); diff --git a/tests/linker_registry_test.cc b/tests/linker_registry_test.cc index 75816bd..a627369 100644 --- a/tests/linker_registry_test.cc +++ b/tests/linker_registry_test.cc @@ -130,8 +130,12 @@ TEST(LinkerRegistryTest, KeepsHostIndependentBionicAbiOwnedByLibc) { reinterpret_cast(mocktail___readlink_chk)); EXPECT_EQ(symbols.at("arc4random_buf"), reinterpret_cast(mocktail_bionic_arc4random_buf)); - EXPECT_EQ(symbols.at("atoi"), - reinterpret_cast(mocktail_bionic_atoi)); + if (mocktail::compat::kHostAtoiMatchesBionic) { + EXPECT_EQ(symbols.count("atoi"), 0u); + } else { + EXPECT_EQ(symbols.at("atoi"), + reinterpret_cast(mocktail_bionic_atoi)); + } EXPECT_EQ(symbols.at("mallinfo"), reinterpret_cast(mocktail_bionic_mallinfo)); EXPECT_EQ(symbols.at("sysinfo"),