From 8e9000cbeb61f7a5f0f54abeab5f7c7538b3ff7c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 20 Dec 2021 14:56:39 -0800 Subject: [PATCH 1/2] arch/arm: Use TPIDRURO on cortex-a too V7-A also supports TPIDRURO, so go ahead and use that for TLS, enabling thread local storage for the other ARM architectures. Add __aeabi_read_tp function in case code was compiled to use that. Signed-off-by: Keith Packard --- arch/Kconfig | 2 +- arch/arm/core/aarch32/cortex_a_r/CMakeLists.txt | 1 + .../core/aarch32/cortex_a_r/__aeabi_read_tp.S | 17 +++++++++++++++++ arch/arm/core/aarch32/swap_helper.S | 2 +- 4 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 arch/arm/core/aarch32/cortex_a_r/__aeabi_read_tp.S diff --git a/arch/Kconfig b/arch/Kconfig index 1c907c9ec1f1dc..7f9e0be3800ba6 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -33,7 +33,7 @@ config ARM # FIXME: current state of the code for all ARM requires this, but # is really only necessary for Cortex-M with ARM MPU! select GEN_PRIV_STACKS - select ARCH_HAS_THREAD_LOCAL_STORAGE if CPU_CORTEX_R || CPU_CORTEX_M + select ARCH_HAS_THREAD_LOCAL_STORAGE help ARM architecture diff --git a/arch/arm/core/aarch32/cortex_a_r/CMakeLists.txt b/arch/arm/core/aarch32/cortex_a_r/CMakeLists.txt index a044a2b20bb85f..d03d53680fc781 100644 --- a/arch/arm/core/aarch32/cortex_a_r/CMakeLists.txt +++ b/arch/arm/core/aarch32/cortex_a_r/CMakeLists.txt @@ -15,3 +15,4 @@ zephyr_library_sources( ) zephyr_library_sources_ifdef(CONFIG_USERSPACE thread.c) +zephyr_library_sources_ifdef(CONFIG_THREAD_LOCAL_STORAGE __aeabi_read_tp.S) diff --git a/arch/arm/core/aarch32/cortex_a_r/__aeabi_read_tp.S b/arch/arm/core/aarch32/cortex_a_r/__aeabi_read_tp.S new file mode 100644 index 00000000000000..48bc5cf7c0e4b9 --- /dev/null +++ b/arch/arm/core/aarch32/cortex_a_r/__aeabi_read_tp.S @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +_ASM_FILE_PROLOGUE + +GTEXT(__aeabi_read_tp) + +GDATA(z_arm_tls_ptr) + +SECTION_FUNC(text, __aeabi_read_tp) + mrc 15, 0, r0, c13, c0, 3 + bx lr diff --git a/arch/arm/core/aarch32/swap_helper.S b/arch/arm/core/aarch32/swap_helper.S index 6a0f884051f09e..0650fe088ecb1b 100644 --- a/arch/arm/core/aarch32/swap_helper.S +++ b/arch/arm/core/aarch32/swap_helper.S @@ -184,7 +184,7 @@ out_fp_endif: adds r4, r2, r4 ldr r0, [r4] -#if defined(CONFIG_CPU_CORTEX_R) +#if defined(CONFIG_CPU_CORTEX_R) || defined(CONFIG_CPU_CORTEX_A) /* Store TLS pointer in the "Process ID" register. * This register is used as a base pointer to all * thread variables with offsets added by toolchain. From fe48e9c97d872a04b624f4df18122207b6941904 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 26 Oct 2020 19:07:50 -0700 Subject: [PATCH 2/2] lib/libc: Add picolibc support (aarch32, aarch64 and RISC-V) [v15] Picolibc is a fork of newlib designed and tested on embedded systems. It offers a smaller memory footprint (both ROM and RAM), and native TLS support, which uses the Zephyr TLS support. By default, the full printf version is included in the executable, which includes exact floating point and long long input and output. A configuration option has been added to switch to the integer-only version (which also omits long long support). Here are some size comparisons using qemu-cortex-m3 and this application (parameters passed to printf to avoid GCC optimizing it into puts): void main(void) { printf("Hello World! %s %d\n", CONFIG_BOARD, 12); } FLASH SRAM minimal 8696 3952 picolibc int 7600 3960 picolibc float 12304 3960 newlib-nano int 11696 4128 newlib-nano float 30516 4496 newlib 34800 6112 --- v2: Include picolibc-tls.ld v3: Document usage in guides/c_library.rst and getting_started/toolchain_other_x_compilers.rst v4: Lost the lib/libc/picolibc directory somehow! v5: Add PICOLIBC_ALIGNED_HEAP_SIZE configuration option. Delete PICOLIBC_SEMIHOST option support code v6: Don't allocate static RAM for TLS values; TLS values only need to be allocated for each thread. v7: Use arm coprocessor for TLS pointer storage where supported for compatibility with the -mtp=cp15 compiler option (or when the target cpu type selects this option) Add a bunch of tests Round TLS segment up to stack alignment so that overall stack remains correctly aligned Add aarch64 support Rebase to upstream head v8: Share NEWLIB, NEWLIB_NANO and PICOLIBC library configuration variables in a single LIBC_PARTITIONS variable instead of having separate PICOLIBC_PART and NEWLIB_PART variables. v9: Update docs to reference pending sdk-ng support for picolibc v10: Support memory protection by creating a partition for picolibc shared data and any pre-defined picolibc heap. v11: Fix formatting in arch/arm/core/aarch64/switch.S v12: Remove TLS support from this patch now that TLS is upstream Require THREAD_LOCAL_STORAGE when using PICOLIBC for architectures that support it. v13: Merge errno changes as they're only needed for picolibc. Adapt cmake changes suggested by Torsten Tejlmand Rasmussen v14: Update to picolibc 1.7 and newer (new stdin/stdout/stderr ABI) v15: Respond to comments from dcpleung: * switch kernel/errno to use CONFIG_LIBC_ERRNO instead of CONFIG_PICOLIBC * Add comment to test/lib/sprintf as to why the %n test was disabled for picolibc. Signed-off-by: Keith Packard --- CMakeLists.txt | 3 + .../core/aarch32/cortex_a_r/__aeabi_read_tp.S | 7 +- cmake/toolchain/gnuarmemb/generic.cmake | 1 + cmake/toolchain/xtools/generic.cmake | 1 + include/sys/libc-hooks.h | 20 +- kernel/Kconfig | 8 + kernel/errno.c | 3 + lib/libc/CMakeLists.txt | 2 + lib/libc/Kconfig | 33 +++ lib/libc/picolibc/CMakeLists.txt | 41 +++ lib/libc/picolibc/libc-hooks.c | 271 ++++++++++++++++++ tests/lib/c_lib/testcase.yaml | 6 + tests/lib/mem_alloc/prj_picolibc.conf | 4 + tests/lib/mem_alloc/testcase.yaml | 6 + tests/lib/sprintf/prj_picolibc.conf | 3 + tests/lib/sprintf/src/main.c | 8 + tests/lib/sprintf/testcase.yaml | 4 + tests/posix/common/testcase.yaml | 5 + 18 files changed, 421 insertions(+), 5 deletions(-) create mode 100644 lib/libc/picolibc/CMakeLists.txt create mode 100644 lib/libc/picolibc/libc-hooks.c create mode 100644 tests/lib/mem_alloc/prj_picolibc.conf create mode 100644 tests/lib/sprintf/prj_picolibc.conf diff --git a/CMakeLists.txt b/CMakeLists.txt index b27826afce097c..22cb61b69c3f9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -875,6 +875,9 @@ if(CONFIG_USERSPACE) if(CONFIG_NEWLIB_LIBC_NANO) set(NEWLIB_PART -l libc_nano.a z_libc_partition) endif() + if(CONFIG_PICOLIBC) + set(NEWLIB_PART -l libc.a z_libc_partition) + endif() add_custom_command( OUTPUT ${APP_SMEM_UNALIGNED_LD} ${APP_SMEM_PINNED_UNALIGNED_LD} diff --git a/arch/arm/core/aarch32/cortex_a_r/__aeabi_read_tp.S b/arch/arm/core/aarch32/cortex_a_r/__aeabi_read_tp.S index 48bc5cf7c0e4b9..7b5bb5c1bf61b0 100644 --- a/arch/arm/core/aarch32/cortex_a_r/__aeabi_read_tp.S +++ b/arch/arm/core/aarch32/cortex_a_r/__aeabi_read_tp.S @@ -10,8 +10,11 @@ _ASM_FILE_PROLOGUE GTEXT(__aeabi_read_tp) -GDATA(z_arm_tls_ptr) - SECTION_FUNC(text, __aeabi_read_tp) + /* + * Load TLS base address, which is stored in the TPIDRURO register, also + * known as the "Process ID" register. Refer to the code in z_arm_pendsv + * to see where this register is set. + */ mrc 15, 0, r0, c13, c0, 3 bx lr diff --git a/cmake/toolchain/gnuarmemb/generic.cmake b/cmake/toolchain/gnuarmemb/generic.cmake index 1ea30ab70debcd..97bff4272f6b17 100644 --- a/cmake/toolchain/gnuarmemb/generic.cmake +++ b/cmake/toolchain/gnuarmemb/generic.cmake @@ -28,5 +28,6 @@ set(SYSROOT_TARGET arm-none-eabi) set(CROSS_COMPILE ${TOOLCHAIN_HOME}/bin/${CROSS_COMPILE_TARGET}-) set(SYSROOT_DIR ${TOOLCHAIN_HOME}/${SYSROOT_TARGET}) set(TOOLCHAIN_HAS_NEWLIB ON CACHE BOOL "True if toolchain supports newlib") +set(TOOLCHAIN_HAS_PICOLIBC ON CACHE BOOL "True if toolchain supports picolibc") message(STATUS "Found toolchain: gnuarmemb (${GNUARMEMB_TOOLCHAIN_PATH})") diff --git a/cmake/toolchain/xtools/generic.cmake b/cmake/toolchain/xtools/generic.cmake index f4836eb5775de4..b6ff678c6722db 100644 --- a/cmake/toolchain/xtools/generic.cmake +++ b/cmake/toolchain/xtools/generic.cmake @@ -33,6 +33,7 @@ set(SYSROOT_TARGET ${CROSS_COMPILE_TARGET}) set(CROSS_COMPILE ${some_toolchain_root}/${CROSS_COMPILE_TARGET}/bin/${CROSS_COMPILE_TARGET}-) set(SYSROOT_DIR ${some_toolchain_root}/${SYSROOT_TARGET}/${SYSROOT_TARGET}) set(TOOLCHAIN_HAS_NEWLIB ON CACHE BOOL "True if toolchain supports newlib") +set(TOOLCHAIN_HAS_PICOLIBC ON CACHE BOOL "True if toolchain supports picolibc") unset(some_toolchain_root) unset(some_toolchain) diff --git a/include/sys/libc-hooks.h b/include/sys/libc-hooks.h index afa3554ed87c09..6bfd379151cc3b 100644 --- a/include/sys/libc-hooks.h +++ b/include/sys/libc-hooks.h @@ -16,7 +16,7 @@ * that need to call into the kernel as system calls */ -#if defined(CONFIG_NEWLIB_LIBC) || defined(CONFIG_ARCMWDT_LIBC) +#if defined(CONFIG_NEWLIB_LIBC) || defined(CONFIG_ARCMWDT_LIBC) || defined(CONFIG_PICOLIBC) /* syscall generation ignores preprocessor, ensure this is defined to ensure * we don't have compile errors @@ -56,7 +56,21 @@ extern struct k_mem_partition z_malloc_partition; */ #define Z_MALLOC_PARTITION_EXISTS 1 #endif /* CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE > 0 */ -#endif /* CONFIG_MINIMAL_LIBC */ +#elif defined(CONFIG_PICOLIBC) +/* If we are using picolibc, the heap arena is in one of two areas: + * - If we have an MPU that requires power of two alignment, the heap bounds + * must be specified in Kconfig via CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE. + * - Otherwise, the heap arena on most arches starts at a suitably + * aligned base addreess after the `_end` linker symbol, through to the end + * of system RAM. + */ +#if (!defined(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT) || \ +(defined(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT) && \ +CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE)) +#define Z_MALLOC_PARTITION_EXISTS 1 +extern struct k_mem_partition z_malloc_partition; +#endif +#endif /* CONFIG_PICOLIBC */ #ifdef Z_MALLOC_PARTITION_EXISTS /* Memory partition containing the libc malloc arena. Configuration controls @@ -66,7 +80,7 @@ extern struct k_mem_partition z_malloc_partition; #endif #if defined(CONFIG_NEWLIB_LIBC) || defined(CONFIG_STACK_CANARIES) || \ - defined(CONFIG_NEED_LIBC_MEM_PARTITION) +defined(CONFIG_PICOLIBC) || defined(CONFIG_NEED_LIBC_MEM_PARTITION) /* - All newlib globals will be placed into z_libc_partition. * - Minimal C library globals, if any, will be placed into * z_libc_partition. diff --git a/kernel/Kconfig b/kernel/Kconfig index faaf2af4a33aa4..0a63b8b41a88a0 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -202,9 +202,17 @@ config THREAD_USERSPACE_LOCAL_DATA depends on USERSPACE default y if ERRNO && !ERRNO_IN_TLS +config LIBC_ERRNO + bool + help + Use external libc errno, not the internal one. This makes sure that + ERRNO is not set for libc configurations that provide their own + implementation. + config ERRNO bool "Enable errno support" default y + depends on !LIBC_ERRNO help Enable per-thread errno in the kernel. Application and library code must include errno.h provided by the C library (libc) to use the errno diff --git a/kernel/errno.c b/kernel/errno.c index 84f78593dc1763..07a8684ddd6c8c 100644 --- a/kernel/errno.c +++ b/kernel/errno.c @@ -44,10 +44,13 @@ static inline int *z_vrfy_z_errno(void) #include #else +#ifndef CONFIG_LIBC_ERRNO int *z_impl_z_errno(void) { return &_current->errno_var; } +#endif /* CONFIG_PICOLIBC */ + #endif /* CONFIG_USERSPACE */ #endif /* CONFIG_ERRNO_IN_TLS */ diff --git a/lib/libc/CMakeLists.txt b/lib/libc/CMakeLists.txt index fe72c26f7aa7dc..f8600b62c45e7c 100644 --- a/lib/libc/CMakeLists.txt +++ b/lib/libc/CMakeLists.txt @@ -8,4 +8,6 @@ elseif(CONFIG_MINIMAL_LIBC) add_subdirectory(minimal) elseif(CONFIG_ARMCLANG_STD_LIBC) add_subdirectory(armstdc) +elseif(CONFIG_PICOLIBC) + add_subdirectory(picolibc) endif() diff --git a/lib/libc/Kconfig b/lib/libc/Kconfig index 316910736dbc1d..23672e05d7f673 100644 --- a/lib/libc/Kconfig +++ b/lib/libc/Kconfig @@ -29,6 +29,15 @@ config MINIMAL_LIBC help Build with minimal C library. +config PICOLIBC + bool "Picolibc library" + depends on !NATIVE_APPLICATION + select THREAD_LOCAL_STORAGE if ARCH_HAS_THREAD_LOCAL_STORAGE + select LIBC_ERRNO + help + Build with picolibc library. The picolibc library is expected to be + part of the SDK in this case. + config NEWLIB_LIBC bool "Newlib C library" depends on !NATIVE_APPLICATION @@ -53,6 +62,30 @@ endchoice # LIBC_IMPLEMENTATION config HAS_NEWLIB_LIBC_NANO bool +if PICOLIBC + +config PICOLIBC_INTEGER_PRINTF + bool "Build with picolibc integer-only printf" + help + Build with floating point printf disabled. This will reduce the size + of the image. + +config PICOLIBC_ALIGNED_HEAP_SIZE + int "Picolibc aligned heap size (bytes)" + depends on MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT + depends on USERSPACE + default 0 + help + If user mode is enabled, and MPU hardware has requirements that + regions be sized to a power of two and aligned to their size, + and user mode threads need to access this heap, then this is necessary + to properly define an MPU region for the heap. + + If this is left at 0, then remaining system RAM will be used for this + area and it may not be possible to program it as an MPU region. + +endif # PICOLIBC + if NEWLIB_LIBC config NEWLIB_LIBC_NANO diff --git a/lib/libc/picolibc/CMakeLists.txt b/lib/libc/picolibc/CMakeLists.txt new file mode 100644 index 00000000000000..ff57fcfde31862 --- /dev/null +++ b/lib/libc/picolibc/CMakeLists.txt @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources(libc-hooks.c) + +# Zephyr normally uses -ffreestanding, which with current GNU toolchains +# means that the flag macros used by picolibc to signal +# support for PRI.64 macros are not present. To make them available we +# need to hook into the include path before the system files and +# explicitly include the picolibc header that provides those macros. +zephyr_include_directories(include) + +# define __LINUX_ERRNO_EXTENSIONS__ so we get errno defines like -ESHUTDOWN +# used by the network stack +zephyr_compile_definitions(__LINUX_ERRNO_EXTENSIONS__) + +zephyr_link_libraries( + m + c + gcc # Lib C depends on libgcc. + ) + +# The -T/dev/null avoids pulling in picolibc.ld +zephyr_link_libraries( + --specs=picolibc.specs + -T/dev/null + ) + +zephyr_compile_options( + --specs=picolibc.specs + -D_GNU_SOURCE + ) + +if(CONFIG_PICOLIBC_INTEGER_PRINTF) + zephyr_compile_options( + -DPICOLIBC_INTEGER_PRINTF_SCANF + ) + zephyr_link_libraries( + -DPICOLIBC_INTEGER_PRINTF_SCANF + ) +endif() diff --git a/lib/libc/picolibc/libc-hooks.c b/lib/libc/picolibc/libc-hooks.c new file mode 100644 index 00000000000000..4367c1a53f6bfc --- /dev/null +++ b/lib/libc/picolibc/libc-hooks.c @@ -0,0 +1,271 @@ +/* + * Copyright © 2021, Keith Packard + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LIBC_BSS K_APP_BMEM(z_libc_partition) +#define LIBC_DATA K_APP_DMEM(z_libc_partition) + +#if CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE +K_APPMEM_PARTITION_DEFINE(z_malloc_partition); +#define MALLOC_BSS K_APP_BMEM(z_malloc_partition) + +/* Compiler will throw an error if the provided value isn't a power of two */ +MALLOC_BSS static unsigned char __aligned(CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE) + heap_base[CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE]; +#define MAX_HEAP_SIZE CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE + +#else /* CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE */ +/* Heap base and size are determined based on the available unused SRAM, + * in the interval from a properly aligned address after the linker symbol + * `_end`, to the end of SRAM + */ +#define USED_RAM_END_ADDR POINTER_TO_UINT(&_end) + +#ifdef Z_MALLOC_PARTITION_EXISTS +/* Need to be able to program a memory protection region from HEAP_BASE + * to the end of RAM so that user threads can get at it. + * Implies that the base address needs to be suitably aligned since the + * bounds have to go in a k_mem_partition. + */ +#ifdef CONFIG_MMU +/* Linker script may already have done this, but just to be safe */ +#define HEAP_BASE ROUND_UP(USED_RAM_END_ADDR, CONFIG_MMU_PAGE_SIZE) +#else /* MPU-based systems */ +/* TODO: Need a generic Kconfig for the MPU region granularity */ +#if defined(CONFIG_ARM) +#define HEAP_BASE ROUND_UP(USED_RAM_END_ADDR, \ + CONFIG_ARM_MPU_REGION_MIN_ALIGN_AND_SIZE) +#elif defined(CONFIG_ARC) +#define HEAP_BASE ROUND_UP(USED_RAM_END_ADDR, Z_ARC_MPU_ALIGN) +#else +#error "Unsupported platform" +#endif /* CONFIG_ */ +#endif /* !CONFIG_MMU */ +#else /* !Z_MALLOC_PARTITION_EXISTS */ +/* No partition, heap can just start wherever _end is */ +#define HEAP_BASE USED_RAM_END_ADDR +#endif /* Z_MALLOC_PARTITION_EXISTS */ + +#ifdef CONFIG_XTENSA +extern void *_heap_sentry; +#define MAX_HEAP_SIZE (POINTER_TO_UINT(&_heap_sentry) - HEAP_BASE) +#else +#define MAX_HEAP_SIZE (KB(CONFIG_SRAM_SIZE) - \ + (HEAP_BASE - CONFIG_SRAM_BASE_ADDRESS)) +#endif + +#if Z_MALLOC_PARTITION_EXISTS +struct k_mem_partition z_malloc_partition; + +static int malloc_prepare(const struct device *unused) +{ + ARG_UNUSED(unused); + + z_malloc_partition.start = HEAP_BASE; + z_malloc_partition.size = MAX_HEAP_SIZE; + z_malloc_partition.attr = K_MEM_PARTITION_P_RW_U_RW; + return 0; +} + +SYS_INIT(malloc_prepare, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); +#endif /* CONFIG_USERSPACE */ +#endif /* CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE */ + +LIBC_BSS static unsigned int heap_sz; + +static int (*put_hook)(int); + +static int picolibc_put(char a, FILE *out) +{ + (*put_hook)(a); + return 0; +} + +static FILE __stdio = FDEV_SETUP_STREAM(picolibc_put, NULL, NULL, 0); + +#ifdef __strong_reference +#define STDIO_ALIAS(x) __strong_reference(stdin, x); +#else +#define STDIO_ALIAS(x) FILE *const x = &__stdio; +#endif + +FILE *const stdin = &__stdio; +STDIO_ALIAS(stdout); +STDIO_ALIAS(stderr); + +void __stdout_hook_install(int (*hook)(int)) +{ + put_hook = hook; + __stdio.flags |= _FDEV_SETUP_WRITE; +} + +void __stdin_hook_install(unsigned char (*hook)(void)) +{ + __stdio.get = (int (*)(FILE *)) hook; + __stdio.flags |= _FDEV_SETUP_READ; +} + +int z_impl_zephyr_read_stdin(char *buf, int nbytes) +{ + int i = 0; + + for (i = 0; i < nbytes; i++) { + *(buf + i) = getchar(); + if ((*(buf + i) == '\n') || (*(buf + i) == '\r')) { + i++; + break; + } + } + return i; +} + +int z_impl_zephyr_write_stdout(const void *buffer, int nbytes) +{ + const char *buf = buffer; + int i; + + for (i = 0; i < nbytes; i++) { + if (*(buf + i) == '\n') { + putchar('\r'); + } + putchar(*(buf + i)); + } + return nbytes; +} + +#ifndef CONFIG_POSIX_API +int _read(int fd, char *buf, int nbytes) +{ + ARG_UNUSED(fd); + + return z_impl_zephyr_read_stdin(buf, nbytes); +} +__weak FUNC_ALIAS(_read, read, int); + +int _write(int fd, const void *buf, int nbytes) +{ + ARG_UNUSED(fd); + + return z_impl_zephyr_write_stdout(buf, nbytes); +} +__weak FUNC_ALIAS(_write, write, int); + +int _open(const char *name, int mode) +{ + return -1; +} +__weak FUNC_ALIAS(_open, open, int); + +int _close(int file) +{ + return -1; +} +__weak FUNC_ALIAS(_close, close, int); + +int _lseek(int file, int ptr, int dir) +{ + return 0; +} +__weak FUNC_ALIAS(_lseek, lseek, int); +#else +extern ssize_t write(int file, const char *buffer, size_t count); +#define _write write +#endif + +int _isatty(int file) +{ + return 1; +} +__weak FUNC_ALIAS(_isatty, isatty, int); + +int _kill(int i, int j) +{ + return 0; +} +__weak FUNC_ALIAS(_kill, kill, int); + +int _getpid(void) +{ + return 0; +} +__weak FUNC_ALIAS(_getpid, getpid, int); + +int _fstat(int file, struct stat *st) +{ + st->st_mode = S_IFCHR; + return 0; +} +__weak FUNC_ALIAS(_fstat, fstat, int); + +__weak void _exit(int status) +{ + _write(1, "exit\n", 5); + while (1) { + ; + } +} + +static LIBC_DATA SYS_SEM_DEFINE(heap_sem, 1, 1); + +void *_sbrk(int count) +{ + void *ret, *ptr; + + sys_sem_take(&heap_sem, K_FOREVER); + +#if CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE + ptr = heap_base + heap_sz; +#else + ptr = ((char *)HEAP_BASE) + heap_sz; +#endif + + if ((heap_sz + count) < MAX_HEAP_SIZE) { + heap_sz += count; + ret = ptr; + } else { + ret = (void *)-1; + } + + /* coverity[CHECKED_RETURN] */ + sys_sem_give(&heap_sem); + + return ret; +} +__weak FUNC_ALIAS(_sbrk, sbrk, void *); + +/* This function gets called if static buffer overflow detection is enabled on + * stdlib side (Picolibc here), in case such an overflow is detected. Picolibc + * provides an implementation not suitable for us, so we override it here. + */ +__weak FUNC_NORETURN void __chk_fail(void) +{ + static const char chk_fail_msg[] = "* buffer overflow detected *\n"; + + _write(2, chk_fail_msg, sizeof(chk_fail_msg) - 1); + k_oops(); + CODE_UNREACHABLE; +} + +int _gettimeofday(struct timeval *__tp, void *__tzp) +{ + ARG_UNUSED(__tp); + ARG_UNUSED(__tzp); + + return -1; +} diff --git a/tests/lib/c_lib/testcase.yaml b/tests/lib/c_lib/testcase.yaml index 33dfe0983a9b64..f63cb476533151 100644 --- a/tests/lib/c_lib/testcase.yaml +++ b/tests/lib/c_lib/testcase.yaml @@ -2,3 +2,9 @@ tests: libraries.libc: tags: clib ignore_faults platform_exclude: native_posix native_posix_64 nrf52_bsim + libraries.picolibc: + tags: clib picolibc ignore_faults + platform_exclude: native_posix native_posix_64 nrf52_bsim + extra_configs: + - CONFIG_PICOLIBC=y + - CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE=8192 diff --git a/tests/lib/mem_alloc/prj_picolibc.conf b/tests/lib/mem_alloc/prj_picolibc.conf new file mode 100644 index 00000000000000..bca686c990bd6f --- /dev/null +++ b/tests/lib/mem_alloc/prj_picolibc.conf @@ -0,0 +1,4 @@ +CONFIG_ZTEST=y +CONFIG_PICOLIBC=y +CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE=8192 +CONFIG_TEST_USERSPACE=y diff --git a/tests/lib/mem_alloc/testcase.yaml b/tests/lib/mem_alloc/testcase.yaml index 8738372cae9047..32d1619501c31b 100644 --- a/tests/lib/mem_alloc/testcase.yaml +++ b/tests/lib/mem_alloc/testcase.yaml @@ -20,3 +20,9 @@ tests: arch_exclude: posix platform_exclude: twr_ke18f native_posix_64 nrf52_bsim tags: clib minimal_libc userspace + libraries.libc.picolibc: + extra_args: CONF_FILE=prj_picolibc.conf + arch_exclude: posix + platform_exclude: twr_ke18f native_posix_64 nrf52_bsim + tags: clib picolibc userspace + filter: TOOLCHAIN_HAS_PICOLIBC == 1 diff --git a/tests/lib/sprintf/prj_picolibc.conf b/tests/lib/sprintf/prj_picolibc.conf new file mode 100644 index 00000000000000..fd34803103ff01 --- /dev/null +++ b/tests/lib/sprintf/prj_picolibc.conf @@ -0,0 +1,3 @@ +CONFIG_ZTEST=y +CONFIG_PICOLIBC=y +CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE=8192 diff --git a/tests/lib/sprintf/src/main.c b/tests/lib/sprintf/src/main.c index 5a569bed65a778..fbb5f043384627 100644 --- a/tests/lib/sprintf/src/main.c +++ b/tests/lib/sprintf/src/main.c @@ -537,6 +537,7 @@ void test_sprintf_misc(void) if (IS_MINIMAL_LIBC_NANO) { TC_PRINT(" MINIMAL_LIBC+CPBPRINTF skipped tests\n"); } else { +#ifndef CONFIG_PICOLIBC sprintf(buffer, "test data %n test data", &count); zassert_false((count != 10), "sprintf(%%n). Expected count to be %d, not %d", @@ -545,6 +546,13 @@ void test_sprintf_misc(void) zassert_false((strcmp(buffer, "test data test data") != 0), "sprintf(%%p). Expected '%s', got '%s'", "test data test data", buffer); +#else + /* + * Picolibc doesn't include %n support as it makes format string + * bugs a more serious security issue + */ + (void) count; +#endif /*******************/ diff --git a/tests/lib/sprintf/testcase.yaml b/tests/lib/sprintf/testcase.yaml index aff5053617bf0c..2e841ccb4b7964 100644 --- a/tests/lib/sprintf/testcase.yaml +++ b/tests/lib/sprintf/testcase.yaml @@ -10,3 +10,7 @@ tests: extra_args: CONF_FILE=prj_new.conf tags: libc platform_exclude: native_posix native_posix_64 nrf52_bsim + libraries.picolibc.sprintf: + extra_args: CONF_FILE=prj_picolibc.conf + tags: clib ignore_faults picolibc + platform_exclude: native_posix native_posix_64 nrf52_bsim diff --git a/tests/posix/common/testcase.yaml b/tests/posix/common/testcase.yaml index d6c0448bd08a87..128a6558bd2097 100644 --- a/tests/posix/common/testcase.yaml +++ b/tests/posix/common/testcase.yaml @@ -43,3 +43,8 @@ tests: extra_configs: - CONFIG_NEWLIB_LIBC=y - CONFIG_TEST_HW_STACK_PROTECTION=n + portability.posix.common.picolibc: + platform_exclude: nsim_sem_mpu_stack_guard nsim_em_mpu_stack_guard + extra_configs: + - CONFIG_PICOLIBC=y + - CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE=8192