diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 64fbe1a250c0b..0031883c98ac8 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -250,6 +250,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.sys.mman.munlock libc.src.sys.mman.munlockall libc.src.sys.mman.munmap + libc.src.sys.mman.remap_file_pages libc.src.sys.mman.posix_madvise libc.src.sys.mman.shm_open libc.src.sys.mman.shm_unlink diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index ff3d821c664c5..8a32407b3c6dc 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -249,6 +249,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.sys.mman.munlock libc.src.sys.mman.munlockall libc.src.sys.mman.munmap + libc.src.sys.mman.remap_file_pages libc.src.sys.mman.posix_madvise libc.src.sys.mman.shm_open libc.src.sys.mman.shm_unlink diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index dd658af3bfb67..4de278f07b6af 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -249,6 +249,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.sys.mman.munlock libc.src.sys.mman.munlockall libc.src.sys.mman.munmap + libc.src.sys.mman.remap_file_pages libc.src.sys.mman.posix_madvise libc.src.sys.mman.shm_open libc.src.sys.mman.shm_unlink diff --git a/libc/newhdrgen/yaml/sys/mman.yaml b/libc/newhdrgen/yaml/sys/mman.yaml index 272e7e2af16aa..7c4fb1ef09e79 100644 --- a/libc/newhdrgen/yaml/sys/mman.yaml +++ b/libc/newhdrgen/yaml/sys/mman.yaml @@ -98,6 +98,16 @@ functions: - type: void * - type: size_t - type: int + - name: remap_file_pages + standards: + - Linux + return_type: int + arguments: + - type: void * + - type: size_t + - type: int + - type: size_t + - type: int - name: shm_open standards: - POSIX diff --git a/libc/spec/linux.td b/libc/spec/linux.td index 395c2a6fe853a..4aaf18b0803f3 100644 --- a/libc/spec/linux.td +++ b/libc/spec/linux.td @@ -103,6 +103,17 @@ def Linux : StandardSpec<"Linux"> { ArgSpec, ] >, + FunctionSpec< + "remap_file_pages", + RetValSpec, + [ + ArgSpec, + ArgSpec, + ArgSpec, + ArgSpec, + ArgSpec, + ] + >, ] // Functions >; diff --git a/libc/src/sys/mman/CMakeLists.txt b/libc/src/sys/mman/CMakeLists.txt index 9c74202a09f03..4ea43e14be029 100644 --- a/libc/src/sys/mman/CMakeLists.txt +++ b/libc/src/sys/mman/CMakeLists.txt @@ -86,6 +86,13 @@ add_entrypoint_object( .${LIBC_TARGET_OS}.msync ) +add_entrypoint_object( + remap_file_pages + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.remap_file_pages +) + add_entrypoint_object( shm_open ALIAS diff --git a/libc/src/sys/mman/linux/CMakeLists.txt b/libc/src/sys/mman/linux/CMakeLists.txt index 00f4f0e64ec06..11188254cfbd4 100644 --- a/libc/src/sys/mman/linux/CMakeLists.txt +++ b/libc/src/sys/mman/linux/CMakeLists.txt @@ -153,6 +153,19 @@ add_entrypoint_object( libc.src.errno.errno ) +add_entrypoint_object( + remap_file_pages + SRCS + remap_file_pages.cpp + HDRS + ../remap_file_pages.h + DEPENDS + libc.include.sys_mman + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) + add_header_library( shm_common HDRS diff --git a/libc/src/sys/mman/linux/remap_file_pages.cpp b/libc/src/sys/mman/linux/remap_file_pages.cpp new file mode 100644 index 0000000000000..f616e1915ecc5 --- /dev/null +++ b/libc/src/sys/mman/linux/remap_file_pages.cpp @@ -0,0 +1,40 @@ +//===------- Linux implementation of the remap_file_pages function --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/sys/mman/remap_file_pages.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/errno/libc_errno.h" +#include // For syscall numbers. + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, remap_file_pages, + (void *addr, size_t size, int prot, size_t pgoff, + int flags)) { +#ifdef SYS_remap_file_pages + int ret = LIBC_NAMESPACE::syscall_impl(SYS_remap_file_pages, + reinterpret_cast(addr), + size, prot, pgoff, flags); +#else +#error "remap_file_pages syscall is not available." +#endif + + // A negative return value indicates an error with the magnitude of the + // value being the error code. + if (ret < 0) { + libc_errno = -ret; + return -1; + } + + return 0; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/sys/mman/remap_file_pages.h b/libc/src/sys/mman/remap_file_pages.h new file mode 100644 index 0000000000000..518502960d5cf --- /dev/null +++ b/libc/src/sys/mman/remap_file_pages.h @@ -0,0 +1,22 @@ +//===-- Implementation header for remap_file_pages function -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SYS_MMAN_REMAP_FILE_PAGES_H +#define LLVM_LIBC_SRC_SYS_MMAN_REMAP_FILE_PAGES_H + +#include "src/__support/macros/config.h" +#include + +namespace LIBC_NAMESPACE_DECL { + +int remap_file_pages(void *addr, size_t size, int prot, size_t pgoff, + int flags); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_SYS_MMAN_REMAP_FILE_PAGES_H diff --git a/libc/test/src/sys/mman/linux/CMakeLists.txt b/libc/test/src/sys/mman/linux/CMakeLists.txt index b63c76f4306fa..a432d88ffb90c 100644 --- a/libc/test/src/sys/mman/linux/CMakeLists.txt +++ b/libc/test/src/sys/mman/linux/CMakeLists.txt @@ -128,6 +128,23 @@ add_libc_unittest( libc.test.UnitTest.ErrnoSetterMatcher ) +add_libc_unittest( + remap_file_pages_test + SUITE + libc_sys_mman_unittests + SRCS + remap_file_pages_test.cpp + DEPENDS + libc.include.sys_mman + libc.include.sys_stat + libc.src.unistd.sysconf + libc.test.UnitTest.ErrnoSetterMatcher + libc.src.sys.mman.remap_file_pages + libc.src.errno.errno + libc.src.sys.mman.mmap + libc.src.sys.mman.munmap +) + add_libc_unittest( shm_test SUITE diff --git a/libc/test/src/sys/mman/linux/remap_file_pages_test.cpp b/libc/test/src/sys/mman/linux/remap_file_pages_test.cpp new file mode 100644 index 0000000000000..267f7598ff70e --- /dev/null +++ b/libc/test/src/sys/mman/linux/remap_file_pages_test.cpp @@ -0,0 +1,93 @@ +//===-- Unittests for remap_file_pages ------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/errno/libc_errno.h" +#include "src/fcntl/open.h" +#include "src/sys/mman/mmap.h" +#include "src/sys/mman/munmap.h" +#include "src/sys/mman/remap_file_pages.h" +#include "src/unistd/close.h" +#include "src/unistd/sysconf.h" +#include "test/UnitTest/ErrnoSetterMatcher.h" +#include "test/UnitTest/Test.h" + +#include +#include // For S_IRWXU + +using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails; +using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds; + +TEST(LlvmLibcRemapFilePagesTest, NoError) { + size_t page_size = sysconf(_SC_PAGE_SIZE); + ASSERT_GT(page_size, size_t(0)); + + // Create a file-backed mapping + constexpr const char *file_name = "remap_file_pages.test.noerror"; + auto test_file = libc_make_test_file_path(file_name); + int fd = LIBC_NAMESPACE::open(test_file, O_RDWR | O_CREAT, S_IRWXU); + ASSERT_GT(fd, 0); + + // First, allocate some memory using mmap + size_t alloc_size = 2 * page_size; + LIBC_NAMESPACE::libc_errno = 0; + void *addr = LIBC_NAMESPACE::mmap(nullptr, alloc_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + ASSERT_ERRNO_SUCCESS(); + EXPECT_NE(addr, MAP_FAILED); + + // Now try to remap the pages + EXPECT_THAT(LIBC_NAMESPACE::remap_file_pages(addr, page_size, 0, 1, 0), + Succeeds()); + + // Reset error number for the new function + LIBC_NAMESPACE::libc_errno = 0; + + // Clean up + EXPECT_THAT(LIBC_NAMESPACE::munmap(addr, alloc_size), Succeeds()); + EXPECT_THAT(LIBC_NAMESPACE::close(fd), Succeeds()); +} + +TEST(LlvmLibcRemapFilePagesTest, ErrorInvalidFlags) { + size_t page_size = sysconf(_SC_PAGE_SIZE); + ASSERT_GT(page_size, size_t(0)); + + // Create a file-backed mapping + constexpr const char *file_name = "remap_file_pages.test.error"; + auto test_file = libc_make_test_file_path(file_name); + int fd = LIBC_NAMESPACE::open(test_file, O_RDWR | O_CREAT, S_IRWXU); + ASSERT_GT(fd, 0); + + // First, allocate some memory using mmap + size_t alloc_size = 2 * page_size; + LIBC_NAMESPACE::libc_errno = 0; + void *addr = LIBC_NAMESPACE::mmap(nullptr, alloc_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + ASSERT_ERRNO_SUCCESS(); + EXPECT_NE(addr, MAP_FAILED); + + // Try to remap pages with an invalid flag MAP_PRIVATE + EXPECT_THAT(LIBC_NAMESPACE::remap_file_pages(addr, page_size, PROT_READ, 0, + MAP_PRIVATE), + Fails(EINVAL)); + + // Clean up + EXPECT_THAT(LIBC_NAMESPACE::munmap(addr, page_size), Succeeds()); + EXPECT_THAT(LIBC_NAMESPACE::close(fd), Succeeds()); +} + +TEST(LlvmLibcRemapFilePagesTest, ErrorInvalidAddress) { + size_t page_size = sysconf(_SC_PAGESIZE); + ASSERT_GT(page_size, size_t(0)); + + // Use an address that we haven't mapped + void *invalid_addr = reinterpret_cast(0x12345000); + + EXPECT_THAT(LIBC_NAMESPACE::remap_file_pages(invalid_addr, page_size, + PROT_READ, 0, 0), + Fails(EINVAL)); +}