diff --git a/base/allocator/dispatcher/tls.cc b/base/allocator/dispatcher/tls.cc index 652481067b7..6a849653fc6 100644 --- a/base/allocator/dispatcher/tls.cc +++ b/base/allocator/dispatcher/tls.cc @@ -13,7 +13,7 @@ #include -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) +#if BUILDFLAG(IS_ANDROID) || (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_STARBOARD)) #include #endif @@ -22,7 +22,7 @@ namespace base::allocator::dispatcher::internal { void* MMapAllocator::AllocateMemory(size_t size_in_bytes) { void* const mmap_res = mmap(nullptr, size_in_bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) +#if BUILDFLAG(IS_ANDROID) || (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_STARBOARD)) #if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME) if (mmap_res != MAP_FAILED) { // Allow the anonymous memory region allocated by mmap(MAP_ANONYMOUS) to diff --git a/base/allocator/partition_allocator/page_allocator_internals_posix.h b/base/allocator/partition_allocator/page_allocator_internals_posix.h index 30009c960a8..f0191daa5ad 100644 --- a/base/allocator/partition_allocator/page_allocator_internals_posix.h +++ b/base/allocator/partition_allocator/page_allocator_internals_posix.h @@ -35,7 +35,7 @@ #include #include #endif -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) +#if BUILDFLAG(IS_ANDROID) || (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_STARBOARD)) #include #endif #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) @@ -62,7 +62,7 @@ namespace partition_alloc::internal { namespace { -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) +#if BUILDFLAG(IS_ANDROID) || (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_STARBOARD)) #if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME) const char* PageTagToName(PageTag tag) { // Important: All the names should be string literals. As per prctl.h in @@ -86,7 +86,7 @@ const char* PageTagToName(PageTag tag) { } } #endif -#endif // BUILDFLAG(IS_ANDROID) +#endif // BUILDFLAG(IS_ANDROID) || (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_STARBOARD)) #if BUILDFLAG(IS_MAC) // Tests whether the version of macOS supports the MAP_JIT flag and if the @@ -197,7 +197,7 @@ uintptr_t SystemAllocPagesInternal(uintptr_t hint, ret = nullptr; } -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) +#if BUILDFLAG(IS_ANDROID) || (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_STARBOARD)) #if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME) // On Android and Linux, anonymous mappings can have a name attached to them. // This is useful for debugging, and double-checking memory attribution. diff --git a/base/memory/madv_free_discardable_memory_posix.cc b/base/memory/madv_free_discardable_memory_posix.cc index a72aee6cff5..f38bb0e2d1c 100644 --- a/base/memory/madv_free_discardable_memory_posix.cc +++ b/base/memory/madv_free_discardable_memory_posix.cc @@ -24,7 +24,7 @@ #include "base/tracing_buildflags.h" #include "build/build_config.h" -#if BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_STARBOARD) #include #endif @@ -47,7 +47,7 @@ void* AllocatePages(size_t size_in_pages) { MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); PCHECK(data != MAP_FAILED); -#if BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_STARBOARD) prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, data, length, "madv-free-discardable"); #endif diff --git a/base/metrics/persistent_memory_allocator.cc b/base/metrics/persistent_memory_allocator.cc index ec0e8528733..c19f33cb0f4 100644 --- a/base/metrics/persistent_memory_allocator.cc +++ b/base/metrics/persistent_memory_allocator.cc @@ -31,7 +31,7 @@ #include #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #include -#if BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_STARBOARD) #include #endif #endif @@ -992,7 +992,7 @@ LocalPersistentMemoryAllocator::AllocateLocalMemory(size_t size, address = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); if (address != MAP_FAILED) { -#if BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_STARBOARD) // Allow the anonymous memory region allocated by mmap(MAP_ANON) to be // identified in /proc/$PID/smaps. This helps improve visibility into // Chrome's memory usage on Android. diff --git a/starboard/elf_loader/exported_symbols.cc b/starboard/elf_loader/exported_symbols.cc index 0b0860b2d46..6699408fe86 100644 --- a/starboard/elf_loader/exported_symbols.cc +++ b/starboard/elf_loader/exported_symbols.cc @@ -59,6 +59,7 @@ #include "starboard/shared/modular/starboard_layer_posix_mmap_abi_wrappers.h" #include "starboard/shared/modular/starboard_layer_posix_pipe2_abi_wrappers.h" #include "starboard/shared/modular/starboard_layer_posix_poll_abi_wrappers.h" +#include "starboard/shared/modular/starboard_layer_posix_prctl_abi_wrappers.h" #include "starboard/shared/modular/starboard_layer_posix_pthread_abi_wrappers.h" #include "starboard/shared/modular/starboard_layer_posix_semaphore_abi_wrappers.h" #include "starboard/shared/modular/starboard_layer_posix_signal_abi_wrappers.h" @@ -230,6 +231,7 @@ ExportedSymbols::ExportedSymbols() { // POSIX APIs REGISTER_SYMBOL(aligned_alloc); + REGISTER_SYMBOL(atexit); REGISTER_SYMBOL(calloc); REGISTER_SYMBOL(close); REGISTER_SYMBOL(fdatasync); @@ -348,6 +350,7 @@ ExportedSymbols::ExportedSymbols() { REGISTER_WRAPPER(pathconf); REGISTER_WRAPPER(pipe2); REGISTER_WRAPPER(poll); + REGISTER_WRAPPER(prctl); REGISTER_WRAPPER(pthread_attr_init); REGISTER_WRAPPER(pthread_attr_destroy); REGISTER_WRAPPER(pthread_attr_getdetachstate); diff --git a/starboard/nplb/BUILD.gn b/starboard/nplb/BUILD.gn index 5edc3b4dae7..56da7f9df22 100644 --- a/starboard/nplb/BUILD.gn +++ b/starboard/nplb/BUILD.gn @@ -179,6 +179,7 @@ test("nplb") { "posix_compliance/posix_pipe_test.cc", "posix_compliance/posix_poll_test.cc", "posix_compliance/posix_posix_memory_allocate_aligned_test.cc", + "posix_compliance/posix_prctl_test.cc", "posix_compliance/posix_process_test.cc", "posix_compliance/posix_rand_r_test.cc", "posix_compliance/posix_rand_test.cc", diff --git a/starboard/nplb/posix_compliance/posix_prctl_test.cc b/starboard/nplb/posix_compliance/posix_prctl_test.cc new file mode 100644 index 00000000000..5f88658a0ae --- /dev/null +++ b/starboard/nplb/posix_compliance/posix_prctl_test.cc @@ -0,0 +1,153 @@ +// Copyright 2025 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard/shared/modular/starboard_layer_posix_prctl_abi_wrappers.h" + +#include +#include +#include +#include +#include +#include + +#include "starboard/common/log.h" +#include "testing/gtest/include/gtest/gtest.h" + +// From third_party/musl/include/sys/prctl.h +#ifndef PR_SET_VMA +#define PR_SET_VMA 0x53564d41 +#endif +#ifndef PR_SET_VMA_ANON_NAME +#define PR_SET_VMA_ANON_NAME 0 +#endif + +namespace { + +const char kVmaName[] = "TestVmaName"; + +// Checks /proc/self/maps to see if the memory mapping containing |addr| has +// the specified |name|. This is the expected outcome when the kernel supports +// PR_SET_VMA_ANON_NAME. +bool VmaIsNamed(void* addr, const char* name) { + FILE* fp = fopen("/proc/self/maps", "r"); + if (!fp) { + // If we can't open /proc/self/maps, we can't verify. + // Assume it's not named and let the fallback check proceed. + return false; + } + + char line[1024]; + bool found = false; + unsigned long target_addr = (unsigned long)addr; + + while (fgets(line, sizeof(line), fp)) { + unsigned long start, end; + if (sscanf(line, "%lx-%lx", &start, &end) != 2) { + continue; + } + if (target_addr >= start && target_addr < end) { + if (strstr(line, name)) { + found = true; + } + // We found the mapping containing our address, so we can stop. + // If it's not named here, it's not named. + break; + } + } + + fclose(fp); + return found; +} + +// Checks for the existence and content of the fallback file. This is the +// expected outcome when the kernel does NOT support PR_SET_VMA_ANON_NAME. +bool FallbackFileIsCorrect(unsigned long start, + unsigned long size, + const char* name) { + char file_path[256]; + snprintf(file_path, sizeof(file_path), "/tmp/cobalt_vma_tags_%d.txt", + getpid()); + + FILE* fp = fopen(file_path, "r"); + if (!fp) { + return false; + } + + char line[1024]; + bool found = false; + while (fgets(line, sizeof(line), fp)) { + unsigned long file_start, file_end; + char file_name[256]; + // The format is "0x%lx 0x%lx %s\n" + if (sscanf(line, "0x%lx 0x%lx %s", &file_start, &file_end, file_name) == + 3) { + if (file_start == start && file_end == start + size && + strcmp(file_name, name) == 0) { + found = true; + break; + } + } + } + + fclose(fp); + return found; +} + +// This test verifies that __abi_wrap_prctl with PR_SET_VMA and +// PR_SET_VMA_ANON_NAME correctly names a VMA region, either by calling the +// underlying prctl syscall or by using the fallback mechanism of writing to a +// file. +TEST(PosixPrctlTest, SetVmaAnonName) { + const size_t kMapSize = 4096; + void* p = malloc(kMapSize); + ASSERT_NE(p, nullptr); + + // Ensure we have a clean state by deleting any leftover fallback file. + char file_path[256]; + snprintf(file_path, sizeof(file_path), "/tmp/cobalt_vma_tags_%d.txt", + getpid()); + unlink(file_path); + + int result = + __abi_wrap_prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, (unsigned long)p, + kMapSize, (unsigned long)kVmaName); + + // The wrapper should return 0 on success, for both the prctl call and the + // fallback. + EXPECT_EQ(result, 0); + + bool vma_is_named = VmaIsNamed(p, kVmaName); + bool fallback_file_is_correct = + FallbackFileIsCorrect((unsigned long)p, kMapSize, kVmaName); + + // We expect one of the two mechanisms to have worked. + EXPECT_TRUE(vma_is_named || fallback_file_is_correct) + << "VMA name was not set via prctl and fallback file was not created or " + "is incorrect."; + + if (vma_is_named) { + SB_LOG(INFO) << "VMA naming with PR_SET_VMA_ANON_NAME is supported by the " + "kernel."; + } + if (fallback_file_is_correct) { + SB_LOG(INFO) << "VMA naming with PR_SET_VMA_ANON_NAME is NOT supported by " + "the kernel, fallback was used."; + } + + free(p); + // Clean up the fallback file if it was created. + unlink(file_path); +} + +} // namespace diff --git a/starboard/shared/modular/BUILD.gn b/starboard/shared/modular/BUILD.gn index 4cebc4353d9..fcaf5702b8b 100644 --- a/starboard/shared/modular/BUILD.gn +++ b/starboard/shared/modular/BUILD.gn @@ -34,6 +34,8 @@ if ((sb_is_modular || sb_is_evergreen_compatible) && "starboard_layer_posix_pipe2_abi_wrappers.h", "starboard_layer_posix_poll_abi_wrappers.cc", "starboard_layer_posix_poll_abi_wrappers.h", + "starboard_layer_posix_prctl_abi_wrappers.cc", + "starboard_layer_posix_prctl_abi_wrappers.h", "starboard_layer_posix_pthread_abi_wrappers.cc", "starboard_layer_posix_pthread_abi_wrappers.h", "starboard_layer_posix_semaphore_abi_wrappers.cc", @@ -61,6 +63,7 @@ if ((sb_is_modular || sb_is_evergreen_compatible) && deps = [ "//starboard:starboard_headers_only", + "//starboard/common", "//starboard/common:common_headers_only", ] } @@ -78,6 +81,7 @@ if (is_cobalt_hermetic_build && !sb_is_evergreen && "cobalt_layer_posix_mmap_abi_wrappers.cc", "cobalt_layer_posix_pipe2_abi_wrappers.cc", "cobalt_layer_posix_poll_abi_wrappers.cc", + "cobalt_layer_posix_prctl_abi_wrappers.cc", "cobalt_layer_posix_pthread_abi_wrappers.cc", "cobalt_layer_posix_semaphore_abi_wrappers.cc", "cobalt_layer_posix_signal_abi_wrappers.cc", diff --git a/starboard/shared/modular/cobalt_layer_posix_prctl_abi_wrappers.cc b/starboard/shared/modular/cobalt_layer_posix_prctl_abi_wrappers.cc new file mode 100644 index 00000000000..54e1ac07688 --- /dev/null +++ b/starboard/shared/modular/cobalt_layer_posix_prctl_abi_wrappers.cc @@ -0,0 +1,37 @@ +// Copyright 2025 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +extern "C" { + +int __abi_wrap_prctl(int option, + unsigned long arg2, + unsigned long arg3, + unsigned long arg4, + unsigned long arg5); + +int prctl(int option, ...) { + va_list args; + va_start(args, option); + long arg2 = va_arg(args, long); + long arg3 = va_arg(args, long); + long arg4 = va_arg(args, long); + long arg5 = va_arg(args, long); + va_end(args); + return __abi_wrap_prctl(option, arg2, arg3, arg4, arg5); +} + +} // extern "C" diff --git a/starboard/shared/modular/starboard_layer_posix_prctl_abi_wrappers.cc b/starboard/shared/modular/starboard_layer_posix_prctl_abi_wrappers.cc new file mode 100644 index 00000000000..d138ca40a21 --- /dev/null +++ b/starboard/shared/modular/starboard_layer_posix_prctl_abi_wrappers.cc @@ -0,0 +1,91 @@ +// Copyright 2025 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard/shared/modular/starboard_layer_posix_prctl_abi_wrappers.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "starboard/common/log.h" +#include "starboard/common/string.h" + +// From third_party/musl/include/sys/prctl.h +#ifndef PR_SET_VMA +#define PR_SET_VMA 0x53564d41 +#endif +#ifndef PR_SET_VMA_ANON_NAME +#define PR_SET_VMA_ANON_NAME 0 +#endif + +namespace { +void VmaTagFileCleanup() { + char file_path[256]; + snprintf(file_path, sizeof(file_path), "/tmp/cobalt_vma_tags_%d.txt", + getpid()); + unlink(file_path); +} + +void VmaTagSignalHandler(int signum) { + VmaTagFileCleanup(); + // Re-raise signal to get default behavior (e.g. core dump). + signal(signum, SIG_DFL); + raise(signum); +} +} // namespace + +SB_EXPORT int __abi_wrap_prctl(int option, + unsigned long arg2, + unsigned long arg3, + unsigned long arg4, + unsigned long arg5) { + int result = prctl(option, arg2, arg3, arg4, arg5); + if (result == -1 && errno == EINVAL && option == PR_SET_VMA && + arg2 == PR_SET_VMA_ANON_NAME) { + // Kernel does not support PR_SET_VMA_ANON_NAME. Fallback to writing to a + // file. + char file_path[256]; + snprintf(file_path, sizeof(file_path), "/tmp/cobalt_vma_tags_%d.txt", + getpid()); + + FILE* file = fopen(file_path, "a"); + if (file) { + static bool cleanup_registered = false; + if (!cleanup_registered) { + atexit(VmaTagFileCleanup); + // Also register signal handlers for common crash signals. + // This is not a perfect solution as it can interfere with application + // signal handlers and does not handle SIGKILL. + signal(SIGSEGV, VmaTagSignalHandler); + signal(SIGABRT, VmaTagSignalHandler); + signal(SIGTERM, VmaTagSignalHandler); + signal(SIGQUIT, VmaTagSignalHandler); + signal(SIGINT, VmaTagSignalHandler); + cleanup_registered = true; + } + fprintf(file, "0x%lx 0x%lx %s\n", arg3, arg3 + arg4, (const char*)arg5); + fclose(file); + return 0; // Success for our fallback. + } else { + SB_LOG(ERROR) << "Failed to open VMA tag file: " << file_path; + // Fall through to return original error. + } + } + return result; +} diff --git a/starboard/shared/modular/starboard_layer_posix_prctl_abi_wrappers.h b/starboard/shared/modular/starboard_layer_posix_prctl_abi_wrappers.h new file mode 100644 index 00000000000..dc8c4e9512b --- /dev/null +++ b/starboard/shared/modular/starboard_layer_posix_prctl_abi_wrappers.h @@ -0,0 +1,34 @@ +// Copyright 2025 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef STARBOARD_SHARED_MODULAR_STARBOARD_LAYER_POSIX_PRCTL_ABI_WRAPPERS_H_ +#define STARBOARD_SHARED_MODULAR_STARBOARD_LAYER_POSIX_PRCTL_ABI_WRAPPERS_H_ + +#include "starboard/export.h" + +#ifdef __cplusplus +extern "C" { +#endif + +SB_EXPORT int __abi_wrap_prctl(int option, + unsigned long arg2, + unsigned long arg3, + unsigned long arg4, + unsigned long arg5); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // STARBOARD_SHARED_MODULAR_STARBOARD_LAYER_POSIX_PRCTL_ABI_WRAPPERS_H_ diff --git a/third_party/musl/include/sys/prctl.h b/third_party/musl/include/sys/prctl.h index 087a75c9da0..59e3a6ba037 100644 --- a/third_party/musl/include/sys/prctl.h +++ b/third_party/musl/include/sys/prctl.h @@ -177,6 +177,14 @@ struct prctl_mm_map { #define PR_PAC_SET_ENABLED_KEYS 60 #define PR_PAC_GET_ENABLED_KEYS 61 +#if !defined(PR_SET_VMA) +#define PR_SET_VMA 0x53564d41 +#endif + +#if !defined(PR_SET_VMA_ANON_NAME) +#define PR_SET_VMA_ANON_NAME 0 +#endif + int prctl (int, ...); #ifdef __cplusplus