Index: libc/config/linux/api.td =================================================================== --- libc/config/linux/api.td +++ libc/config/linux/api.td @@ -304,3 +304,9 @@ "thrd_join", ]; } + +def UniStdAPI : PublicAPI<"unistd.h"> { + let Functions = [ + "write", + ]; +} Index: libc/include/CMakeLists.txt =================================================================== --- libc/include/CMakeLists.txt +++ libc/include/CMakeLists.txt @@ -91,6 +91,14 @@ .llvm_libc_common_h ) +add_gen_header( + unistd + DEF_FILE unistd.h.def + GEN_HDR unistd.h + DEPENDS + .llvm_libc_common_h +) + # TODO: Not all platforms will have a include/sys directory. Add the sys # directory and the targets for sys/*.h files conditional to the OS requiring # them. Index: libc/include/unistd.h.def =================================================================== --- /dev/null +++ libc/include/unistd.h.def @@ -0,0 +1,16 @@ +//===-- C standard library header unistd.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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_UNISTD_H +#define LLVM_LIBC_UNISTD_H + +#include <__llvm-libc-common.h> + +%%public_api() + +#endif // LLVM_LIBC_UNISTD_H Index: libc/lib/CMakeLists.txt =================================================================== --- libc/lib/CMakeLists.txt +++ libc/lib/CMakeLists.txt @@ -35,6 +35,9 @@ libc.src.threads.mtx_unlock libc.src.threads.thrd_create libc.src.threads.thrd_join + + # unistd.h entrypoints + libc.src.unistd.write ) add_entrypoint_library( Index: libc/spec/posix.td =================================================================== --- libc/spec/posix.td +++ libc/spec/posix.td @@ -174,9 +174,24 @@ ] >; + HeaderSpec UniStd = HeaderSpec< + "unistd.h", + [], // Macros + [], // Types + [], // Enumerations + [ + FunctionSpec< + "write", + RetValSpec, // TODO: this should be ssize_t from sys/types.h + [ArgSpec, ArgSpec, ArgSpec] + >, + ] + >; + let Headers = [ Errno, SysMMan, Signal, + UniStd, ]; } Index: libc/spec/spec.td =================================================================== --- libc/spec/spec.td +++ libc/spec/spec.td @@ -46,6 +46,7 @@ // Common types def VoidPtr : PtrType; +def ConstVoidPtr : ConstType; def SizeTType : NamedType<"size_t">; def FloatPtr : PtrType; Index: libc/src/CMakeLists.txt =================================================================== --- libc/src/CMakeLists.txt +++ libc/src/CMakeLists.txt @@ -8,5 +8,6 @@ # TODO: Add this target conditional to the target OS. add_subdirectory(sys) add_subdirectory(threads) +add_subdirectory(unistd) add_subdirectory(__support) Index: libc/src/unistd/CMakeLists.txt =================================================================== --- /dev/null +++ libc/src/unistd/CMakeLists.txt @@ -0,0 +1,10 @@ +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) +endif() + +add_entrypoint_object( + write + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.write +) Index: libc/src/unistd/linux/CMakeLists.txt =================================================================== --- /dev/null +++ libc/src/unistd/linux/CMakeLists.txt @@ -0,0 +1,11 @@ +add_entrypoint_object( + write + SRCS + write.cpp + HDRS + ../write.h + DEPENDS + libc.config.linux.linux_syscall_h + libc.include.sys_syscall + libc.src.errno.__errno_location +) Index: libc/src/unistd/linux/write.cpp =================================================================== --- /dev/null +++ libc/src/unistd/linux/write.cpp @@ -0,0 +1,28 @@ +//===-- Linux implementation of write -------------------------------------===// +// +// 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/write.h" + +#include "config/linux/syscall.h" // For internal syscall function. +#include "include/sys/syscall.h" // For syscall numbers. +#include "src/__support/common.h" +#include "src/errno/llvmlibc_errno.h" + +namespace __llvm_libc { + +// TOOD: This should return ssize_t when available from sys/types.h +size_t LLVM_LIBC_ENTRYPOINT(write)(int fd, const void *buf, size_t count) { + long ret = __llvm_libc::syscall(SYS_write, fd, buf, count); + if (ret < 0) { + llvmlibc_errno = -ret; + return -1; + } + return ret; +} + +} // namespace __llvm_libc Index: libc/src/unistd/write.h =================================================================== --- /dev/null +++ libc/src/unistd/write.h @@ -0,0 +1,21 @@ +//===-- Implementation header for write -------------------------*- 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_UNISTD_WRITE_H +#define LLVM_LIBC_SRC_UNISTD_WRITE_H + +#include + +namespace __llvm_libc { + +// TODO: write should return ssize_t from sys/types.h +size_t write(int fd, const void *buf, size_t count); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_UNISTD_WRITE_H Index: libc/test/src/CMakeLists.txt =================================================================== --- libc/test/src/CMakeLists.txt +++ libc/test/src/CMakeLists.txt @@ -6,3 +6,4 @@ add_subdirectory(string) add_subdirectory(sys) add_subdirectory(threads) +add_subdirectory(unistd) Index: libc/test/src/unistd/CMakeLists.txt =================================================================== --- /dev/null +++ libc/test/src/unistd/CMakeLists.txt @@ -0,0 +1,14 @@ +add_libc_testsuite(libc_unistd_unittests) + +add_libc_unittest( + write_test + SUITE + libc_unistd_unittests + SRCS + write_test.cpp + DEPENDS + libc.src.unistd.write + libc.include.errno + # TODO(sivachandra): Remove redundant deps. + libc.src.errno.__errno_location +) Index: libc/test/src/unistd/write_test.cpp =================================================================== --- /dev/null +++ libc/test/src/unistd/write_test.cpp @@ -0,0 +1,29 @@ +//===-- Unittests for write -----------------------------------------------===// +// +// 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 "include/errno.h" +#include "src/unistd/write.h" +#include "utils/UnitTest/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" +#include "utils/testutils/FDReader.h" + +TEST(UniStd, WriteBasic) { + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + + __llvm_libc::testutils::FDReader reader; + EXPECT_THAT(__llvm_libc::write(reader.getWriteFD(), "hello", 5), Succeeds(5)); + EXPECT_TRUE(reader.matchWritten("hello")); +} + +TEST(UniStd, WriteFails) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + + EXPECT_THAT(__llvm_libc::write(-1, "", 1), Fails(EBADF)); + EXPECT_THAT(__llvm_libc::write(1, reinterpret_cast(-1), 1), + Fails(EFAULT)); +} Index: libc/utils/testutils/CMakeLists.txt =================================================================== --- libc/utils/testutils/CMakeLists.txt +++ libc/utils/testutils/CMakeLists.txt @@ -1,5 +1,6 @@ if(CMAKE_HOST_UNIX) set(EFFile ExecuteFunctionUnix.cpp) + set(FDReaderFile FDReaderUnix.cpp) endif() add_llvm_library( @@ -8,6 +9,8 @@ StreamWrapper.h ${EFFile} ExecuteFunction.h + ${FDReaderFile} + FDReader.h LINK_COMPONENTS Support ) Index: libc/utils/testutils/FDReader.h =================================================================== --- /dev/null +++ libc/utils/testutils/FDReader.h @@ -0,0 +1,29 @@ +//===-- FDReader.h ----------------------------------------------*- 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_UTILS_TESTUTILS_FDREADER_H +#define LLVM_LIBC_UTILS_TESTUTILS_FDREADER_H + +namespace __llvm_libc { +namespace testutils { + +class FDReader { + int pipefd[2]; + +public: + FDReader(); + ~FDReader(); + + int getWriteFD() { return pipefd[1]; } + bool matchWritten(const char *); +}; + +} // namespace testutils +} // namespace __llvm_libc + +#endif // LLVM_LIBC_UTILS_TESTUTILS_FDREADER_H Index: libc/utils/testutils/FDReaderUnix.cpp =================================================================== --- /dev/null +++ libc/utils/testutils/FDReaderUnix.cpp @@ -0,0 +1,41 @@ +//===-- FDReader.cpp ------------------------------------------------------===// +// +// 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 "FDReader.h" +#include "llvm/Support/MemoryBuffer.h" +#include +#include +#include + +namespace __llvm_libc { +namespace testutils { + +FDReader::FDReader() { + int err = ::pipe(pipefd); + assert(!err && "pipe(2) failed"); +} + +FDReader::~FDReader() { + ::close(pipefd[0]); + ::close(pipefd[1]); +} + +bool FDReader::matchWritten(const char *str) { + llvm::ErrorOr> bufOrErr = + llvm::MemoryBuffer::getOpenFile(pipefd[0], "", + /* FileSize (irrelevant) */ 0); + if (!bufOrErr) { + assert(0 && "Error reading from pipe"); + return false; + } + const llvm::MemoryBuffer &buf = **bufOrErr; + return !std::strncmp(buf.getBufferStart(), str, buf.getBufferSize()); +} + +} // namespace testutils +} // namespace __llvm_libc