diff --git a/arm-software/embedded/llvmlibc-support/semihost/semihost.cpp b/arm-software/embedded/llvmlibc-support/semihost/semihost.cpp index db0781dfefa4..e51bfd08b7dc 100644 --- a/arm-software/embedded/llvmlibc-support/semihost/semihost.cpp +++ b/arm-software/embedded/llvmlibc-support/semihost/semihost.cpp @@ -1,29 +1,168 @@ // -// Copyright (c) 2022-2025, Arm Limited and affiliates. +// Copyright (c) 2022-2026, Arm Limited and affiliates. // -// Part of the Arm Toolchain project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. +// Part of the Arm Toolchain 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 "semihost.h" #include "platform.h" +#include #include +#include #include #include namespace { +// File cookies +inline constexpr size_t DEFAULT_FILE_COOKIE_COUNT = 4; +__llvm_libc_semihost_file_cookie + default_file_cookies[DEFAULT_FILE_COOKIE_COUNT]; +} // namespace -void stdio_open(struct __llvm_libc_stdio_cookie *cookie, size_t mode) { - const char std_stream_name[] = ":tt"; +extern "C" { +// Override this weak definition with a pool backed by an application-owned +// array to change the maximum number of simultaneously open files. +__attribute__((weak)) __llvm_libc_semihost_file_cookie_pool + __llvm_libc_semihost_file_cookie_storage = {default_file_cookies, + DEFAULT_FILE_COOKIE_COUNT}; +struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie; +struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie; +struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie; +} + +namespace { +// File cookie helpers +bool is_std_stream(void *cookie) { + return cookie == &__llvm_libc_stdin_cookie || + cookie == &__llvm_libc_stdout_cookie || + cookie == &__llvm_libc_stderr_cookie; +} + +size_t get_handle(void *cookie) { + return static_cast<__llvm_libc_stdio_cookie *>(cookie)->handle; +} + +__llvm_libc_semihost_file_cookie *allocate_file() { + for (size_t i = 0; i < __llvm_libc_semihost_file_cookie_storage.size; ++i) { + auto *cookie = &__llvm_libc_semihost_file_cookie_storage.cookies[i]; + if (!cookie->in_use) { + cookie->in_use = true; + cookie->position = 0; + return cookie; + } + } + return nullptr; +} + +void release_file(void *cookie) { + static_cast<__llvm_libc_semihost_file_cookie *>(cookie)->in_use = false; +} + +off_t get_file_position(void *cookie) { + return static_cast<__llvm_libc_semihost_file_cookie *>(cookie)->position; +} + +void set_file_position(void *cookie, off_t position) { + static_cast<__llvm_libc_semihost_file_cookie *>(cookie)->position = position; +} + +void advance_file_position(void *cookie, size_t amount) { + set_file_position(cookie, + get_file_position(cookie) + static_cast(amount)); +} +} // namespace + +namespace { +// Helper functions implemented here to avoid dependency on libc which is not +// available in LLVM libc hermetic testing. +int _isspace(char ch) { + return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '\v' || + ch == '\f'; +} + +__attribute__((no_builtin("strlen"))) size_t _strlen(const char *str) { + const char *end = str; + while (*end) + ++end; + return static_cast(end - str); +} + +// Semihosting helpers +int semihost_errno_negative() { + int error = semihosting_call(SYS_ERRNO, nullptr); + return error > 0 ? -error : -EIO; +} + +off_t semihost_file_length(size_t handle) { + size_t args[] = {handle}; + off_t length = semihosting_call(SYS_FLEN, args); + return length < 0 ? semihost_errno_negative() : length; +} + +ssize_t semihost_read(size_t handle, char *buf, size_t size) { size_t args[] = { - reinterpret_cast(std_stream_name), + handle, + reinterpret_cast(buf), + size, + }; + ssize_t not_read = semihosting_call(SYS_READ, args); + return not_read < 0 ? semihost_errno_negative() + : static_cast(size) - not_read; +} + +ssize_t semihost_read_console(char *buf, size_t size) { + for (size_t i = 0; i < size; ++i) { + long ch = semihosting_call(SYS_READC, nullptr); + buf[i] = static_cast(ch & 0xff); + if (buf[i] == '\r') + buf[i] = '\n'; + } + return static_cast(size); +} + +ssize_t semihost_write(size_t handle, const char *buf, size_t size) { + size_t args[] = { + handle, + reinterpret_cast(buf), + size, + }; + ssize_t not_written = semihosting_call(SYS_WRITE, args); + return not_written < 0 ? semihost_errno_negative() + : static_cast(size) - not_written; +} + +int semihost_seek(size_t handle, off_t position) { + size_t args[] = { + handle, + static_cast(position), + }; + return semihosting_call(SYS_SEEK, args) == 0 ? 0 : semihost_errno_negative(); +} + +long semihost_open(const char *path, size_t length, size_t mode) { + size_t args[] = { + reinterpret_cast(path), mode, - sizeof(std_stream_name) - 1UL, + length, }; - cookie->handle = semihosting_call(SYS_OPEN, args); + long handle = semihosting_call(SYS_OPEN, args); + return handle < 0 ? semihost_errno_negative() : handle; +} + +int semihost_close(size_t handle) { + size_t args[] = {handle}; + return semihosting_call(SYS_CLOSE, args); } + +void stdio_cookie_open(struct __llvm_libc_stdio_cookie *cookie, size_t mode) { + const char std_stream_name[] = ":tt"; + cookie->handle = static_cast( + semihost_open(std_stream_name, sizeof(std_stream_name) - 1, mode)); +} + } // namespace extern "C" { @@ -55,41 +194,105 @@ void __llvm_libc_exit(int status) { semihosting_call_exit(status); } -struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie; -struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie; -struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie; +ssize_t __llvm_libc_stdio_read(void *cookie, char *buf, size_t size) { + if (cookie == &__llvm_libc_stdin_cookie) + return semihost_read_console(buf, size); + if (is_std_stream(cookie)) + return -EBADF; + + ssize_t result = semihost_read(get_handle(cookie), buf, size); + if (result < 0) + return result; + advance_file_position(cookie, static_cast(result)); + return result; +} -// Currently only supports reading from stdin. -// We use SYS_READC for reading from stdin as QEMUs SYS_READ does not block. -// For other files SYS_READ should be used as SYS_READC is intended for console -// input and may block indefinitely in QEMU. -// TODO: Extend to handle regular files when implemented in LLVM libc. +ssize_t __llvm_libc_stdio_write(void *cookie, const char *buf, size_t size) { + ssize_t result = semihost_write(get_handle(cookie), buf, size); + if (result < 0) + return result; + if (!is_std_stream(cookie)) + advance_file_position(cookie, static_cast(result)); + return result; +} -ssize_t __llvm_libc_stdio_read(struct __llvm_libc_stdio_cookie *cookie, - char *buf, size_t size) { - if (cookie != &__llvm_libc_stdin_cookie) - return -1; - - for (size_t i = 0; i < size; ++i) { - long ch = semihosting_call(SYS_READC, nullptr); - buf[i] = static_cast(ch & 0xff); - if (buf[i] == '\r') - buf[i] = '\n'; +int __llvm_libc_stdio_open(const char *path, const char *mode, void **cookie) { + auto *file_cookie = allocate_file(); + if (!file_cookie) + return -EMFILE; + + size_t open_mode = OPENMODE_R; + if (mode[0] == 'w') + open_mode = OPENMODE_W; + else if (mode[0] == 'a') + open_mode = OPENMODE_A; + for (const char *option = mode + 1; *option != '\0'; ++option) { + if (*option == '+') + open_mode |= OPENMODE_PLUS; + else if (*option == 'b') + open_mode |= OPENMODE_B; + } + + long handle = semihost_open(path, _strlen(path), open_mode); + if (handle < 0) { + release_file(file_cookie); + return static_cast(handle); } - return size; + file_cookie->stdio_cookie.handle = static_cast(handle); + if (mode[0] == 'a') { + off_t length = semihost_file_length(get_handle(file_cookie)); + if (length < 0) { + semihost_close(get_handle(file_cookie)); + release_file(file_cookie); + return static_cast(length); + } + set_file_position(file_cookie, length); + } + *cookie = file_cookie; + return 0; } -ssize_t __llvm_libc_stdio_write(struct __llvm_libc_stdio_cookie *cookie, - const char *buf, size_t size) { - size_t args[] = { - static_cast(cookie->handle), - reinterpret_cast(buf), - size, - }; - ssize_t retval = semihosting_call(SYS_WRITE, args); - if (retval >= 0) - retval = size - retval; - return retval; +off_t __llvm_libc_stdio_seek(void *cookie, off_t offset, int whence) { + if (is_std_stream(cookie)) + return -ESPIPE; + + off_t base; + if (whence == SEEK_SET) + base = 0; + else if (whence == SEEK_CUR) + base = get_file_position(cookie); + else if (whence == SEEK_END) { + base = semihost_file_length(get_handle(cookie)); + if (base < 0) + return base; + } else + return -EINVAL; + + off_t position = base + offset; + if (position < 0) + return -EINVAL; + int error = semihost_seek(get_handle(cookie), position); + if (error) + return error; + set_file_position(cookie, position); + return position; +} + +int __llvm_libc_stdio_set_buffer(void *, char *, size_t, int) { + // Semihost streams are unbuffered. + return 0; +} + +int __llvm_libc_stdio_flush(void *) { + // Semihost streams are unbuffered. + return 0; +} + +int __llvm_libc_stdio_close(void *cookie) { + int result = semihost_close(get_handle(cookie)); + if (!is_std_stream(cookie)) + release_file(cookie); + return result; } bool __llvm_libc_timespec_get_active(struct timespec *ts) { @@ -114,9 +317,9 @@ bool __llvm_libc_timespec_get_utc(struct timespec *ts) { // Entry point void _platform_init(void) { - stdio_open(&__llvm_libc_stdin_cookie, OPENMODE_R); - stdio_open(&__llvm_libc_stdout_cookie, OPENMODE_W); - stdio_open(&__llvm_libc_stderr_cookie, OPENMODE_W); + stdio_cookie_open(&__llvm_libc_stdin_cookie, OPENMODE_R); + stdio_cookie_open(&__llvm_libc_stdout_cookie, OPENMODE_W); + stdio_cookie_open(&__llvm_libc_stderr_cookie, OPENMODE_W); } // Debug output @@ -135,21 +338,6 @@ void _platform_debug_putc(int c) { // - Escape sequences: \ copies next char as-is unless inside ' quotes // or at the end of the string. -// Helper functions implemented here to avoid dependency on libc which is not -// available in LLVM libc hermetic testing. -static int _isspace(char ch) { - return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '\v' || - ch == '\f'; -} - -__attribute__((no_builtin("strlen"))) static size_t _strlen(const char *str) { - const char *pend = str; - while (*pend) { - pend++; - } - return (size_t)(pend - str); -} - static inline void skip_spaces(const char *&p) { while (_isspace(*p)) ++p; diff --git a/arm-software/embedded/llvmlibc-support/semihost/semihost.h b/arm-software/embedded/llvmlibc-support/semihost/semihost.h index d21ee2ecdaa3..600b78fb0cca 100644 --- a/arm-software/embedded/llvmlibc-support/semihost/semihost.h +++ b/arm-software/embedded/llvmlibc-support/semihost/semihost.h @@ -13,6 +13,8 @@ #ifndef LLVMET_LLVMLIBC_SUPPORT_SEMIHOST_H #define LLVMET_LLVMLIBC_SUPPORT_SEMIHOST_H +#include +#include #include #include @@ -112,6 +114,22 @@ inline constexpr uint32_t OPENMODE_B = 1; inline constexpr uint32_t OPENMODE_PLUS = 2; } // namespace -struct __llvm_libc_stdio_cookie { int handle; }; +struct __llvm_libc_stdio_cookie { + size_t handle; +}; + +struct __llvm_libc_semihost_file_cookie { + __llvm_libc_stdio_cookie stdio_cookie; + bool in_use; + off_t position; +}; + +struct __llvm_libc_semihost_file_cookie_pool { + __llvm_libc_semihost_file_cookie *cookies; + size_t size; +}; + +extern "C" __llvm_libc_semihost_file_cookie_pool + __llvm_libc_semihost_file_cookie_storage; #endif // LLVMET_LLVMLIBC_SUPPORT_SEMIHOST_H diff --git a/libc/config/baremetal/aarch64/entrypoints.txt b/libc/config/baremetal/aarch64/entrypoints.txt index 6c9733799f01..255bbf577ffa 100644 --- a/libc/config/baremetal/aarch64/entrypoints.txt +++ b/libc/config/baremetal/aarch64/entrypoints.txt @@ -128,14 +128,22 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.stdio.feof libc.src.stdio.ferror libc.src.stdio.fflush + libc.src.stdio.fclose libc.src.stdio.fgetc libc.src.stdio.fgets libc.src.stdio.fprintf libc.src.stdio.fputc libc.src.stdio.fputs + libc.src.stdio.fopen libc.src.stdio.fread libc.src.stdio.fscanf libc.src.stdio.fwrite + libc.src.stdio.fseek + libc.src.stdio.fseeko + libc.src.stdio.ftell + libc.src.stdio.ftello + libc.src.stdio.setbuf + libc.src.stdio.setvbuf libc.src.stdio.getc libc.src.stdio.getchar libc.src.stdio.printf diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt index a6b0dc50d030..81b1cb90687c 100644 --- a/libc/config/baremetal/arm/entrypoints.txt +++ b/libc/config/baremetal/arm/entrypoints.txt @@ -128,14 +128,22 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.stdio.feof libc.src.stdio.ferror libc.src.stdio.fflush + libc.src.stdio.fclose libc.src.stdio.fgetc libc.src.stdio.fgets libc.src.stdio.fprintf libc.src.stdio.fputc libc.src.stdio.fputs + libc.src.stdio.fopen libc.src.stdio.fread libc.src.stdio.fscanf libc.src.stdio.fwrite + libc.src.stdio.fseek + libc.src.stdio.fseeko + libc.src.stdio.ftell + libc.src.stdio.ftello + libc.src.stdio.setbuf + libc.src.stdio.setvbuf libc.src.stdio.getc libc.src.stdio.getchar libc.src.stdio.printf diff --git a/libc/src/__support/OSUtil/baremetal/io.h b/libc/src/__support/OSUtil/baremetal/io.h index 5e691f26060e..bb270b4b7b6e 100644 --- a/libc/src/__support/OSUtil/baremetal/io.h +++ b/libc/src/__support/OSUtil/baremetal/io.h @@ -9,6 +9,7 @@ #ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_BAREMETAL_IO_H #define LLVM_LIBC_SRC___SUPPORT_OSUTIL_BAREMETAL_IO_H +#include "include/llvm-libc-types/off_t.h" #include "include/llvm-libc-types/size_t.h" #include "include/llvm-libc-types/ssize_t.h" #include "src/__support/CPP/string_view.h" @@ -47,10 +48,36 @@ namespace LIBC_NAMESPACE_DECL { struct __llvm_libc_stdio_cookie; +// On success, store a non-null application-owned cookie in `cookie` and return +// 0. On failure, return a negative errno value. +extern "C" int __llvm_libc_stdio_open(const char *path, const char *mode, + void **cookie); + +// Return the number of bytes read, which can be less than `size` and is zero at +// end-of-file. On failure, return a negative errno value. extern "C" ssize_t __llvm_libc_stdio_read(void *cookie, char *buf, size_t size); + +// Return the number of bytes written, which can be less than `size`. On +// failure, return a negative errno value. extern "C" ssize_t __llvm_libc_stdio_write(void *cookie, const char *buf, size_t size); +// Return the resulting absolute file position on success. On failure, return a +// negative errno value. +extern "C" off_t __llvm_libc_stdio_seek(void *cookie, off_t offset, int whence); + +// Configure buffering for `cookie`. Return 0 on success or nonzero on failure, +// matching setvbuf. The application owns any supplied buffer. +extern "C" int __llvm_libc_stdio_set_buffer(void *cookie, char *buffer, + size_t size, int mode); + +// Flush buffered output for `cookie`, or all output streams if `cookie` is +// null. Return 0 on success or EOF on failure, matching fflush. +extern "C" int __llvm_libc_stdio_flush(void *cookie); + +// Return 0 on success or EOF on failure, matching fclose. +extern "C" int __llvm_libc_stdio_close(void *cookie); + void write_to_stderr(cpp::string_view msg); } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt index 68ee57fae646..c4db9834e7ce 100644 --- a/libc/src/stdio/CMakeLists.txt +++ b/libc/src/stdio/CMakeLists.txt @@ -67,32 +67,6 @@ add_entrypoint_object( libc.src.__support.File.file ) -add_entrypoint_object( - setbuf - SRCS - setbuf.cpp - HDRS - setbuf.h - DEPENDS - libc.src.errno.errno - libc.hdr.types.off_t - libc.src.__support.File.file - libc.src.__support.File.platform_file -) - -add_entrypoint_object( - setvbuf - SRCS - setvbuf.cpp - HDRS - setvbuf.h - DEPENDS - libc.src.errno.errno - libc.hdr.types.FILE - libc.src.__support.File.file - libc.src.__support.File.platform_file -) - add_entrypoint_object( sscanf SRCS @@ -274,6 +248,8 @@ add_stdio_entrypoint_object(ftell) add_stdio_entrypoint_object(fseeko) add_stdio_entrypoint_object(fileno) add_stdio_entrypoint_object(ftello) +add_stdio_entrypoint_object(setbuf) +add_stdio_entrypoint_object(setvbuf) add_stdio_entrypoint_object(fflush) add_stdio_entrypoint_object(clearerr) add_stdio_entrypoint_object(clearerr_unlocked) diff --git a/libc/src/stdio/baremetal/CMakeLists.txt b/libc/src/stdio/baremetal/CMakeLists.txt index d26cdbb0f9d5..d0734d5531dd 100644 --- a/libc/src/stdio/baremetal/CMakeLists.txt +++ b/libc/src/stdio/baremetal/CMakeLists.txt @@ -10,6 +10,62 @@ add_object_library( libc.src.__support.OSUtil.osutil ) +add_entrypoint_object( + fopen + SRCS fopen.cpp + HDRS ../fopen.h + DEPENDS libc.hdr.errno_macros libc.src.__support.OSUtil.osutil libc.src.errno.errno +) + +add_entrypoint_object( + fclose + SRCS fclose.cpp + HDRS ../fclose.h + DEPENDS libc.src.__support.OSUtil.osutil +) + +add_entrypoint_object( + fseek + SRCS fseek.cpp + HDRS ../fseek.h + DEPENDS libc.hdr.errno_macros libc.src.__support.OSUtil.osutil libc.src.errno.errno +) + +add_entrypoint_object( + ftell + SRCS ftell.cpp + HDRS ../ftell.h + DEPENDS libc.hdr.errno_macros libc.hdr.stdio_macros libc.src.__support.OSUtil.osutil libc.src.errno.errno +) + +add_entrypoint_object( + fseeko + SRCS fseeko.cpp + HDRS ../fseeko.h + DEPENDS libc.hdr.errno_macros libc.src.__support.OSUtil.osutil libc.src.errno.errno +) + +add_entrypoint_object( + ftello + SRCS ftello.cpp + HDRS ../ftello.h + DEPENDS libc.hdr.errno_macros libc.hdr.stdio_macros libc.src.__support.OSUtil.osutil libc.src.errno.errno +) + +add_entrypoint_object( + setbuf + SRCS setbuf.cpp + HDRS ../setbuf.h + DEPENDS libc.hdr.stdio_macros libc.hdr.types.FILE libc.src.__support.OSUtil.osutil +) + +add_entrypoint_object( + setvbuf + SRCS setvbuf.cpp + HDRS ../setvbuf.h + DEPENDS libc.hdr.types.FILE libc.src.__support.OSUtil.osutil +) + add_header_library( vfprintf_internal HDRS @@ -98,6 +154,7 @@ add_entrypoint_object( ../fflush.h DEPENDS .file_internal + libc.src.__support.OSUtil.osutil ) add_entrypoint_object( diff --git a/libc/src/stdio/baremetal/fclose.cpp b/libc/src/stdio/baremetal/fclose.cpp new file mode 100644 index 000000000000..2714a11c681c --- /dev/null +++ b/libc/src/stdio/baremetal/fclose.cpp @@ -0,0 +1,12 @@ +#include "src/stdio/fclose.h" + +#include "src/__support/OSUtil/io.h" +#include "src/__support/common.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, fclose, (::FILE * stream)) { + return __llvm_libc_stdio_close(stream); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/baremetal/fflush.cpp b/libc/src/stdio/baremetal/fflush.cpp index 4a39c25ae11c..136e98cc7dae 100644 --- a/libc/src/stdio/baremetal/fflush.cpp +++ b/libc/src/stdio/baremetal/fflush.cpp @@ -8,15 +8,13 @@ #include "src/stdio/fflush.h" +#include "src/__support/OSUtil/io.h" #include "src/__support/common.h" namespace LIBC_NAMESPACE_DECL { -// Baremetal uses unbuffered I/O, so there is nothing to flush. LLVM_LIBC_FUNCTION(int, fflush, (::FILE * stream)) { - (void)stream; - // TODO: Shall we have an embedding API for fflush? - return 0; + return __llvm_libc_stdio_flush(stream); } } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/baremetal/fopen.cpp b/libc/src/stdio/baremetal/fopen.cpp new file mode 100644 index 000000000000..1d7cfe98f2cb --- /dev/null +++ b/libc/src/stdio/baremetal/fopen.cpp @@ -0,0 +1,25 @@ +#include "src/stdio/fopen.h" + +#include "hdr/errno_macros.h" +#include "src/__support/OSUtil/io.h" +#include "src/__support/common.h" +#include "src/__support/libc_errno.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(::FILE *, fopen, + (const char *__restrict path, const char *__restrict mode)) { + if (path == nullptr || mode == nullptr || mode[0] == '\0') { + libc_errno = EINVAL; + return nullptr; + } + void *cookie = nullptr; + int result = __llvm_libc_stdio_open(path, mode, &cookie); + if (result != 0 || cookie == nullptr) { + libc_errno = result != 0 ? -result : EINVAL; + return nullptr; + } + return reinterpret_cast<::FILE *>(cookie); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/baremetal/fseek.cpp b/libc/src/stdio/baremetal/fseek.cpp new file mode 100644 index 000000000000..f7db74786fd3 --- /dev/null +++ b/libc/src/stdio/baremetal/fseek.cpp @@ -0,0 +1,23 @@ +#include "src/stdio/fseek.h" + +#include "hdr/errno_macros.h" +#include "src/__support/OSUtil/io.h" +#include "src/__support/common.h" +#include "src/__support/libc_errno.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, fseek, (::FILE * stream, long offset, int whence)) { + if (stream == nullptr) { + libc_errno = EINVAL; + return -1; + } + off_t result = __llvm_libc_stdio_seek(stream, offset, whence); + if (result < 0) { + libc_errno = static_cast(-result); + return -1; + } + return 0; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/baremetal/fseeko.cpp b/libc/src/stdio/baremetal/fseeko.cpp new file mode 100644 index 000000000000..c5b32b1158e1 --- /dev/null +++ b/libc/src/stdio/baremetal/fseeko.cpp @@ -0,0 +1,31 @@ +//===-- Implementation of fseeko for baremetal -----------------*- 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 "src/stdio/fseeko.h" + +#include "hdr/errno_macros.h" +#include "src/__support/OSUtil/io.h" +#include "src/__support/common.h" +#include "src/__support/libc_errno.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, fseeko, (::FILE * stream, off_t offset, int whence)) { + if (stream == nullptr) { + libc_errno = EINVAL; + return -1; + } + off_t result = __llvm_libc_stdio_seek(stream, offset, whence); + if (result < 0) { + libc_errno = static_cast(-result); + return -1; + } + return 0; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/baremetal/ftell.cpp b/libc/src/stdio/baremetal/ftell.cpp new file mode 100644 index 000000000000..7ebb4d059622 --- /dev/null +++ b/libc/src/stdio/baremetal/ftell.cpp @@ -0,0 +1,24 @@ +#include "src/stdio/ftell.h" + +#include "hdr/errno_macros.h" +#include "hdr/stdio_macros.h" +#include "src/__support/OSUtil/io.h" +#include "src/__support/common.h" +#include "src/__support/libc_errno.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(long, ftell, (::FILE * stream)) { + if (stream == nullptr) { + libc_errno = EINVAL; + return -1; + } + off_t result = __llvm_libc_stdio_seek(stream, 0, SEEK_CUR); + if (result < 0) { + libc_errno = static_cast(-result); + return -1; + } + return static_cast(result); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/baremetal/ftello.cpp b/libc/src/stdio/baremetal/ftello.cpp new file mode 100644 index 000000000000..84ae74a72da7 --- /dev/null +++ b/libc/src/stdio/baremetal/ftello.cpp @@ -0,0 +1,32 @@ +//===-- Implementation of ftello for baremetal -----------------*- 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 "src/stdio/ftello.h" + +#include "hdr/errno_macros.h" +#include "hdr/stdio_macros.h" +#include "src/__support/OSUtil/io.h" +#include "src/__support/common.h" +#include "src/__support/libc_errno.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(off_t, ftello, (::FILE * stream)) { + if (stream == nullptr) { + libc_errno = EINVAL; + return static_cast(-1); + } + off_t result = __llvm_libc_stdio_seek(stream, 0, SEEK_CUR); + if (result < 0) { + libc_errno = static_cast(-result); + return static_cast(-1); + } + return result; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/baremetal/setbuf.cpp b/libc/src/stdio/baremetal/setbuf.cpp new file mode 100644 index 000000000000..482f0ac43c75 --- /dev/null +++ b/libc/src/stdio/baremetal/setbuf.cpp @@ -0,0 +1,22 @@ +//===-- Bare-metal implementation of setbuf ------------------------------===// +// +// 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/stdio/setbuf.h" +#include "hdr/stdio_macros.h" +#include "src/__support/OSUtil/io.h" +#include "src/__support/common.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(void, setbuf, + (::FILE *__restrict stream, char *__restrict buffer)) { + __llvm_libc_stdio_set_buffer(stream, buffer, BUFSIZ, + buffer == nullptr ? _IONBF : _IOFBF); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/baremetal/setvbuf.cpp b/libc/src/stdio/baremetal/setvbuf.cpp new file mode 100644 index 000000000000..4235230a10ef --- /dev/null +++ b/libc/src/stdio/baremetal/setvbuf.cpp @@ -0,0 +1,21 @@ +//===-- Bare-metal implementation of setvbuf -----------------------------===// +// +// 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/stdio/setvbuf.h" +#include "src/__support/OSUtil/io.h" +#include "src/__support/common.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, setvbuf, + (::FILE *__restrict stream, char *__restrict buffer, + int mode, size_t size)) { + return __llvm_libc_stdio_set_buffer(stream, buffer, size, mode); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/generic/CMakeLists.txt b/libc/src/stdio/generic/CMakeLists.txt index b6bb41b5053a..3df8ab501c4a 100644 --- a/libc/src/stdio/generic/CMakeLists.txt +++ b/libc/src/stdio/generic/CMakeLists.txt @@ -10,6 +10,32 @@ function(add_generic_entrypoint_object name) endif() endfunction() +add_generic_entrypoint_object( + setbuf + SRCS + ../setbuf.cpp + HDRS + ../setbuf.h + DEPENDS + libc.src.errno.errno + libc.hdr.types.off_t + libc.src.__support.File.file + libc.src.__support.File.platform_file +) + +add_generic_entrypoint_object( + setvbuf + SRCS + ../setvbuf.cpp + HDRS + ../setvbuf.h + DEPENDS + libc.src.errno.errno + libc.hdr.types.FILE + libc.src.__support.File.file + libc.src.__support.File.platform_file +) + add_generic_entrypoint_object( clearerr SRCS diff --git a/libcxx/include/fstream b/libcxx/include/fstream index c2296fe42729..fc06b2965b8c 100644 --- a/libcxx/include/fstream +++ b/libcxx/include/fstream @@ -192,7 +192,7 @@ typedef basic_fstream wfstream; # include <__config> // Downstream issue: #375 (Enable fstream independently of filesystem) -# if _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION || _LIBCPP_LIBC_NEWLIB +# if _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION || _LIBCPP_LIBC_NEWLIB || _LIBCPP_LIBC_LLVM_LIBC # include <__algorithm/max.h> # include <__assert> @@ -242,6 +242,8 @@ public: using native_handle_type = void*; // HANDLE # elif __has_include() using native_handle_type = int; // POSIX file descriptor +# elif _LIBCPP_LIBC_LLVM_LIBC + using native_handle_type = FILE*; # else # error "Provide a native file handle!" # endif @@ -278,6 +280,8 @@ public: return std::__filebuf_windows_native_handle(__file_); # elif __has_include() return fileno(__file_); +# elif _LIBCPP_LIBC_LLVM_LIBC + return __file_; # else # error "Provide a way to determine the file native handle!" # endif @@ -1640,7 +1644,7 @@ _LIBCPP_END_NAMESPACE_STD _LIBCPP_POP_MACROS -# endif // _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION +# endif // _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION || _LIBCPP_LIBC_NEWLIB || _LIBCPP_LIBC_LLVM_LIBC #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS) diff --git a/libcxx/src/ios.instantiations.cpp b/libcxx/src/ios.instantiations.cpp index a6e56395617a..98d6f53f7802 100644 --- a/libcxx/src/ios.instantiations.cpp +++ b/libcxx/src/ios.instantiations.cpp @@ -58,7 +58,7 @@ template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_ostringstream; // Downstream issue: #375 (Enable fstream independently of filesystem) -#if _LIBCPP_HAS_FILESYSTEM || _LIBCPP_LIBC_NEWLIB +#if _LIBCPP_HAS_FILESYSTEM || _LIBCPP_LIBC_NEWLIB || _LIBCPP_LIBC_LLVM_LIBC template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_ifstream; template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_ofstream; template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_filebuf;