| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| add_custom_target(libc_linux_x86_64_unittests) | ||
|
|
||
| add_libc_unittest( | ||
| libc_linux_x86_64_syscall_unittest | ||
| SUITE libc_linux_x86_64_unittests | ||
| SRCS syscall_test.cpp | ||
| DEPENDS | ||
| syscall_impl_h | ||
| support_common_h | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| //===------------ inline implementation of x86_64 syscalls --------------*-===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| %%begin() | ||
|
|
||
| #include "src/__support/common.h" | ||
|
|
||
| #define SYSCALL_CLOBBER_LIST "rcx", "r11", "memory" | ||
|
|
||
| namespace __llvm_libc { | ||
|
|
||
| inline long syscall(long __number) { | ||
| long retcode; | ||
| LIBC_INLINE_ASM("syscall" : | ||
| "=a"(retcode) : | ||
| "a"(__number) : | ||
| SYSCALL_CLOBBER_LIST); | ||
| return retcode; | ||
| } | ||
|
|
||
| inline long syscall(long __number, long __arg1) { | ||
| long retcode; | ||
| LIBC_INLINE_ASM("syscall" : | ||
| "=a"(retcode) : | ||
| "a"(__number), "D"(__arg1) : | ||
| SYSCALL_CLOBBER_LIST); | ||
| return retcode; | ||
| } | ||
|
|
||
| inline long syscall(long __number, long __arg1, long __arg2) { | ||
| long retcode; | ||
| LIBC_INLINE_ASM("syscall" : | ||
| "=a"(retcode) : | ||
| "a"(__number), "D"(__arg1), "S"(__arg2) : | ||
| SYSCALL_CLOBBER_LIST); | ||
| return retcode; | ||
| } | ||
|
|
||
| inline long syscall(long __number, long __arg1, long __arg2, long __arg3) { | ||
| long retcode; | ||
| LIBC_INLINE_ASM("syscall" : | ||
| "=a"(retcode) : | ||
| "a"(__number), "D"(__arg1), "S"(__arg2), "d"(__arg3) : | ||
| SYSCALL_CLOBBER_LIST); | ||
| return retcode; | ||
| } | ||
|
|
||
| inline long syscall( | ||
| long __number, long __arg1, long __arg2, long __arg3, long __arg4) { | ||
| long retcode; | ||
| register long r10 __asm__("r10") = __arg4; | ||
| LIBC_INLINE_ASM("syscall" : | ||
| "=a"(retcode) : | ||
| "a"(__number), "D"(__arg1), "S"(__arg2), "d"(__arg3), "r"(r10) : | ||
| SYSCALL_CLOBBER_LIST); | ||
| return retcode; | ||
| } | ||
|
|
||
| inline long syscall(long __number, long __arg1, long __arg2, long __arg3, | ||
| long __arg4, long __arg5) { | ||
| long retcode; | ||
| register long r10 __asm__("r10") = __arg4; | ||
| register long r8 __asm__("r8") = __arg5; | ||
| LIBC_INLINE_ASM( | ||
| "syscall" : | ||
| "=a"(retcode) : | ||
| "a"(__number), "D"(__arg1), "S"(__arg2), "d"(__arg3), "r"(r10), "r"(r8) : | ||
| SYSCALL_CLOBBER_LIST); | ||
| return retcode; | ||
| } | ||
|
|
||
| inline long syscall(long __number, long __arg1, long __arg2, long __arg3, | ||
| long __arg4, long __arg5, long __arg6) { | ||
| long retcode; | ||
| register long r10 __asm__("r10") = __arg4; | ||
| register long r8 __asm__("r8") = __arg5; | ||
| register long r9 __asm__("r9") = __arg6; | ||
| LIBC_INLINE_ASM( | ||
| "syscall" : | ||
| "=a"(retcode) : | ||
| "a"(__number), "D"(__arg1), "S"(__arg2), "d"(__arg3), "r"(r10), "r"(r8), "r"(r9) : | ||
| SYSCALL_CLOBBER_LIST); | ||
| return retcode; | ||
| } | ||
|
|
||
| #undef SYSCALL_CLOBBER_LIST | ||
|
|
||
| } // namespace __llvm_libc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| //===------------------ Unittests for x86_64 syscalls ---------------------===// | ||
| // | ||
| // 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/unistd/syscall.h" | ||
|
|
||
| #include "gtest/gtest.h" | ||
|
|
||
| #include <functional> | ||
|
|
||
| TEST(X86_64_SyscallTest, APITest) { | ||
| // We only do a signature test here. Actual functionality tests are | ||
| // done by the unit tests of the syscall wrappers like mmap. | ||
|
|
||
| std::function<long(long)> f([](long n) { return __llvm_libc::syscall(n); }); | ||
| std::function<long(long, long)> f1( | ||
| [](long n, long a1) { return __llvm_libc::syscall(n, a1); }); | ||
| std::function<long(long, long, long)> f2( | ||
| [](long n, long a1, long a2) { return __llvm_libc::syscall(n, a1, a2); }); | ||
| std::function<long(long, long, long, long)> f3( | ||
| [](long n, long a1, long a2, long a3) { | ||
| return __llvm_libc::syscall(n, a1, a2, a3); | ||
| }); | ||
| std::function<long(long, long, long, long, long)> f4( | ||
| [](long n, long a1, long a2, long a3, long a4) { | ||
| return __llvm_libc::syscall(n, a1, a2, a3, a4); | ||
| }); | ||
| std::function<long(long, long, long, long, long, long)> f5( | ||
| [](long n, long a1, long a2, long a3, long a4, long a5) { | ||
| return __llvm_libc::syscall(n, a1, a2, a3, a4, a5); | ||
| }); | ||
| std::function<long(long, long, long, long, long, long, long)> f6( | ||
| [](long n, long a1, long a2, long a3, long a4, long a5, long a6) { | ||
| return __llvm_libc::syscall(n, a1, a2, a3, a4, a5, a6); | ||
| }); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| //===----------------- Definitions of common POSIX types ------------------===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // This header file does not have a header guard. It is internal to LLVM libc | ||
| // and intended to be used to pick specific definitions without polluting the | ||
| // public headers with unneccesary definitions. | ||
|
|
||
| #if defined(__need_off_t) && !defined(__llvm_libc_off_t_defined) | ||
| typedef __INT64_TYPE__ off_t; | ||
| #define __llvm_libc_off_t_defined | ||
| #endif // __need_off_t |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| //===------------------------- POSIX header mman.h ------------------------===// | ||
| // | ||
| // 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 <__llvm-libc-common.h> | ||
|
|
||
| %%public_api() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| //===---------------------- Linux like sys/syscall.h ----------------------===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| <!> If syscall.h were a linux only file, then we do not need this indirection. | ||
| <!> However, to keep the option of a non-linux OS requiring a syscall.h file, | ||
| <!> with its own special syscall numbers, we use this indirection. | ||
| %%include_file(${syscall_numbers}) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,8 @@ | ||
| add_subdirectory(errno) | ||
| add_subdirectory(math) | ||
| add_subdirectory(string) | ||
| # TODO: Add this target conditional to the target OS. | ||
| add_subdirectory(sys) | ||
| add_subdirectory(unistd) | ||
|
|
||
| add_subdirectory(__support) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| add_subdirectory(mman) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| #TODO: The sources and target listed here should ideally live in config/linux. | ||
|
|
||
| add_entrypoint_object( | ||
| mmap | ||
| SRCS | ||
| mmap.cpp | ||
| HDRS | ||
| mmap.h | ||
| DEPENDS | ||
| sys_mman_h | ||
| sys_syscall_h | ||
| syscall_impl_h | ||
| __errno_location | ||
| ) | ||
|
|
||
| add_entrypoint_object( | ||
| munmap | ||
| SRCS | ||
| munmap.cpp | ||
| HDRS | ||
| munmap.h | ||
| DEPENDS | ||
| sys_mman_h | ||
| sys_syscall_h | ||
| syscall_impl_h | ||
| __errno_location | ||
| ) | ||
|
|
||
| add_custom_target(libc_sys_mman_unittests) | ||
|
|
||
| add_libc_unittest( | ||
| mmap_test | ||
| SUITE | ||
| libc_sys_mman_unittests | ||
| SRCS | ||
| mmap_test.cpp | ||
| DEPENDS | ||
| mmap | ||
| munmap | ||
| __errno_location | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| //===-------------- Implementation of the POSIX mmap 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/mmap.h" | ||
| #include "src/__support/common.h" | ||
| #include "src/errno/llvmlibc_errno.h" | ||
|
|
||
| #include "src/unistd/syscall.h" // For internal syscall function. | ||
|
|
||
| #include <sys/syscall.h> // For syscall numbers. | ||
|
|
||
| namespace __llvm_libc { | ||
|
|
||
| // This function is currently linux only. It has to be refactored suitably if | ||
| // mmap is to be supported on non-linux operating systems also. | ||
| void *LLVM_LIBC_ENTRYPOINT(mmap)(void *addr, size_t size, int prot, int flags, | ||
| int fd, off_t offset) { | ||
| // A lot of POSIX standard prescribed validation of the parameters is not | ||
| // done in this function as modern linux versions do it in the syscall. | ||
| // TODO: Perform argument validation not done by the linux syscall. | ||
|
|
||
| #ifdef SYS_mmap2 | ||
| offset /= PAGE_SIZE; | ||
| long syscall_number = SYS_mmap2; | ||
| #elif SYS_mmap | ||
| long syscall_number = SYS_mmap; | ||
| #else | ||
| #error "Target platform does not have SYS_mmap or SYS_mmap2 defined" | ||
| #endif | ||
|
|
||
| long ret_val = | ||
| __llvm_libc::syscall(syscall_number, reinterpret_cast<long>(addr), size, | ||
| prot, flags, fd, offset); | ||
|
|
||
| // The mmap/mmap2 syscalls return negative values on error. These negative | ||
| // values are actually the negative values of the error codes. So, fix them | ||
| // up in case an error code is detected. | ||
| // | ||
| // A point to keep in mind for the fix up is that a negative return value | ||
| // from the syscall can also be an error-free value returned by the syscall. | ||
| // However, since a valid return address cannot be within the last page, a | ||
| // return value corresponding to a location in the last page is an error | ||
| // value. | ||
| if (ret_val < 0 && ret_val > -PAGE_SIZE) { | ||
| llvmlibc_errno = -ret_val; | ||
| return MAP_FAILED; | ||
| } | ||
|
|
||
| return reinterpret_cast<void *>(ret_val); | ||
| } | ||
|
|
||
| } // namespace __llvm_libc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| //===-------------- Implementation header for mmap 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_MMAP_H | ||
| #define LLVM_LIBC_SRC_SYS_MMAN_MMAP_H | ||
|
|
||
| #include <sys/mman.h> // For size_t and off_t | ||
|
|
||
| namespace __llvm_libc { | ||
|
|
||
| void *mmap(void *addr, size_t size, int prot, int flags, int fd, off_t offset); | ||
|
|
||
| } // namespace __llvm_libc | ||
|
|
||
| #endif // LLVM_LIBC_SRC_SYS_MMAN_MMAP_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| //===------------------ Unittests for mmap and munmap ---------------------===// | ||
| // | ||
| // 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/llvmlibc_errno.h" | ||
| #include "src/sys/mman/mmap.h" | ||
| #include "src/sys/mman/munmap.h" | ||
|
|
||
| #include "gtest/gtest.h" | ||
|
|
||
| #include "errno.h" | ||
| #include "sys/mman.h" | ||
|
|
||
| TEST(MMapTest, NoError) { | ||
| size_t alloc_size = 128; | ||
| llvmlibc_errno = 0; | ||
| void *addr = __llvm_libc::mmap(nullptr, alloc_size, PROT_READ, | ||
| MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); | ||
| EXPECT_EQ(0, llvmlibc_errno); | ||
| EXPECT_NE(addr, MAP_FAILED); | ||
|
|
||
| int *array = reinterpret_cast<int *>(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); | ||
|
|
||
| int ret_val = __llvm_libc::munmap(addr, alloc_size); | ||
| EXPECT_EQ(0, ret_val); | ||
| EXPECT_EQ(0, llvmlibc_errno); | ||
| } | ||
|
|
||
| TEST(MMapTest, Error_InvalidSize) { | ||
| llvmlibc_errno = 0; | ||
| void *addr = __llvm_libc::mmap(nullptr, 0, PROT_READ, | ||
| MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); | ||
| EXPECT_EQ(EINVAL, llvmlibc_errno); | ||
| EXPECT_EQ(addr, MAP_FAILED); | ||
|
|
||
| llvmlibc_errno = 0; | ||
| int ret_val = __llvm_libc::munmap(0, 0); | ||
| EXPECT_EQ(-1, ret_val); | ||
| EXPECT_EQ(EINVAL, llvmlibc_errno); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| //===------------- Implementation of the POSIX munmap 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/munmap.h" | ||
| #include "src/__support/common.h" | ||
| #include "src/errno/llvmlibc_errno.h" | ||
|
|
||
| #include "src/unistd/syscall.h" // For internal syscall function. | ||
|
|
||
| #include <sys/syscall.h> // For syscall numbers. | ||
|
|
||
| namespace __llvm_libc { | ||
|
|
||
| // This function is currently linux only. It has to be refactored suitably if | ||
| // mmap is to be supported on non-linux operating systems also. | ||
| int LLVM_LIBC_ENTRYPOINT(munmap)(void *addr, size_t size) { | ||
| long ret_val = | ||
| __llvm_libc::syscall(SYS_munmap, reinterpret_cast<long>(addr), size); | ||
|
|
||
| // A negative return value indicates an error with the magnitude of the | ||
| // value being the error code. | ||
| if (ret_val < 0) { | ||
| llvmlibc_errno = -ret_val; | ||
| return -1; | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| } // namespace __llvm_libc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| //===-------------- Implementation header for mumap 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_MUNMAP_H | ||
| #define LLVM_LIBC_SRC_SYS_MMAN_MUNMAP_H | ||
|
|
||
| #include <sys/mman.h> // For size_t | ||
|
|
||
| namespace __llvm_libc { | ||
|
|
||
| int munmap(void *addr, size_t size); | ||
|
|
||
| } // namespace __llvm_libc | ||
|
|
||
| #endif // LLVM_LIBC_SRC_SYS_MMAN_MUNMAP_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| add_gen_header( | ||
| syscall_impl_h | ||
| DEF_FILE syscall.h.def | ||
| GEN_HDR syscall.h | ||
| PARAMS | ||
| inline_syscalls=../../config/${LIBC_TARGET_OS}/${LIBC_TARGET_MACHINE}/syscall.h.inc | ||
| DATA_FILES | ||
| ../../config/${LIBC_TARGET_OS}/${LIBC_TARGET_MACHINE}/syscall.h.inc | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| //===--------------- Internal syscall declarations --------------*- 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| %%include_file(${inline_syscalls}) |