diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index c24703840183af..b447b5dfe0989e 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -185,6 +185,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.sys.mman.munlock libc.src.sys.mman.mlockall libc.src.sys.mman.munlockall + libc.src.sys.mman.msync # sys/random.h entrypoints libc.src.sys.random.getrandom diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index f7a65615115f34..5175b14adf2e74 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -190,6 +190,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.sys.mman.munlock libc.src.sys.mman.mlockall libc.src.sys.mman.munlockall + libc.src.sys.mman.msync # sys/random.h entrypoints libc.src.sys.random.getrandom diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index b51227e5f25d74..b8bec14a3d2a6d 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -228,6 +228,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.sys.mman.munlock libc.src.sys.mman.mlockall libc.src.sys.mman.munlockall + libc.src.sys.mman.msync # sys/random.h entrypoints libc.src.sys.random.getrandom diff --git a/libc/spec/posix.td b/libc/spec/posix.td index 70be692e208a88..d0f5a4584dd45b 100644 --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -309,6 +309,11 @@ def POSIX : StandardSpec<"POSIX"> { RetValSpec, [ArgSpec] >, + FunctionSpec< + "msync", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec] + >, ] >; diff --git a/libc/src/sys/mman/CMakeLists.txt b/libc/src/sys/mman/CMakeLists.txt index f11f5ac9df9f2f..b49f73873c2006 100644 --- a/libc/src/sys/mman/CMakeLists.txt +++ b/libc/src/sys/mman/CMakeLists.txt @@ -78,3 +78,10 @@ add_entrypoint_object( DEPENDS .${LIBC_TARGET_OS}.munlockall ) + +add_entrypoint_object( + msync + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.msync +) diff --git a/libc/src/sys/mman/linux/CMakeLists.txt b/libc/src/sys/mman/linux/CMakeLists.txt index a6feff1f0bec19..04086ee5d33254 100644 --- a/libc/src/sys/mman/linux/CMakeLists.txt +++ b/libc/src/sys/mman/linux/CMakeLists.txt @@ -139,3 +139,16 @@ add_entrypoint_object( libc.src.__support.OSUtil.osutil libc.src.errno.errno ) + +add_entrypoint_object( + msync + SRCS + msync.cpp + HDRS + ../msync.h + DEPENDS + libc.include.sys_mman + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) diff --git a/libc/src/sys/mman/linux/msync.cpp b/libc/src/sys/mman/linux/msync.cpp new file mode 100644 index 00000000000000..1d2544f023c2c0 --- /dev/null +++ b/libc/src/sys/mman/linux/msync.cpp @@ -0,0 +1,25 @@ +//===---------- Linux implementation of the msync 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/msync.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. + +#include "src/errno/libc_errno.h" +#include // For syscall numbers. + +namespace LIBC_NAMESPACE { +LLVM_LIBC_FUNCTION(int, msync, (void *addr, size_t len, int flags)) { + long ret = syscall_impl(SYS_msync, cpp::bit_cast(addr), len, flags); + if (ret < 0) { + libc_errno = static_cast(-ret); + return -1; + } + return 0; +} +} // namespace LIBC_NAMESPACE diff --git a/libc/src/sys/mman/msync.h b/libc/src/sys/mman/msync.h new file mode 100644 index 00000000000000..08afdd8c062857 --- /dev/null +++ b/libc/src/sys/mman/msync.h @@ -0,0 +1,21 @@ +//===-- Implementation header for msync 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_MSYNC_H +#define LLVM_LIBC_SRC_SYS_MMAN_MSYNC_H + +#include +#include + +namespace LIBC_NAMESPACE { + +int msync(void *addr, size_t len, int flags); + +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_SYS_MMAN_MSYNC_H diff --git a/libc/test/src/sys/mman/linux/CMakeLists.txt b/libc/test/src/sys/mman/linux/CMakeLists.txt index 09ab5b09be6403..6f7fc34d8d3ce3 100644 --- a/libc/test/src/sys/mman/linux/CMakeLists.txt +++ b/libc/test/src/sys/mman/linux/CMakeLists.txt @@ -107,3 +107,23 @@ add_libc_unittest( libc.src.unistd.sysconf libc.test.UnitTest.ErrnoSetterMatcher ) + +add_libc_unittest( + msync_test + SUITE + libc_sys_mman_unittests + SRCS + msync_test.cpp + DEPENDS + libc.include.sys_mman + libc.include.unistd + libc.src.errno.errno + libc.src.sys.mman.mmap + libc.src.sys.mman.munmap + libc.src.sys.mman.msync + libc.src.sys.mman.mincore + libc.src.sys.mman.mlock + libc.src.sys.mman.munlock + libc.src.unistd.sysconf + libc.test.UnitTest.ErrnoSetterMatcher +) diff --git a/libc/test/src/sys/mman/linux/msync_test.cpp b/libc/test/src/sys/mman/linux/msync_test.cpp new file mode 100644 index 00000000000000..0d60415b1243ab --- /dev/null +++ b/libc/test/src/sys/mman/linux/msync_test.cpp @@ -0,0 +1,69 @@ +//===-- Unittests for msync -----------------------------------------------===// +// +// 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/sys/mman/mlock.h" +#include "src/sys/mman/mmap.h" +#include "src/sys/mman/msync.h" +#include "src/sys/mman/munlock.h" +#include "src/sys/mman/munmap.h" +#include "src/unistd/sysconf.h" +#include "test/UnitTest/ErrnoSetterMatcher.h" +#include "test/UnitTest/LibcTest.h" +#include "test/UnitTest/Test.h" + +using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher; + +struct PageHolder { + size_t size; + void *addr; + + PageHolder() + : size(LIBC_NAMESPACE::sysconf(_SC_PAGESIZE)), + addr(LIBC_NAMESPACE::mmap(nullptr, size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)) {} + ~PageHolder() { + if (addr != MAP_FAILED) + LIBC_NAMESPACE::munmap(addr, size); + } + + char &operator[](size_t i) { return reinterpret_cast(addr)[i]; } + + bool is_valid() { return addr != MAP_FAILED; } +}; + +TEST(LlvmLibcMsyncTest, UnMappedMemory) { + EXPECT_THAT(LIBC_NAMESPACE::msync(nullptr, 1024, MS_SYNC), Fails(ENOMEM)); + EXPECT_THAT(LIBC_NAMESPACE::msync(nullptr, 1024, MS_ASYNC), Fails(ENOMEM)); +} + +TEST(LlvmLibcMsyncTest, LockedPage) { + PageHolder page; + ASSERT_TRUE(page.is_valid()); + ASSERT_THAT(LIBC_NAMESPACE::mlock(page.addr, page.size), Succeeds()); + EXPECT_THAT( + LIBC_NAMESPACE::msync(page.addr, page.size, MS_SYNC | MS_INVALIDATE), + Fails(EBUSY)); + ASSERT_THAT(LIBC_NAMESPACE::munlock(page.addr, page.size), Succeeds()); + EXPECT_THAT(LIBC_NAMESPACE::msync(page.addr, page.size, MS_SYNC), Succeeds()); +} + +TEST(LlvmLibcMsyncTest, UnalignedAddress) { + PageHolder page; + ASSERT_TRUE(page.is_valid()); + EXPECT_THAT(LIBC_NAMESPACE::msync(&page[1], page.size - 1, MS_SYNC), + Fails(EINVAL)); +} + +TEST(LlvmLibcMsyncTest, InvalidFlag) { + PageHolder page; + ASSERT_TRUE(page.is_valid()); + EXPECT_THAT(LIBC_NAMESPACE::msync(page.addr, page.size, MS_SYNC | MS_ASYNC), + Fails(EINVAL)); + EXPECT_THAT(LIBC_NAMESPACE::msync(page.addr, page.size, -1), Fails(EINVAL)); +}