From c759b8ac3dc629fdcf05d1775424a50983467a8c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 18 Nov 2023 11:50:38 -0800 Subject: [PATCH] libc/picolibc: Split hooks into separate files This splits the picolibc helper functions into separate files instead of smashing them all together. Signed-off-by: Keith Packard --- boards/qemu/x86/qemu_x86_tiny.ld | 6 +- lib/libc/picolibc/CMakeLists.txt | 13 +- lib/libc/picolibc/assert.c | 28 ++++ lib/libc/picolibc/cbprintf.c | 31 ++++ lib/libc/picolibc/chk_fail.c | 18 ++ lib/libc/picolibc/errno_wrap.c | 21 +++ lib/libc/picolibc/exit.c | 15 ++ lib/libc/picolibc/libc-hooks.c | 253 ----------------------------- lib/libc/picolibc/locks.c | 106 ++++++++++++ lib/libc/picolibc/picolibc-hooks.h | 32 ++++ lib/libc/picolibc/stdio.c | 54 ++++++ 11 files changed, 317 insertions(+), 260 deletions(-) create mode 100644 lib/libc/picolibc/assert.c create mode 100644 lib/libc/picolibc/cbprintf.c create mode 100644 lib/libc/picolibc/chk_fail.c create mode 100644 lib/libc/picolibc/errno_wrap.c create mode 100644 lib/libc/picolibc/exit.c delete mode 100644 lib/libc/picolibc/libc-hooks.c create mode 100644 lib/libc/picolibc/locks.c create mode 100644 lib/libc/picolibc/picolibc-hooks.h create mode 100644 lib/libc/picolibc/stdio.c diff --git a/boards/qemu/x86/qemu_x86_tiny.ld b/boards/qemu/x86/qemu_x86_tiny.ld index 548e5fbdcb03b2..607b096e9d7790 100644 --- a/boards/qemu/x86/qemu_x86_tiny.ld +++ b/boards/qemu/x86/qemu_x86_tiny.ld @@ -144,10 +144,10 @@ MEMORY #endif /* CONFIG_NEWLIB_LIBC */ #ifdef CONFIG_PICOLIBC -/* For Picolibc libc-hook.c. */ +/* For Picolibc, all files under lib/libc/picolibc */ #define LIB_C_IN_SECT(lsect) \ - *liblib__libc__picolibc.a:libc-hooks.c.obj(.##lsect) \ - *liblib__libc__picolibc.a:libc-hooks.c.obj(.##lsect##.*) + *liblib__libc__picolibc.a:(.##lsect) \ + *liblib__libc__picolibc.a:(.##lsect##.*) #endif /* CONFIG_PICOLIBC */ diff --git a/lib/libc/picolibc/CMakeLists.txt b/lib/libc/picolibc/CMakeLists.txt index 14af66b7b7dc38..d1abfa675d25f5 100644 --- a/lib/libc/picolibc/CMakeLists.txt +++ b/lib/libc/picolibc/CMakeLists.txt @@ -1,10 +1,15 @@ # SPDX-License-Identifier: Apache-2.0 zephyr_library() -zephyr_library_sources(libc-hooks.c) - -# Do not allow LTO when compiling libc-hooks.c file -set_source_files_properties(libc-hooks.c PROPERTIES COMPILE_OPTIONS $) +zephyr_library_sources( + assert.c + cbprintf.c + chk_fail.c + errno_wrap.c + exit.c + locks.c + stdio.c + ) # define __LINUX_ERRNO_EXTENSIONS__ so we get errno defines like -ESHUTDOWN # used by the network stack diff --git a/lib/libc/picolibc/assert.c b/lib/libc/picolibc/assert.c new file mode 100644 index 00000000000000..806d996a230a12 --- /dev/null +++ b/lib/libc/picolibc/assert.c @@ -0,0 +1,28 @@ +/* + * Copyright © 2021, Keith Packard + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "picolibc-hooks.h" + +#ifdef CONFIG_PICOLIBC_ASSERT_VERBOSE + +FUNC_NORETURN void __assert_func(const char *file, int line, + const char *function, const char *expression) +{ + __ASSERT(0, "assertion \"%s\" failed: file \"%s\", line %d%s%s\n", + expression, file, line, + function ? ", function: " : "", function ? function : ""); + CODE_UNREACHABLE; +} + +#else + +FUNC_NORETURN void __assert_no_args(void) +{ + __ASSERT_NO_MSG(0); + CODE_UNREACHABLE; +} + +#endif diff --git a/lib/libc/picolibc/cbprintf.c b/lib/libc/picolibc/cbprintf.c new file mode 100644 index 00000000000000..a81aaaf22bdfa9 --- /dev/null +++ b/lib/libc/picolibc/cbprintf.c @@ -0,0 +1,31 @@ +/* + * Copyright © 2021, Keith Packard + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "picolibc-hooks.h" + +struct cb_bits { + FILE f; + cbprintf_cb out; + void *ctx; +}; + +static int cbputc(char c, FILE *_s) +{ + struct cb_bits *s = (struct cb_bits *) _s; + + (*s->out) (c, s->ctx); + return 0; +} + +int cbvprintf(cbprintf_cb out, void *ctx, const char *fp, va_list ap) +{ + struct cb_bits s = { + .f = FDEV_SETUP_STREAM(cbputc, NULL, NULL, _FDEV_SETUP_WRITE), + .out = out, + .ctx = ctx, + }; + return vfprintf(&s.f, fp, ap); +} diff --git a/lib/libc/picolibc/chk_fail.c b/lib/libc/picolibc/chk_fail.c new file mode 100644 index 00000000000000..24abbcb34f5b54 --- /dev/null +++ b/lib/libc/picolibc/chk_fail.c @@ -0,0 +1,18 @@ +/* + * Copyright © 2021, Keith Packard + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "picolibc-hooks.h" + +/* 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) +{ + printk("* buffer overflow detected *\n"); + z_except_reason(K_ERR_STACK_CHK_FAIL); + CODE_UNREACHABLE; +} diff --git a/lib/libc/picolibc/errno_wrap.c b/lib/libc/picolibc/errno_wrap.c new file mode 100644 index 00000000000000..4e9b81cec54b92 --- /dev/null +++ b/lib/libc/picolibc/errno_wrap.c @@ -0,0 +1,21 @@ +/* + * Copyright © 2021, Keith Packard + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "picolibc-hooks.h" + +#ifndef CONFIG_LIBC_ERRNO + +/* + * Picolibc needs to be able to declare this itself so that the library + * doesn't end up needing zephyr header files. That means using a regular + * function instead of an inline. + */ +int *z_errno_wrap(void) +{ + return z_errno(); +} + +#endif diff --git a/lib/libc/picolibc/exit.c b/lib/libc/picolibc/exit.c new file mode 100644 index 00000000000000..cf4ab856e25ac4 --- /dev/null +++ b/lib/libc/picolibc/exit.c @@ -0,0 +1,15 @@ +/* + * Copyright © 2021, Keith Packard + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "picolibc-hooks.h" + +__weak void _exit(int status) +{ + printf("exit\n"); + while (1) { + Z_SPIN_DELAY(100); + } +} diff --git a/lib/libc/picolibc/libc-hooks.c b/lib/libc/picolibc/libc-hooks.c deleted file mode 100644 index cefb0d46ddd37c..00000000000000 --- a/lib/libc/picolibc/libc-hooks.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright © 2021, Keith Packard - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_MMU -#include -#endif - -#define LIBC_BSS K_APP_BMEM(z_libc_partition) -#define LIBC_DATA K_APP_DMEM(z_libc_partition) - -static LIBC_DATA int (*_stdout_hook)(int); - -int z_impl_zephyr_fputc(int a, FILE *out) -{ - (*_stdout_hook)(a); - return 0; -} - -#ifdef CONFIG_USERSPACE -static inline int z_vrfy_zephyr_fputc(int c, FILE *stream) -{ - return z_impl_zephyr_fputc(c, stream); -} -#include -#endif - -static int picolibc_put(char a, FILE *f) -{ - zephyr_fputc(a, f); - return 0; -} - -static LIBC_DATA FILE __stdout = FDEV_SETUP_STREAM(picolibc_put, NULL, NULL, 0); -static LIBC_DATA FILE __stdin = FDEV_SETUP_STREAM(NULL, NULL, NULL, 0); - -#ifdef __strong_reference -#define STDIO_ALIAS(x) __strong_reference(stdout, x); -#else -#define STDIO_ALIAS(x) FILE *const x = &__stdout; -#endif - -FILE *const stdin = &__stdin; -FILE *const stdout = &__stdout; -STDIO_ALIAS(stderr); - -void __stdout_hook_install(int (*hook)(int)) -{ - _stdout_hook = hook; - __stdout.flags |= _FDEV_SETUP_WRITE; -} - -void __stdin_hook_install(unsigned char (*hook)(void)) -{ - __stdin.get = (int (*)(FILE *)) hook; - __stdin.flags |= _FDEV_SETUP_READ; -} - -#include - -struct cb_bits { - FILE f; - cbprintf_cb out; - void *ctx; -}; - -static int cbputc(char c, FILE *_s) -{ - struct cb_bits *s = (struct cb_bits *) _s; - - (*s->out) (c, s->ctx); - return 0; -} - -int cbvprintf(cbprintf_cb out, void *ctx, const char *fp, va_list ap) -{ - struct cb_bits s = { - .f = FDEV_SETUP_STREAM(cbputc, NULL, NULL, _FDEV_SETUP_WRITE), - .out = out, - .ctx = ctx, - }; - return vfprintf(&s.f, fp, ap); -} - -__weak void _exit(int status) -{ - printk("exit\n"); - while (1) { - Z_SPIN_DELAY(100); - } -} - -#ifdef CONFIG_MULTITHREADING -#define _LOCK_T void * -K_MUTEX_DEFINE(__lock___libc_recursive_mutex); - -#ifdef CONFIG_USERSPACE -/* Grant public access to picolibc lock after boot */ -static int picolibc_locks_prepare(void) -{ - - /* Initialise recursive locks */ - k_object_access_all_grant(&__lock___libc_recursive_mutex); - - return 0; -} - -SYS_INIT(picolibc_locks_prepare, POST_KERNEL, - CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); -#endif /* CONFIG_USERSPACE */ - -/* Create a new dynamic recursive lock */ -void __retarget_lock_init_recursive(_LOCK_T *lock) -{ - __ASSERT_NO_MSG(lock != NULL); - - /* Allocate mutex object */ -#ifndef CONFIG_USERSPACE - *lock = malloc(sizeof(struct k_mutex)); -#else - *lock = k_object_alloc(K_OBJ_MUTEX); -#endif /* !CONFIG_USERSPACE */ - __ASSERT(*lock != NULL, "recursive lock allocation failed"); - - k_mutex_init((struct k_mutex *)*lock); -} - -/* Create a new dynamic non-recursive lock */ -void __retarget_lock_init(_LOCK_T *lock) -{ - __retarget_lock_init_recursive(lock); -} - -/* Close dynamic recursive lock */ -void __retarget_lock_close_recursive(_LOCK_T lock) -{ - __ASSERT_NO_MSG(lock != NULL); -#ifndef CONFIG_USERSPACE - free(lock); -#else - k_object_release(lock); -#endif /* !CONFIG_USERSPACE */ -} - -/* Close dynamic non-recursive lock */ -void __retarget_lock_close(_LOCK_T lock) -{ - __retarget_lock_close_recursive(lock); -} - -/* Acquiure recursive lock */ -void __retarget_lock_acquire_recursive(_LOCK_T lock) -{ - __ASSERT_NO_MSG(lock != NULL); - k_mutex_lock((struct k_mutex *)lock, K_FOREVER); -} - -/* Acquiure non-recursive lock */ -void __retarget_lock_acquire(_LOCK_T lock) -{ - __retarget_lock_acquire_recursive(lock); -} - -/* Try acquiring recursive lock */ -int __retarget_lock_try_acquire_recursive(_LOCK_T lock) -{ - __ASSERT_NO_MSG(lock != NULL); - return !k_mutex_lock((struct k_mutex *)lock, K_NO_WAIT); -} - -/* Try acquiring non-recursive lock */ -int __retarget_lock_try_acquire(_LOCK_T lock) -{ - return __retarget_lock_try_acquire_recursive(lock); -} - -/* Release recursive lock */ -void __retarget_lock_release_recursive(_LOCK_T lock) -{ - __ASSERT_NO_MSG(lock != NULL); - k_mutex_unlock((struct k_mutex *)lock); -} - -/* Release non-recursive lock */ -void __retarget_lock_release(_LOCK_T lock) -{ - __retarget_lock_release_recursive(lock); -} - -#endif /* CONFIG_MULTITHREADING */ - -#ifdef CONFIG_PICOLIBC_ASSERT_VERBOSE - -FUNC_NORETURN void __assert_func(const char *file, int line, - const char *function, const char *expression) -{ - __ASSERT(0, "assertion \"%s\" failed: file \"%s\", line %d%s%s\n", - expression, file, line, - function ? ", function: " : "", function ? function : ""); - CODE_UNREACHABLE; -} - -#else - -FUNC_NORETURN void __assert_no_args(void) -{ - __ASSERT_NO_MSG(0); - CODE_UNREACHABLE; -} - -#endif - -/* 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) -{ - printk("* buffer overflow detected *\n"); - z_except_reason(K_ERR_STACK_CHK_FAIL); - CODE_UNREACHABLE; -} - -#ifndef CONFIG_LIBC_ERRNO - -/* - * Picolibc needs to be able to declare this itself so that the library - * doesn't end up needing zephyr header files. That means using a regular - * function instead of an inline. - */ -int *z_errno_wrap(void) -{ - return z_errno(); -} - -#endif diff --git a/lib/libc/picolibc/locks.c b/lib/libc/picolibc/locks.c new file mode 100644 index 00000000000000..c32ab158398486 --- /dev/null +++ b/lib/libc/picolibc/locks.c @@ -0,0 +1,106 @@ +/* + * Copyright © 2021, Keith Packard + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "picolibc-hooks.h" + +#ifdef CONFIG_MULTITHREADING +#define _LOCK_T void * +K_MUTEX_DEFINE(__lock___libc_recursive_mutex); + +#ifdef CONFIG_USERSPACE +/* Grant public access to picolibc lock after boot */ +static int picolibc_locks_prepare(void) +{ + + /* Initialise recursive locks */ + k_object_access_all_grant(&__lock___libc_recursive_mutex); + + return 0; +} + +SYS_INIT(picolibc_locks_prepare, POST_KERNEL, + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); +#endif /* CONFIG_USERSPACE */ + +/* Create a new dynamic recursive lock */ +void __retarget_lock_init_recursive(_LOCK_T *lock) +{ + __ASSERT_NO_MSG(lock != NULL); + + /* Allocate mutex object */ +#ifndef CONFIG_USERSPACE + *lock = malloc(sizeof(struct k_mutex)); +#else + *lock = k_object_alloc(K_OBJ_MUTEX); +#endif /* !CONFIG_USERSPACE */ + __ASSERT(*lock != NULL, "recursive lock allocation failed"); + + k_mutex_init((struct k_mutex *)*lock); +} + +/* Create a new dynamic non-recursive lock */ +void __retarget_lock_init(_LOCK_T *lock) +{ + __retarget_lock_init_recursive(lock); +} + +/* Close dynamic recursive lock */ +void __retarget_lock_close_recursive(_LOCK_T lock) +{ + __ASSERT_NO_MSG(lock != NULL); +#ifndef CONFIG_USERSPACE + free(lock); +#else + k_object_release(lock); +#endif /* !CONFIG_USERSPACE */ +} + +/* Close dynamic non-recursive lock */ +void __retarget_lock_close(_LOCK_T lock) +{ + __retarget_lock_close_recursive(lock); +} + +/* Acquiure recursive lock */ +void __retarget_lock_acquire_recursive(_LOCK_T lock) +{ + __ASSERT_NO_MSG(lock != NULL); + k_mutex_lock((struct k_mutex *)lock, K_FOREVER); +} + +/* Acquiure non-recursive lock */ +void __retarget_lock_acquire(_LOCK_T lock) +{ + __retarget_lock_acquire_recursive(lock); +} + +/* Try acquiring recursive lock */ +int __retarget_lock_try_acquire_recursive(_LOCK_T lock) +{ + __ASSERT_NO_MSG(lock != NULL); + return !k_mutex_lock((struct k_mutex *)lock, K_NO_WAIT); +} + +/* Try acquiring non-recursive lock */ +int __retarget_lock_try_acquire(_LOCK_T lock) +{ + return __retarget_lock_try_acquire_recursive(lock); +} + +/* Release recursive lock */ +void __retarget_lock_release_recursive(_LOCK_T lock) +{ + __ASSERT_NO_MSG(lock != NULL); + k_mutex_unlock((struct k_mutex *)lock); +} + +/* Release non-recursive lock */ +void __retarget_lock_release(_LOCK_T lock) +{ + __retarget_lock_release_recursive(lock); +} + +#endif /* CONFIG_MULTITHREADING */ diff --git a/lib/libc/picolibc/picolibc-hooks.h b/lib/libc/picolibc/picolibc-hooks.h new file mode 100644 index 00000000000000..1800e5ee404e37 --- /dev/null +++ b/lib/libc/picolibc/picolibc-hooks.h @@ -0,0 +1,32 @@ +/* + * Copyright © 2021, Keith Packard + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _PICOLIBC_HOOKS_H_ +#define _PICOLIBC_HOOKS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_MMU +#include +#endif + +#define LIBC_BSS K_APP_BMEM(z_libc_partition) +#define LIBC_DATA K_APP_DMEM(z_libc_partition) + +#endif /* _PICOLIBC_HOOKS_H_ */ diff --git a/lib/libc/picolibc/stdio.c b/lib/libc/picolibc/stdio.c new file mode 100644 index 00000000000000..342eedc8771010 --- /dev/null +++ b/lib/libc/picolibc/stdio.c @@ -0,0 +1,54 @@ +/* + * Copyright © 2021, Keith Packard + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "picolibc-hooks.h" + +static LIBC_DATA int (*_stdout_hook)(int); + +int z_impl_zephyr_fputc(int a, FILE *out) +{ + (*_stdout_hook)(a); + return 0; +} + +#ifdef CONFIG_USERSPACE +static inline int z_vrfy_zephyr_fputc(int c, FILE *stream) +{ + return z_impl_zephyr_fputc(c, stream); +} +#include +#endif + +static int picolibc_put(char a, FILE *f) +{ + zephyr_fputc(a, f); + return 0; +} + +static LIBC_DATA FILE __stdout = FDEV_SETUP_STREAM(picolibc_put, NULL, NULL, 0); +static LIBC_DATA FILE __stdin = FDEV_SETUP_STREAM(NULL, NULL, NULL, 0); + +#ifdef __strong_reference +#define STDIO_ALIAS(x) __strong_reference(stdout, x); +#else +#define STDIO_ALIAS(x) FILE *const x = &__stdout; +#endif + +FILE *const stdin = &__stdin; +FILE *const stdout = &__stdout; +STDIO_ALIAS(stderr); + +void __stdout_hook_install(int (*hook)(int)) +{ + _stdout_hook = hook; + __stdout.flags |= _FDEV_SETUP_WRITE; +} + +void __stdin_hook_install(unsigned char (*hook)(void)) +{ + __stdin.get = (int (*)(FILE *)) hook; + __stdin.flags |= _FDEV_SETUP_READ; +}