diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -99,9 +99,11 @@ libc.src.stdio.snprintf # sys/mman.h entrypoints + libc.src.sys.mman.madvise libc.src.sys.mman.mmap libc.src.sys.mman.mprotect libc.src.sys.mman.munmap + libc.src.sys.mman.posix_madvise # sys/resource.h entrypoints libc.src.sys.resource.getrlimit diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -200,6 +200,13 @@ ], [], // Enumerations [ + FunctionSpec< + "madvise", + RetValSpec, + [ArgSpec, + ArgSpec, + ArgSpec] + >, FunctionSpec< "mmap", RetValSpec, @@ -222,6 +229,13 @@ RetValSpec, [ArgSpec, ArgSpec] >, + FunctionSpec< + "posix_madvise", + RetValSpec, + [ArgSpec, + ArgSpec, + ArgSpec] + >, ] >; diff --git a/libc/src/sys/mman/CMakeLists.txt b/libc/src/sys/mman/CMakeLists.txt --- a/libc/src/sys/mman/CMakeLists.txt +++ b/libc/src/sys/mman/CMakeLists.txt @@ -2,6 +2,13 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) endif() +add_entrypoint_object( + madvise + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.madvise +) + add_entrypoint_object( mmap ALIAS @@ -22,3 +29,10 @@ DEPENDS .${LIBC_TARGET_OS}.mprotect ) + +add_entrypoint_object( + posix_madvise + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.posix_madvise +) diff --git a/libc/src/sys/mman/linux/CMakeLists.txt b/libc/src/sys/mman/linux/CMakeLists.txt --- a/libc/src/sys/mman/linux/CMakeLists.txt +++ b/libc/src/sys/mman/linux/CMakeLists.txt @@ -1,3 +1,16 @@ +add_entrypoint_object( + madvise + SRCS + madvise.cpp + HDRS + ../madvise.h + DEPENDS + libc.include.sys_mman + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) + add_entrypoint_object( mmap SRCS @@ -36,3 +49,15 @@ libc.src.__support.OSUtil.osutil libc.src.errno.errno ) + +add_entrypoint_object( + posix_madvise + SRCS + posix_madvise.cpp + HDRS + ../posix_madvise.h + DEPENDS + libc.include.sys_mman + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil +) diff --git a/libc/src/sys/mman/linux/madvise.cpp b/libc/src/sys/mman/linux/madvise.cpp new file mode 100644 --- /dev/null +++ b/libc/src/sys/mman/linux/madvise.cpp @@ -0,0 +1,35 @@ +//===---------- Linux implementation of the POSIX madvise 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/madvise.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include // For syscall numbers. + +namespace __llvm_libc { + +// This function is currently linux only. It has to be refactored suitably if +// madvise is to be supported on non-linux operating systems also. +LLVM_LIBC_FUNCTION(int, madvise, (void *addr, size_t size, int advice)) { + long ret_val = __llvm_libc::syscall(SYS_madvise, reinterpret_cast(addr), + size, advice); + + // A negative return value indicates an error with the magnitude of the + // value being the error code. + if (ret_val < 0) { + errno = -ret_val; + return -1; + } + + return 0; +} + +} // namespace __llvm_libc diff --git a/libc/src/sys/mman/linux/posix_madvise.cpp b/libc/src/sys/mman/linux/posix_madvise.cpp new file mode 100644 --- /dev/null +++ b/libc/src/sys/mman/linux/posix_madvise.cpp @@ -0,0 +1,31 @@ +//===---------- Linux implementation of the POSIX posix_madvise 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/posix_madvise.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include // For syscall numbers. + +namespace __llvm_libc { + +// This function is currently linux only. It has to be refactored suitably if +// posix_madvise is to be supported on non-linux operating systems also. +LLVM_LIBC_FUNCTION(int, posix_madvise, (void *addr, size_t size, int advice)) { + // POSIX_MADV_DONTNEED does nothing because the default MADV_DONTNEED may + // cause data loss, which the posix madvise does not allow. + if (advice == POSIX_MADV_DONTNEED) { + return 0; + } + long ret_val = __llvm_libc::syscall(SYS_madvise, reinterpret_cast(addr), + size, advice); + return ret_val < 0 ? -ret_val : 0; +} + +} // namespace __llvm_libc diff --git a/libc/src/sys/mman/madvise.h b/libc/src/sys/mman/madvise.h new file mode 100644 --- /dev/null +++ b/libc/src/sys/mman/madvise.h @@ -0,0 +1,20 @@ +//===-- Implementation header for madvise 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_MADVISE_H +#define LLVM_LIBC_SRC_SYS_MMAN_MADVISE_H + +#include // For size_t and off_t + +namespace __llvm_libc { + +int madvise(void *addr, size_t size, int advice); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SYS_MMAN_MADVISE_H diff --git a/libc/src/sys/mman/posix_madvise.h b/libc/src/sys/mman/posix_madvise.h new file mode 100644 --- /dev/null +++ b/libc/src/sys/mman/posix_madvise.h @@ -0,0 +1,20 @@ +//===-- Implementation header for posix_madvise 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_POSIX_MADVISE_H +#define LLVM_LIBC_SRC_SYS_MMAN_POSIX_MADVISE_H + +#include // For size_t and off_t + +namespace __llvm_libc { + +int posix_madvise(void *addr, size_t size, int advice); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SYS_MMAN_POSIX_MADVISE_H diff --git a/libc/test/src/sys/mman/linux/CMakeLists.txt b/libc/test/src/sys/mman/linux/CMakeLists.txt --- a/libc/test/src/sys/mman/linux/CMakeLists.txt +++ b/libc/test/src/sys/mman/linux/CMakeLists.txt @@ -32,3 +32,35 @@ libc.test.errno_setter_matcher ) +add_libc_unittest( + madvise_test + SUITE + libc_sys_mman_unittests + SRCS + madvise_test.cpp + DEPENDS + libc.include.errno + libc.include.sys_mman + libc.src.errno.errno + libc.src.sys.mman.mmap + libc.src.sys.mman.munmap + libc.src.sys.mman.madvise + libc.test.errno_setter_matcher +) + + +add_libc_unittest( + posix_madvise_test + SUITE + libc_sys_mman_unittests + SRCS + posix_madvise_test.cpp + DEPENDS + libc.include.errno + libc.include.sys_mman + libc.src.errno.errno + libc.src.sys.mman.mmap + libc.src.sys.mman.munmap + libc.src.sys.mman.posix_madvise + libc.test.errno_setter_matcher +) diff --git a/libc/test/src/sys/mman/linux/madvise_test.cpp b/libc/test/src/sys/mman/linux/madvise_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/sys/mman/linux/madvise_test.cpp @@ -0,0 +1,42 @@ +//===-- Unittests for madvise ---------------------------------------------===// +// +// 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/madvise.h" +#include "src/sys/mman/mmap.h" +#include "src/sys/mman/munmap.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" + +#include +#include + +using __llvm_libc::testing::ErrnoSetterMatcher::Fails; +using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + +TEST(LlvmLibcMadviseTest, NoError) { + size_t alloc_size = 128; + errno = 0; + void *addr = __llvm_libc::mmap(nullptr, alloc_size, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + EXPECT_EQ(0, errno); + EXPECT_NE(addr, MAP_FAILED); + + EXPECT_THAT(__llvm_libc::madvise(addr, alloc_size, MADV_RANDOM), Succeeds()); + + int *array = reinterpret_cast(addr); + // Reading from the memory should not crash the test. + // Since we used the MAP_ANONYMOUS flag, the contents of the newly + // allocated memory should be initialized to zero. + EXPECT_EQ(array[0], 0); + EXPECT_THAT(__llvm_libc::munmap(addr, alloc_size), Succeeds()); +} + +TEST(LlvmLibcMadviseTest, Error_BadPtr) { + errno = 0; + EXPECT_THAT(__llvm_libc::madvise(nullptr, 8, MADV_SEQUENTIAL), Fails(ENOMEM)); +} diff --git a/libc/test/src/sys/mman/linux/posix_madvise_test.cpp b/libc/test/src/sys/mman/linux/posix_madvise_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/sys/mman/linux/posix_madvise_test.cpp @@ -0,0 +1,49 @@ +//===-- Unittests for posix_madvise ---------------------------------------===// +// +// 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/mmap.h" +#include "src/sys/mman/munmap.h" +#include "src/sys/mman/posix_madvise.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" + +#include +#include + +using __llvm_libc::testing::ErrnoSetterMatcher::Fails; +using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + +TEST(LlvmLibcPosixMadviseTest, NoError) { + size_t alloc_size = 128; + errno = 0; + void *addr = __llvm_libc::mmap(nullptr, alloc_size, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + EXPECT_EQ(0, errno); + EXPECT_NE(addr, MAP_FAILED); + + EXPECT_EQ(__llvm_libc::posix_madvise(addr, alloc_size, POSIX_MADV_RANDOM), 0); + + int *array = reinterpret_cast(addr); + // Reading from the memory should not crash the test. + // Since we used the MAP_ANONYMOUS flag, the contents of the newly + // allocated memory should be initialized to zero. + EXPECT_EQ(array[0], 0); + EXPECT_THAT(__llvm_libc::munmap(addr, alloc_size), Succeeds()); +} + +TEST(LlvmLibcPosixMadviseTest, Error_BadPtr) { + errno = 0; + // posix_madvise is a no-op on DONTNEED, so it shouldn't fail even with the + // nullptr. + EXPECT_EQ(__llvm_libc::posix_madvise(nullptr, 8, POSIX_MADV_DONTNEED), 0); + + // posix_madvise doesn't set errno, but the return value is actually the error + // code. + EXPECT_EQ(__llvm_libc::posix_madvise(nullptr, 8, POSIX_MADV_SEQUENTIAL), + ENOMEM); +}