From 66e8d6137d780cc9f1b221c031de9969207d0568 Mon Sep 17 00:00:00 2001 From: William Huynh Date: Fri, 8 Aug 2025 16:40:14 +0100 Subject: [PATCH 1/4] [libc] Migrate from baremetal stdio.h to generic stdio.h This is a follow up to the RFC here: https://discourse.llvm.org/t/rfc-implementation-of-stdio-on-baremetal/86944 Currently, this is very rough and this PR is only for feedback. This provides the stdout/stderr/stdin symbols (which now don't have to provided by the user). This allows the user to have access to all functions, currently I've only tested `fprintf` but in theory everything that works in the generic folder should work in the baremetal configuration. The most important part of the implementation is: - where we want to put `std*`. Here it is a part of `libc.a` - if we want it here, what should we set it to? I have aliased all the streams to a _non-buffered_ stream, which does NOT require flushing. It is based on the CookieFile that already existed --- libc/config/baremetal/aarch64/entrypoints.txt | 2 + libc/config/baremetal/arm/entrypoints.txt | 2 + libc/config/baremetal/riscv/entrypoints.txt | 2 + libc/src/__support/File/CMakeLists.txt | 29 +++++- libc/src/__support/OSUtil/baremetal/io.cpp | 39 -------- libc/src/__support/OSUtil/baremetal/io.h | 38 ++++++++ libc/src/stdio/baremetal/CMakeLists.txt | 96 ------------------- libc/src/stdio/baremetal/printf.cpp | 54 ----------- libc/src/stdio/baremetal/putchar.cpp | 24 ----- libc/src/stdio/baremetal/puts.cpp | 26 ----- libc/src/stdio/baremetal/vprintf.cpp | 52 ---------- libc/src/stdio/fopencookie.cpp | 67 +------------ 12 files changed, 72 insertions(+), 359 deletions(-) delete mode 100644 libc/src/stdio/baremetal/printf.cpp delete mode 100644 libc/src/stdio/baremetal/putchar.cpp delete mode 100644 libc/src/stdio/baremetal/puts.cpp delete mode 100644 libc/src/stdio/baremetal/vprintf.cpp diff --git a/libc/config/baremetal/aarch64/entrypoints.txt b/libc/config/baremetal/aarch64/entrypoints.txt index e24e2b9e2a7bb..787fa5b6eb519 100644 --- a/libc/config/baremetal/aarch64/entrypoints.txt +++ b/libc/config/baremetal/aarch64/entrypoints.txt @@ -124,6 +124,8 @@ set(TARGET_LIBC_ENTRYPOINTS # stdio.h entrypoints libc.src.stdio.asprintf + libc.src.stdio.fopencookie + libc.src.stdio.fprintf libc.src.stdio.getchar libc.src.stdio.printf libc.src.stdio.putchar diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt index 44e9c3edac353..4b19b4267e20d 100644 --- a/libc/config/baremetal/arm/entrypoints.txt +++ b/libc/config/baremetal/arm/entrypoints.txt @@ -124,6 +124,8 @@ set(TARGET_LIBC_ENTRYPOINTS # stdio.h entrypoints libc.src.stdio.asprintf + libc.src.stdio.fopencookie + libc.src.stdio.fprintf libc.src.stdio.getchar libc.src.stdio.printf libc.src.stdio.putchar diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt index 29cf322a2e33f..7198dab044532 100644 --- a/libc/config/baremetal/riscv/entrypoints.txt +++ b/libc/config/baremetal/riscv/entrypoints.txt @@ -124,6 +124,8 @@ set(TARGET_LIBC_ENTRYPOINTS # stdio.h entrypoints libc.src.stdio.asprintf + libc.src.stdio.fopencookie + libc.src.stdio.fprintf libc.src.stdio.getchar libc.src.stdio.printf libc.src.stdio.putchar diff --git a/libc/src/__support/File/CMakeLists.txt b/libc/src/__support/File/CMakeLists.txt index 253243ff40ded..b351953f8409c 100644 --- a/libc/src/__support/File/CMakeLists.txt +++ b/libc/src/__support/File/CMakeLists.txt @@ -22,6 +22,20 @@ add_object_library( libc.src.__support.error_or ) +add_header_library( + cookie_file + HDRS + cookie_file.h + DEPENDS + .file + libc.hdr.errno_macros + libc.hdr.stdio_macros + libc.hdr.types.FILE + libc.hdr.types.cookie_io_functions_t + libc.src.__support.CPP.new + libc.src.__support.File.file +) + add_object_library( dir SRCS @@ -41,10 +55,17 @@ endif() add_subdirectory(${LIBC_TARGET_OS}) -set(target_file libc.src.__support.File.${LIBC_TARGET_OS}.file) -set(target_stdout libc.src.__support.File.${LIBC_TARGET_OS}.stdout) -set(target_stderr libc.src.__support.File.${LIBC_TARGET_OS}.stderr) -set(target_stdin libc.src.__support.File.${LIBC_TARGET_OS}.stdin) +if(LIBC_TARGET_OS_IS_BAREMETAL) + set(target_file libc.src.__support.File.cookie_file) + set(target_stdout libc.src.__support.File.${LIBC_TARGET_OS}.stderr) + set(target_stderr libc.src.__support.File.${LIBC_TARGET_OS}.stderr) + set(target_stdin libc.src.__support.File.${LIBC_TARGET_OS}.stderr) +else() + set(target_file libc.src.__support.File.${LIBC_TARGET_OS}.file) + set(target_stdout libc.src.__support.File.${LIBC_TARGET_OS}.stdout) + set(target_stderr libc.src.__support.File.${LIBC_TARGET_OS}.stderr) + set(target_stdin libc.src.__support.File.${LIBC_TARGET_OS}.stdin) +endif() set(file_targets "${target_file};${target_stdout};${target_stdin};${target_stderr}") set(file_aliases "platform_file;platform_stdout;platform_stdin;platform_stderr") diff --git a/libc/src/__support/OSUtil/baremetal/io.cpp b/libc/src/__support/OSUtil/baremetal/io.cpp index 2a9ef6bfa6579..6b475209d421c 100644 --- a/libc/src/__support/OSUtil/baremetal/io.cpp +++ b/libc/src/__support/OSUtil/baremetal/io.cpp @@ -13,45 +13,6 @@ namespace LIBC_NAMESPACE_DECL { -// These are intended to be provided by the vendor. -// -// The signature of these types and functions intentionally match `fopencookie` -// which allows the following: -// -// ``` -// struct __llvm_libc_stdio_cookie { ... }; -// ... -// struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie; -// cookie_io_functions_t stdin_func = { .read = __llvm_libc_stdio_read }; -// FILE *stdin = fopencookie(&__llvm_libc_stdin_cookie, "r", stdin_func); -// ... -// struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie; -// cookie_io_functions_t stdout_func = { .write = __llvm_libc_stdio_write }; -// FILE *stdout = fopencookie(&__llvm_libc_stdout_cookie, "w", stdout_func); -// ... -// struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie; -// cookie_io_functions_t stderr_func = { .write = __llvm_libc_stdio_write }; -// FILE *stderr = fopencookie(&__llvm_libc_stderr_cookie, "w", stderr_func); -// ``` -// -// At the same time, implementation of functions like `printf` and `scanf` can -// use `__llvm_libc_stdio_read` and `__llvm_libc_stdio_write` directly to avoid -// the extra indirection. -// -// All three symbols `__llvm_libc_stdin_cookie`, `__llvm_libc_stdout_cookie`, -// and `__llvm_libc_stderr_cookie` must be provided, even if they don't point -// at anything. - -struct __llvm_libc_stdio_cookie; - -extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie; -extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie; -extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie; - -extern "C" ssize_t __llvm_libc_stdio_read(void *cookie, char *buf, size_t size); -extern "C" ssize_t __llvm_libc_stdio_write(void *cookie, const char *buf, - size_t size); - ssize_t read_from_stdin(char *buf, size_t size) { return __llvm_libc_stdio_read(static_cast(&__llvm_libc_stdin_cookie), buf, size); diff --git a/libc/src/__support/OSUtil/baremetal/io.h b/libc/src/__support/OSUtil/baremetal/io.h index aed34ec7e62e3..b6c5ffb21c1a8 100644 --- a/libc/src/__support/OSUtil/baremetal/io.h +++ b/libc/src/__support/OSUtil/baremetal/io.h @@ -14,8 +14,46 @@ #include "src/__support/CPP/string_view.h" #include "src/__support/macros/config.h" +// These are intended to be provided by the vendor. +// +// The signature of these types and functions intentionally match `fopencookie` +// which allows the following: +// +// ``` +// struct __llvm_libc_stdio_cookie { ... }; +// ... +// struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie; +// cookie_io_functions_t stdin_func = { .read = __llvm_libc_stdio_read }; +// FILE *stdin = fopencookie(&__llvm_libc_stdin_cookie, "r", stdin_func); +// ... +// struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie; +// cookie_io_functions_t stdout_func = { .write = __llvm_libc_stdio_write }; +// FILE *stdout = fopencookie(&__llvm_libc_stdout_cookie, "w", stdout_func); +// ... +// struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie; +// cookie_io_functions_t stderr_func = { .write = __llvm_libc_stdio_write }; +// FILE *stderr = fopencookie(&__llvm_libc_stderr_cookie, "w", stderr_func); +// ``` +// +// At the same time, implementation of functions like `printf` and `scanf` can +// use `__llvm_libc_stdio_read` and `__llvm_libc_stdio_write` directly to avoid +// the extra indirection. +// +// All three symbols `__llvm_libc_stdin_cookie`, `__llvm_libc_stdout_cookie`, +// and `__llvm_libc_stderr_cookie` must be provided, even if they don't point +// at anything. namespace LIBC_NAMESPACE_DECL { +struct __llvm_libc_stdio_cookie; + +extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie; +extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie; +extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie; + +extern "C" ssize_t __llvm_libc_stdio_read(void *cookie, char *buf, size_t size); +extern "C" ssize_t __llvm_libc_stdio_write(void *cookie, const char *buf, + size_t size); + ssize_t read_from_stdin(char *buf, size_t size); void write_to_stderr(cpp::string_view msg); void write_to_stdout(cpp::string_view msg); diff --git a/libc/src/stdio/baremetal/CMakeLists.txt b/libc/src/stdio/baremetal/CMakeLists.txt index 548938f885c94..65089274050bd 100644 --- a/libc/src/stdio/baremetal/CMakeLists.txt +++ b/libc/src/stdio/baremetal/CMakeLists.txt @@ -1,15 +1,3 @@ -add_entrypoint_object( - getchar - SRCS - getchar.cpp - HDRS - ../getchar.h - DEPENDS - libc.hdr.stdio_macros - libc.src.__support.OSUtil.osutil - libc.src.__support.CPP.string_view -) - add_entrypoint_object( remove SRCS @@ -19,87 +7,3 @@ add_entrypoint_object( DEPENDS libc.include.stdio ) - -add_entrypoint_object( - printf - SRCS - printf.cpp - HDRS - ../printf.h - DEPENDS - libc.src.stdio.printf_core.printf_main - libc.src.stdio.printf_core.writer - libc.src.__support.arg_list - libc.src.__support.OSUtil.osutil -) - -add_entrypoint_object( - putchar - SRCS - putchar.cpp - HDRS - ../putchar.h - DEPENDS - libc.src.__support.OSUtil.osutil - libc.src.__support.CPP.string_view -) - -add_entrypoint_object( - puts - SRCS - puts.cpp - HDRS - ../puts.h - DEPENDS - libc.src.__support.OSUtil.osutil - libc.src.__support.CPP.string_view -) - -add_header_library( - scanf_internal - HDRS - scanf_internal.h - DEPENDS - libc.src.stdio.scanf_core.reader - libc.src.__support.OSUtil.osutil -) - -add_entrypoint_object( - scanf - SRCS - scanf.cpp - HDRS - ../scanf.h - DEPENDS - .scanf_internal - libc.include.inttypes - libc.src.stdio.scanf_core.scanf_main - libc.src.__support.arg_list - libc.src.__support.OSUtil.osutil -) - -add_entrypoint_object( - vprintf - SRCS - vprintf.cpp - HDRS - ../vprintf.h - DEPENDS - libc.src.stdio.printf_core.printf_main - libc.src.stdio.printf_core.writer - libc.src.__support.arg_list - libc.src.__support.OSUtil.osutil -) - -add_entrypoint_object( - vscanf - SRCS - vscanf.cpp - HDRS - ../vscanf.h - DEPENDS - .scanf_internal - libc.src.stdio.scanf_core.scanf_main - libc.src.__support.arg_list - libc.src.__support.OSUtil.osutil -) diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp deleted file mode 100644 index 7253c6549a4e4..0000000000000 --- a/libc/src/stdio/baremetal/printf.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//===-- Implementation of printf 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/printf.h" -#include "src/__support/OSUtil/io.h" -#include "src/__support/arg_list.h" -#include "src/__support/macros/config.h" -#include "src/stdio/printf_core/core_structs.h" -#include "src/stdio/printf_core/printf_main.h" -#include "src/stdio/printf_core/writer.h" - -#include -#include - -namespace LIBC_NAMESPACE_DECL { - -namespace { - -LIBC_INLINE int stdout_write_hook(cpp::string_view new_str, void *) { - write_to_stdout(new_str); - return printf_core::WRITE_OK; -} - -} // namespace - -LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) { - va_list vlist; - va_start(vlist, format); - internal::ArgList args(vlist); // This holder class allows for easier copying - // and pointer semantics, as well as handling - // destruction automatically. - va_end(vlist); - static constexpr size_t BUFF_SIZE = 1024; - char buffer[BUFF_SIZE]; - - printf_core::WriteBuffer wb( - buffer, BUFF_SIZE, &stdout_write_hook, nullptr); - printf_core::Writer writer(wb); - - int retval = printf_core::printf_main(&writer, format, args); - - int flushval = wb.overflow_write(""); - if (flushval != printf_core::WRITE_OK) - retval = flushval; - - return retval; -} - -} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/baremetal/putchar.cpp b/libc/src/stdio/baremetal/putchar.cpp deleted file mode 100644 index ac21e6e783b01..0000000000000 --- a/libc/src/stdio/baremetal/putchar.cpp +++ /dev/null @@ -1,24 +0,0 @@ -//===-- Baremetal Implementation of putchar -------------------------------===// -// -// 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/putchar.h" -#include "src/__support/CPP/string_view.h" -#include "src/__support/OSUtil/io.h" -#include "src/__support/macros/config.h" - -namespace LIBC_NAMESPACE_DECL { - -LLVM_LIBC_FUNCTION(int, putchar, (int c)) { - char uc = static_cast(c); - - write_to_stdout(cpp::string_view(&uc, 1)); - - return 0; -} - -} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/baremetal/puts.cpp b/libc/src/stdio/baremetal/puts.cpp deleted file mode 100644 index fcd3aa086b2bf..0000000000000 --- a/libc/src/stdio/baremetal/puts.cpp +++ /dev/null @@ -1,26 +0,0 @@ -//===-- Implementation of puts for baremetal-------------------------------===// -// -// 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/puts.h" -#include "src/__support/CPP/string_view.h" -#include "src/__support/OSUtil/io.h" -#include "src/__support/macros/config.h" - -namespace LIBC_NAMESPACE_DECL { - -LLVM_LIBC_FUNCTION(int, puts, (const char *__restrict str)) { - cpp::string_view str_view(str); - - // TODO: Can we combine these to avoid needing two writes? - write_to_stdout(str_view); - write_to_stdout("\n"); - - return 0; -} - -} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/baremetal/vprintf.cpp b/libc/src/stdio/baremetal/vprintf.cpp deleted file mode 100644 index ab02533f14911..0000000000000 --- a/libc/src/stdio/baremetal/vprintf.cpp +++ /dev/null @@ -1,52 +0,0 @@ -//===-- Implementation of vprintf -------------------------------*- 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/vprintf.h" -#include "src/__support/OSUtil/io.h" -#include "src/__support/arg_list.h" -#include "src/__support/macros/config.h" -#include "src/stdio/printf_core/core_structs.h" -#include "src/stdio/printf_core/printf_main.h" -#include "src/stdio/printf_core/writer.h" - -#include -#include - -namespace LIBC_NAMESPACE_DECL { - -namespace { - -LIBC_INLINE int stdout_write_hook(cpp::string_view new_str, void *) { - write_to_stdout(new_str); - return printf_core::WRITE_OK; -} - -} // namespace - -LLVM_LIBC_FUNCTION(int, vprintf, - (const char *__restrict format, va_list vlist)) { - internal::ArgList args(vlist); // This holder class allows for easier copying - // and pointer semantics, as well as handling - // destruction automatically. - static constexpr size_t BUFF_SIZE = 1024; - char buffer[BUFF_SIZE]; - - printf_core::WriteBuffer wb( - buffer, BUFF_SIZE, &stdout_write_hook, nullptr); - printf_core::Writer writer(wb); - - int retval = printf_core::printf_main(&writer, format, args); - - int flushval = wb.overflow_write(""); - if (flushval != printf_core::WRITE_OK) - retval = flushval; - - return retval; -} - -} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/fopencookie.cpp b/libc/src/stdio/fopencookie.cpp index da8a132a4db6e..cb82c4e0e323c 100644 --- a/libc/src/stdio/fopencookie.cpp +++ b/libc/src/stdio/fopencookie.cpp @@ -14,72 +14,10 @@ #include "src/__support/CPP/new.h" #include "src/__support/File/file.h" -#include "src/__support/libc_errno.h" #include "src/__support/macros/config.h" namespace LIBC_NAMESPACE_DECL { -namespace { - -class CookieFile : public LIBC_NAMESPACE::File { - void *cookie; - cookie_io_functions_t ops; - - static FileIOResult cookie_write(File *f, const void *data, size_t size); - static FileIOResult cookie_read(File *f, void *data, size_t size); - static ErrorOr cookie_seek(File *f, off_t offset, int whence); - static int cookie_close(File *f); - -public: - CookieFile(void *c, cookie_io_functions_t cops, uint8_t *buffer, - size_t bufsize, File::ModeFlags mode) - : File(&cookie_write, &cookie_read, &CookieFile::cookie_seek, - &cookie_close, buffer, bufsize, 0 /* default buffering mode */, - true /* File owns buffer */, mode), - cookie(c), ops(cops) {} -}; - -FileIOResult CookieFile::cookie_write(File *f, const void *data, size_t size) { - auto cookie_file = reinterpret_cast(f); - if (cookie_file->ops.write == nullptr) - return 0; - return static_cast(cookie_file->ops.write( - cookie_file->cookie, reinterpret_cast(data), size)); -} - -FileIOResult CookieFile::cookie_read(File *f, void *data, size_t size) { - auto cookie_file = reinterpret_cast(f); - if (cookie_file->ops.read == nullptr) - return 0; - return static_cast(cookie_file->ops.read( - cookie_file->cookie, reinterpret_cast(data), size)); -} - -ErrorOr CookieFile::cookie_seek(File *f, off_t offset, int whence) { - auto cookie_file = reinterpret_cast(f); - if (cookie_file->ops.seek == nullptr) { - return Error(EINVAL); - } - off64_t offset64 = offset; - int result = cookie_file->ops.seek(cookie_file->cookie, &offset64, whence); - if (result == 0) - return offset64; - return -1; -} - -int CookieFile::cookie_close(File *f) { - auto cookie_file = reinterpret_cast(f); - if (cookie_file->ops.close == nullptr) - return 0; - int retval = cookie_file->ops.close(cookie_file->cookie); - if (retval != 0) - return retval; - delete cookie_file; - return 0; -} - -} // anonymous namespace - LLVM_LIBC_FUNCTION(::FILE *, fopencookie, (void *cookie, const char *mode, cookie_io_functions_t ops)) { @@ -91,8 +29,9 @@ LLVM_LIBC_FUNCTION(::FILE *, fopencookie, return nullptr; } AllocChecker ac; - auto *file = new (ac) CookieFile( - cookie, ops, buffer, File::DEFAULT_BUFFER_SIZE, File::mode_flags(mode)); + auto *file = + new (ac) CookieFile(cookie, ops, buffer, File::DEFAULT_BUFFER_SIZE, + _IOFBF, File::mode_flags(mode)); if (!ac) return nullptr; return reinterpret_cast<::FILE *>(file); From 45aeb22b50f060b92e3dce6a041d3997dda2cf40 Mon Sep 17 00:00:00 2001 From: William Huynh Date: Mon, 11 Aug 2025 14:00:45 +0100 Subject: [PATCH 2/4] Some files were not tracked, commit these --- libc/src/__support/File/CMakeLists.txt | 4 +- .../__support/File/baremetal/CMakeLists.txt | 10 +++ libc/src/__support/File/baremetal/stderr.cpp | 38 ++++++++++ libc/src/__support/File/cookie_file.h | 75 +++++++++++++++++++ libc/src/stdio/CMakeLists.txt | 1 + libc/src/stdio/fopencookie.cpp | 2 +- 6 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 libc/src/__support/File/baremetal/CMakeLists.txt create mode 100644 libc/src/__support/File/baremetal/stderr.cpp create mode 100644 libc/src/__support/File/cookie_file.h diff --git a/libc/src/__support/File/CMakeLists.txt b/libc/src/__support/File/CMakeLists.txt index b351953f8409c..905b8daa2faac 100644 --- a/libc/src/__support/File/CMakeLists.txt +++ b/libc/src/__support/File/CMakeLists.txt @@ -29,11 +29,9 @@ add_header_library( DEPENDS .file libc.hdr.errno_macros - libc.hdr.stdio_macros - libc.hdr.types.FILE libc.hdr.types.cookie_io_functions_t + libc.hdr.types.off_t libc.src.__support.CPP.new - libc.src.__support.File.file ) add_object_library( diff --git a/libc/src/__support/File/baremetal/CMakeLists.txt b/libc/src/__support/File/baremetal/CMakeLists.txt new file mode 100644 index 0000000000000..3804b5b93e6a6 --- /dev/null +++ b/libc/src/__support/File/baremetal/CMakeLists.txt @@ -0,0 +1,10 @@ +add_object_library( + stderr + SRCS + stderr.cpp + DEPENDS + libc.hdr.stdio_macros + libc.src.__support.File.cookie_file + libc.src.__support.File.file + libc.src.__support.OSUtil.osutil +) diff --git a/libc/src/__support/File/baremetal/stderr.cpp b/libc/src/__support/File/baremetal/stderr.cpp new file mode 100644 index 0000000000000..d14a4de150c24 --- /dev/null +++ b/libc/src/__support/File/baremetal/stderr.cpp @@ -0,0 +1,38 @@ +//===--- Definition of baremetal stderr -----------------------------------===// +// +// 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 "hdr/stdio_macros.h" +#include "src/__support/File/cookie_file.h" +#include "src/__support/File/file.h" +#include "src/__support/OSUtil/baremetal/io.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +// To save space, all streams are aliased to one stream. Furthermore, no +// buffering is used. +cookie_io_functions_t io_func = {.read = __llvm_libc_stdio_read, + .write = __llvm_libc_stdio_write, + .seek = nullptr, + .close = nullptr}; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wglobal-constructors" +static CookieFile StdErr(&__llvm_libc_stderr_cookie, io_func, nullptr, 0, + _IONBF, File::mode_flags("r+")); +#pragma clang diagnostic pop +File *stderr = &StdErr; +File *stdin = &StdErr; +File *stdout = &StdErr; + +} // namespace LIBC_NAMESPACE_DECL + +extern "C" { +FILE *stderr = reinterpret_cast(&LIBC_NAMESPACE::StdErr); +FILE *stdin = reinterpret_cast(&LIBC_NAMESPACE::StdErr); +FILE *stdout = reinterpret_cast(&LIBC_NAMESPACE::StdErr); +} diff --git a/libc/src/__support/File/cookie_file.h b/libc/src/__support/File/cookie_file.h new file mode 100644 index 0000000000000..b7e2dde23bbb3 --- /dev/null +++ b/libc/src/__support/File/cookie_file.h @@ -0,0 +1,75 @@ +//===--- A platform independent cookie file class -------------------------===// +// +// 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 "hdr/errno_macros.h" +#include "hdr/types/cookie_io_functions_t.h" +#include "hdr/types/off_t.h" +#include "src/__support/CPP/new.h" +#include "src/__support/File/file.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +class CookieFile : public LIBC_NAMESPACE::File { + void *cookie; + cookie_io_functions_t ops; + + static FileIOResult cookie_write(File *f, const void *data, size_t size); + static FileIOResult cookie_read(File *f, void *data, size_t size); + static ErrorOr cookie_seek(File *f, off_t offset, int whence); + static int cookie_close(File *f); + +public: + CookieFile(void *c, cookie_io_functions_t cops, uint8_t *buffer, + size_t bufsize, int buffer_mode, File::ModeFlags mode) + : File(&cookie_write, &cookie_read, &CookieFile::cookie_seek, + &cookie_close, buffer, bufsize, buffer_mode, + true /* File owns buffer */, mode), + cookie(c), ops(cops) {} +}; + +FileIOResult CookieFile::cookie_write(File *f, const void *data, size_t size) { + auto cookie_file = reinterpret_cast(f); + if (cookie_file->ops.write == nullptr) + return 0; + return static_cast(cookie_file->ops.write( + cookie_file->cookie, reinterpret_cast(data), size)); +} + +FileIOResult CookieFile::cookie_read(File *f, void *data, size_t size) { + auto cookie_file = reinterpret_cast(f); + if (cookie_file->ops.read == nullptr) + return 0; + return static_cast(cookie_file->ops.read( + cookie_file->cookie, reinterpret_cast(data), size)); +} + +ErrorOr CookieFile::cookie_seek(File *f, off_t offset, int whence) { + auto cookie_file = reinterpret_cast(f); + if (cookie_file->ops.seek == nullptr) { + return Error(EINVAL); + } + off64_t offset64 = offset; + int result = cookie_file->ops.seek(cookie_file->cookie, &offset64, whence); + if (result == 0) + return offset64; + return -1; +} + +int CookieFile::cookie_close(File *f) { + auto cookie_file = reinterpret_cast(f); + if (cookie_file->ops.close == nullptr) + return 0; + int retval = cookie_file->ops.close(cookie_file->cookie); + if (retval != 0) + return retval; + delete cookie_file; + return 0; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt index b0a6ef1e291b5..273619ae2a570 100644 --- a/libc/src/stdio/CMakeLists.txt +++ b/libc/src/stdio/CMakeLists.txt @@ -63,6 +63,7 @@ add_entrypoint_object( libc.hdr.types.cookie_io_functions_t libc.hdr.types.FILE libc.src.__support.CPP.new + libc.src.__support.File.cookie_file libc.src.__support.File.file ) diff --git a/libc/src/stdio/fopencookie.cpp b/libc/src/stdio/fopencookie.cpp index cb82c4e0e323c..43a9acc5536c3 100644 --- a/libc/src/stdio/fopencookie.cpp +++ b/libc/src/stdio/fopencookie.cpp @@ -10,8 +10,8 @@ #include "hdr/stdio_macros.h" #include "hdr/types/FILE.h" #include "hdr/types/cookie_io_functions_t.h" -#include "hdr/types/off_t.h" #include "src/__support/CPP/new.h" +#include "src/__support/File/cookie_file.h" #include "src/__support/File/file.h" #include "src/__support/macros/config.h" From 9f1846fdb83c3baef6492818a9092d3a16a965c8 Mon Sep 17 00:00:00 2001 From: William Huynh Date: Thu, 14 Aug 2025 16:13:41 +0100 Subject: [PATCH 3/4] Apply suggestions from @mysterymath --- libc/src/__support/File/CMakeLists.txt | 9 ++--- .../__support/File/baremetal/CMakeLists.txt | 22 ++++++++++++ libc/src/__support/File/baremetal/stderr.cpp | 12 +++---- libc/src/__support/File/baremetal/stdin.cpp | 34 +++++++++++++++++++ libc/src/__support/File/baremetal/stdout.cpp | 34 +++++++++++++++++++ 5 files changed, 97 insertions(+), 14 deletions(-) create mode 100644 libc/src/__support/File/baremetal/stdin.cpp create mode 100644 libc/src/__support/File/baremetal/stdout.cpp diff --git a/libc/src/__support/File/CMakeLists.txt b/libc/src/__support/File/CMakeLists.txt index 905b8daa2faac..bcc6cef2163a1 100644 --- a/libc/src/__support/File/CMakeLists.txt +++ b/libc/src/__support/File/CMakeLists.txt @@ -55,15 +55,12 @@ add_subdirectory(${LIBC_TARGET_OS}) if(LIBC_TARGET_OS_IS_BAREMETAL) set(target_file libc.src.__support.File.cookie_file) - set(target_stdout libc.src.__support.File.${LIBC_TARGET_OS}.stderr) - set(target_stderr libc.src.__support.File.${LIBC_TARGET_OS}.stderr) - set(target_stdin libc.src.__support.File.${LIBC_TARGET_OS}.stderr) else() set(target_file libc.src.__support.File.${LIBC_TARGET_OS}.file) - set(target_stdout libc.src.__support.File.${LIBC_TARGET_OS}.stdout) - set(target_stderr libc.src.__support.File.${LIBC_TARGET_OS}.stderr) - set(target_stdin libc.src.__support.File.${LIBC_TARGET_OS}.stdin) endif() +set(target_stdout libc.src.__support.File.${LIBC_TARGET_OS}.stdout) +set(target_stderr libc.src.__support.File.${LIBC_TARGET_OS}.stderr) +set(target_stdin libc.src.__support.File.${LIBC_TARGET_OS}.stdin) set(file_targets "${target_file};${target_stdout};${target_stdin};${target_stderr}") set(file_aliases "platform_file;platform_stdout;platform_stdin;platform_stderr") diff --git a/libc/src/__support/File/baremetal/CMakeLists.txt b/libc/src/__support/File/baremetal/CMakeLists.txt index 3804b5b93e6a6..a789948368f02 100644 --- a/libc/src/__support/File/baremetal/CMakeLists.txt +++ b/libc/src/__support/File/baremetal/CMakeLists.txt @@ -1,3 +1,25 @@ +add_object_library( + stdout + SRCS + stdout.cpp + DEPENDS + libc.hdr.stdio_macros + libc.src.__support.File.cookie_file + libc.src.__support.File.file + libc.src.__support.OSUtil.osutil +) + +add_object_library( + stdin + SRCS + stdin.cpp + DEPENDS + libc.hdr.stdio_macros + libc.src.__support.File.cookie_file + libc.src.__support.File.file + libc.src.__support.OSUtil.osutil +) + add_object_library( stderr SRCS diff --git a/libc/src/__support/File/baremetal/stderr.cpp b/libc/src/__support/File/baremetal/stderr.cpp index d14a4de150c24..0a67771f034aa 100644 --- a/libc/src/__support/File/baremetal/stderr.cpp +++ b/libc/src/__support/File/baremetal/stderr.cpp @@ -14,25 +14,21 @@ namespace LIBC_NAMESPACE_DECL { -// To save space, all streams are aliased to one stream. Furthermore, no -// buffering is used. -cookie_io_functions_t io_func = {.read = __llvm_libc_stdio_read, +cookie_io_functions_t io_func = {.read = nullptr, .write = __llvm_libc_stdio_write, .seek = nullptr, .close = nullptr}; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wglobal-constructors" +// Buffering is implementation defined. Therefore to save RAM, we use no +// buffering static CookieFile StdErr(&__llvm_libc_stderr_cookie, io_func, nullptr, 0, - _IONBF, File::mode_flags("r+")); + _IONBF, File::mode_flags("w")); #pragma clang diagnostic pop File *stderr = &StdErr; -File *stdin = &StdErr; -File *stdout = &StdErr; } // namespace LIBC_NAMESPACE_DECL extern "C" { FILE *stderr = reinterpret_cast(&LIBC_NAMESPACE::StdErr); -FILE *stdin = reinterpret_cast(&LIBC_NAMESPACE::StdErr); -FILE *stdout = reinterpret_cast(&LIBC_NAMESPACE::StdErr); } diff --git a/libc/src/__support/File/baremetal/stdin.cpp b/libc/src/__support/File/baremetal/stdin.cpp new file mode 100644 index 0000000000000..569d924749eec --- /dev/null +++ b/libc/src/__support/File/baremetal/stdin.cpp @@ -0,0 +1,34 @@ +//===--- Definition of baremetal stdin ------------------------------------===// +// +// 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 "hdr/stdio_macros.h" +#include "src/__support/File/cookie_file.h" +#include "src/__support/File/file.h" +#include "src/__support/OSUtil/baremetal/io.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +cookie_io_functions_t io_func = {.read = __llvm_libc_stdio_read, + .write = nullptr, + .seek = nullptr, + .close = nullptr}; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wglobal-constructors" +// Buffering is implementation defined. Therefore to save RAM, we use no +// buffering +static CookieFile StdIn(&__llvm_libc_stdin_cookie, io_func, nullptr, 0, _IONBF, + File::mode_flags("r")); +#pragma clang diagnostic pop +File *stdin = &StdIn; + +} // namespace LIBC_NAMESPACE_DECL + +extern "C" { +FILE *stdin = reinterpret_cast(&LIBC_NAMESPACE::StdIn); +} diff --git a/libc/src/__support/File/baremetal/stdout.cpp b/libc/src/__support/File/baremetal/stdout.cpp new file mode 100644 index 0000000000000..ee8535c0d7643 --- /dev/null +++ b/libc/src/__support/File/baremetal/stdout.cpp @@ -0,0 +1,34 @@ +//===--- Definition of baremetal stdout -----------------------------------===// +// +// 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 "hdr/stdio_macros.h" +#include "src/__support/File/cookie_file.h" +#include "src/__support/File/file.h" +#include "src/__support/OSUtil/baremetal/io.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +cookie_io_functions_t io_func = {.read = nullptr, + .write = __llvm_libc_stdio_write, + .seek = nullptr, + .close = nullptr}; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wglobal-constructors" +// Buffering is implementation defined. Therefore to save RAM, we use no +// buffering +static CookieFile StdOut(&__llvm_libc_stdout_cookie, io_func, nullptr, 0, + _IONBF, File::mode_flags("w")); +#pragma clang diagnostic pop +File *stdout = &StdOut; + +} // namespace LIBC_NAMESPACE_DECL + +extern "C" { +FILE *stdout = reinterpret_cast(&LIBC_NAMESPACE::StdOut); +} From 7b3dd936db3f19b2d816f7a345692d61a2e3c1a8 Mon Sep 17 00:00:00 2001 From: William Huynh Date: Fri, 15 Aug 2025 10:37:14 +0100 Subject: [PATCH 4/4] Add suggestion from @michaelrj-google --- libc/src/__support/File/cookie_file.h | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/libc/src/__support/File/cookie_file.h b/libc/src/__support/File/cookie_file.h index b7e2dde23bbb3..8fc13b047b292 100644 --- a/libc/src/__support/File/cookie_file.h +++ b/libc/src/__support/File/cookie_file.h @@ -19,21 +19,24 @@ class CookieFile : public LIBC_NAMESPACE::File { void *cookie; cookie_io_functions_t ops; - static FileIOResult cookie_write(File *f, const void *data, size_t size); - static FileIOResult cookie_read(File *f, void *data, size_t size); - static ErrorOr cookie_seek(File *f, off_t offset, int whence); - static int cookie_close(File *f); + LIBC_INLINE static FileIOResult cookie_write(File *f, const void *data, + size_t size); + LIBC_INLINE static FileIOResult cookie_read(File *f, void *data, size_t size); + LIBC_INLINE static ErrorOr cookie_seek(File *f, off_t offset, + int whence); + LIBC_INLINE static int cookie_close(File *f); public: - CookieFile(void *c, cookie_io_functions_t cops, uint8_t *buffer, - size_t bufsize, int buffer_mode, File::ModeFlags mode) + LIBC_INLINE CookieFile(void *c, cookie_io_functions_t cops, uint8_t *buffer, + size_t bufsize, int buffer_mode, File::ModeFlags mode) : File(&cookie_write, &cookie_read, &CookieFile::cookie_seek, &cookie_close, buffer, bufsize, buffer_mode, true /* File owns buffer */, mode), cookie(c), ops(cops) {} }; -FileIOResult CookieFile::cookie_write(File *f, const void *data, size_t size) { +LIBC_INLINE FileIOResult CookieFile::cookie_write(File *f, const void *data, + size_t size) { auto cookie_file = reinterpret_cast(f); if (cookie_file->ops.write == nullptr) return 0; @@ -41,7 +44,8 @@ FileIOResult CookieFile::cookie_write(File *f, const void *data, size_t size) { cookie_file->cookie, reinterpret_cast(data), size)); } -FileIOResult CookieFile::cookie_read(File *f, void *data, size_t size) { +LIBC_INLINE FileIOResult CookieFile::cookie_read(File *f, void *data, + size_t size) { auto cookie_file = reinterpret_cast(f); if (cookie_file->ops.read == nullptr) return 0; @@ -49,7 +53,8 @@ FileIOResult CookieFile::cookie_read(File *f, void *data, size_t size) { cookie_file->cookie, reinterpret_cast(data), size)); } -ErrorOr CookieFile::cookie_seek(File *f, off_t offset, int whence) { +LIBC_INLINE ErrorOr CookieFile::cookie_seek(File *f, off_t offset, + int whence) { auto cookie_file = reinterpret_cast(f); if (cookie_file->ops.seek == nullptr) { return Error(EINVAL); @@ -61,7 +66,7 @@ ErrorOr CookieFile::cookie_seek(File *f, off_t offset, int whence) { return -1; } -int CookieFile::cookie_close(File *f) { +LIBC_INLINE int CookieFile::cookie_close(File *f) { auto cookie_file = reinterpret_cast(f); if (cookie_file->ops.close == nullptr) return 0;