Skip to content

Commit

Permalink
libc: minimal: errno.h, strerror to common, strerror_r to posix
Browse files Browse the repository at this point in the history
The strerror_r() function is POSIX. It's not part of ISO C.
Moreover, the versions available in both Newlib and Picolibc do
not seem to comply with the POSIX specification OOTB.

Moving this function to lib/posix ensures that Zephyr
applications have a consistent implementation of the POSIX API.

Signed-off-by: Christopher Friedt <[email protected]>
  • Loading branch information
cfriedt committed Jan 13, 2024
1 parent ae0ad38 commit 7d7ac6d
Show file tree
Hide file tree
Showing 27 changed files with 223 additions and 146 deletions.
24 changes: 24 additions & 0 deletions doc/develop/languages/c/common_libc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,27 @@ The standard dynamic memory management interface functions implemented by
the common C library are thread safe and may be simultaneously called by
multiple threads. These functions are implemented in
:file:`lib/libc/common/source/stdlib/malloc.c`.

Error numbers
*************

Error numbers are used throughout Zephyr APIs to signal error conditions as
return values from functions. They are typically returned as the negative value
of the integer literals defined in this section, and are defined in the
:file:`errno.h` header file.

A subset of the error numbers defined in the `POSIX errno.h specification`_ and
other de-facto standard sources have been added to the common libc resources.

A conscious effort is made in Zephyr to keep the values of the common libc
error numbers consistent with the different implementations of the C standard
libraries supported by Zephyr. The minimal libc :file:`errno.h` is checked
against that of the :ref:`Newlib <c_library_newlib>` to ensure that the error
numbers are kept aligned.

Below is a list of the error number definitions. For the actual numeric values
please refer to :file:`lib/libc/common/include/errno.h`.

.. doxygengroup:: system_errno

.. _`POSIX errno.h specification`: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
25 changes: 0 additions & 25 deletions doc/develop/languages/c/minimal_libc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,28 +40,3 @@ Dynamic Memory Management
The minimal libc uses the malloc api family implementation provided by the
:ref:`common C library <c_library_common>`, which itself is built upon the
:ref:`kernel memory heap API <heap_v2>`.

Error numbers
*************

Error numbers are used throughout Zephyr APIs to signal error conditions as
return values from functions. They are typically returned as the negative value
of the integer literals defined in this section, and are defined in the
:file:`errno.h` header file.

A subset of the error numbers defined in the `POSIX errno.h specification`_ and
other de-facto standard sources have been added to the minimal libc.

A conscious effort is made in Zephyr to keep the values of the minimal libc
error numbers consistent with the different implementations of the C standard
libraries supported by Zephyr. The minimal libc :file:`errno.h` is checked
against that of the :ref:`Newlib <c_library_newlib>` to ensure that the error
numbers are kept aligned.

Below is a list of the error number definitions. For the actual numeric values
please refer to `errno.h`_.

.. doxygengroup:: system_errno

.. _`POSIX errno.h specification`: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
.. _`errno.h`: https://github.com/zephyrproject-rtos/zephyr/blob/main/lib/libc/minimal/include/errno.h
1 change: 1 addition & 0 deletions doc/zephyr.doxyfile.in
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,7 @@ INPUT = @ZEPHYR_BASE@/doc/_doxygen/mainpage.md \
@ZEPHYR_BASE@/doc/_doxygen/groups.dox \
@ZEPHYR_BASE@/kernel/include/kernel_arch_interface.h \
@ZEPHYR_BASE@/include/ \
@ZEPHYR_BASE@/lib/libc/common/include/ \
@ZEPHYR_BASE@/lib/libc/minimal/include/ \
@ZEPHYR_BASE@/subsys/testsuite/include/ \
@ZEPHYR_BASE@/subsys/testsuite/ztest/include/
Expand Down
2 changes: 2 additions & 0 deletions include/zephyr/sys/errno_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ extern "C" {
#ifdef CONFIG_LIBC_ERRNO
#include <errno.h>

extern int __thread errno;

static inline int *z_errno(void)
{
return &errno;
Expand Down
2 changes: 0 additions & 2 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
add_compile_options($<TARGET_PROPERTY:compiler,warning_shadow_variables>)

add_subdirectory(crc)
if(NOT CONFIG_EXTERNAL_LIBC)
add_subdirectory(libc)
endif()
if(NOT CONFIG_NATIVE_LIBC)
add_subdirectory(posix)
endif()
Expand Down
3 changes: 3 additions & 0 deletions lib/libc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ config MINIMAL_LIBC
imply COMPILER_FREESTANDING
select COMMON_LIBC_ABORT
select COMMON_LIBC_STRNLEN
select COMMON_LIBC_STRERROR
help
Build with minimal C library.

Expand All @@ -86,6 +87,7 @@ config PICOLIBC
imply COMMON_LIBC_MALLOC
depends on !NATIVE_APPLICATION
depends on PICOLIBC_SUPPORTED
select COMMON_LIBC_STRERROR
help
Build with picolibc library. The picolibc library is built as
a module if PICOLIBC_MODULE is set, otherwise picolibc is
Expand All @@ -97,6 +99,7 @@ config NEWLIB_LIBC
depends on !NATIVE_APPLICATION
depends on NEWLIB_LIBC_SUPPORTED
select NEED_LIBC_MEM_PARTITION
select COMMON_LIBC_STRERROR
help
Build with newlib library. The newlib library is expected to be
part of the SDK in this case.
Expand Down
18 changes: 18 additions & 0 deletions lib/libc/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@

zephyr_system_include_directories(include)

set(GEN_DIR ${ZEPHYR_BINARY_DIR}/include/generated)
set(STRERROR_TABLE_H ${GEN_DIR}/libc/common/strerror_table.h)

zephyr_library()
zephyr_library_property(ALLOW_EMPTY TRUE)
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_ABORT source/stdlib/abort.c)
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_TIME source/time/time.c)
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_MALLOC source/stdlib/malloc.c)
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_STRNLEN source/string/strnlen.c)
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_STRERROR
${STRERROR_TABLE_H}
source/string/strerror.c
)
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_THRD
source/thrd/cnd.c
source/thrd/mtx.c
Expand All @@ -18,3 +25,14 @@ zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_THRD

# Prevent compiler from optimizing calloc into an infinite recursive call
zephyr_library_compile_options($<TARGET_PROPERTY:compiler,no_builtin_malloc>)

add_custom_command(
OUTPUT ${STRERROR_TABLE_H}
COMMAND
${PYTHON_EXECUTABLE}
${ZEPHYR_BASE}/scripts/build/gen_strerror_table.py
-i include/errno.h
-o ${STRERROR_TABLE_H}
DEPENDS include/errno.h
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
16 changes: 16 additions & 0 deletions lib/libc/common/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,22 @@ config COMMON_LIBC_STRNLEN
help
common implementation of strnlen().

config COMMON_LIBC_STRERROR
bool "Common C library strerror"
default y
help
common implementation of strerror().

config COMMON_LIBC_STRING_ERROR_TABLE
bool "String error table for strerror()"
depends on COMMON_LIBC_STRERROR
help
Select this option to ensure that strerror() produces strings corresponding to the
descriptions in errno.h.

The string error table can add ~2kiB to ROM. As such, it is disabled by default. In this
case, strerror() is still present but it produces an empty string.

config COMMON_LIBC_THRD
bool "C11 <threads.h> API support"
depends on DYNAMIC_THREAD
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
* @brief System error numbers
*/

#ifndef ZEPHYR_LIB_LIBC_MINIMAL_INCLUDE_ERRNO_H_
#define ZEPHYR_LIB_LIBC_MINIMAL_INCLUDE_ERRNO_H_
#ifndef ZEPHYR_LIB_LIBC_COMMON_INCLUDE_ERRNO_H_
#define ZEPHYR_LIB_LIBC_COMMON_INCLUDE_ERRNO_H_

/**
* @brief System error numbers
Expand Down Expand Up @@ -127,4 +127,4 @@ extern "C" {
}
#endif

#endif /* ZEPHYR_LIB_LIBC_MINIMAL_INCLUDE_ERRNO_H_ */
#endif /* ZEPHYR_LIB_LIBC_COMMON_INCLUDE_ERRNO_H_ */
30 changes: 30 additions & 0 deletions lib/libc/common/source/string/strerror.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2022 Meta
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <errno.h>
#include <string.h>

#include <zephyr/sys/util.h>
#include <zephyr/toolchain.h>

/*
* See scripts/build/gen_strerror_table.py
*
* #define sys_nerr N
* const char *const sys_errlist[sys_nerr];
* const uint8_t sys_errlen[sys_nerr];
*/
#define __sys_errlist_declare
#include "libc/common/strerror_table.h"

char *strerror(int errnum)
{
if (IS_ENABLED(CONFIG_COMMON_LIBC_STRING_ERROR_TABLE) && errnum >= 0 && errnum < sys_nerr) {
return (char *)sys_errlist[errnum];
}

return (char *) "";
}
16 changes: 0 additions & 16 deletions lib/libc/minimal/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ zephyr_system_include_directories(include)

zephyr_library()

set(GEN_DIR ${ZEPHYR_BINARY_DIR}/include/generated)
set(STRERROR_TABLE_H ${GEN_DIR}/libc/minimal/strerror_table.h)

zephyr_library_compile_options($<TARGET_PROPERTY:compiler,no_builtin>)

zephyr_library_sources(
Expand All @@ -18,7 +15,6 @@ zephyr_library_sources(
source/stdlib/bsearch.c
source/stdlib/exit.c
source/stdlib/qsort.c
source/string/strerror.c
source/string/strncasecmp.c
source/string/strstr.c
source/string/string.c
Expand All @@ -28,22 +24,10 @@ zephyr_library_sources(
source/stdout/fprintf.c
source/math/sqrtf.c
source/math/sqrt.c
${STRERROR_TABLE_H}
)

if(CONFIG_MINIMAL_LIBC_TIME)
zephyr_library_sources(source/time/gmtime.c)
endif()

zephyr_library_sources_ifdef(CONFIG_MINIMAL_LIBC_RAND source/stdlib/rand.c)

add_custom_command(
OUTPUT ${STRERROR_TABLE_H}
COMMAND
${PYTHON_EXECUTABLE}
${ZEPHYR_BASE}/scripts/build/gen_strerror_table.py
-i include/errno.h
-o ${STRERROR_TABLE_H}
DEPENDS include/errno.h
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
13 changes: 5 additions & 8 deletions lib/libc/minimal/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,11 @@ config MINIMAL_LIBC_TIME
to set CONFIG_MINIMAL_LIBC_NON_REENTRANT_FUNCTIONS=y.

config MINIMAL_LIBC_STRING_ERROR_TABLE
bool "String error table for strerror() and strerror_r()"
bool "String error table for strerror() [DEPRECATED]"
select DEPRECATED
select COMMON_LIBC_STRING_ERROR_TABLE
help
Select this option to ensure that streror(), strerror_r()
produce strings corresponding to the descriptions in errno.h.

The string error table can add ~2kiB to ROM. As such, it is
disabled by default. In this case, strerror() and strerror_r()
symbols are still present, but the functions produce an empty
string.
This option is deprecated. Use CONFIG_COMMON_LIBC_STRING_ERROR_TABLE
instead.

endif # MINIMAL_LIBC
2 changes: 2 additions & 0 deletions lib/libc/minimal/include/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ extern "C" {

extern char *strcpy(char *ZRESTRICT d, const char *ZRESTRICT s);
extern char *strerror(int errnum);
#if _POSIX_C_SOURCE >= 200112L
extern int strerror_r(int errnum, char *strerrbuf, size_t buflen);
#endif
extern char *strncpy(char *ZRESTRICT d, const char *ZRESTRICT s,
size_t n);
extern char *strchr(const char *s, int c);
Expand Down
1 change: 1 addition & 0 deletions lib/posix/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ zephyr_library_sources_ifdef(CONFIG_POSIX_CLOCK clock.c)
zephyr_library_sources_ifdef(CONFIG_POSIX_CLOCK nanosleep.c)
zephyr_library_sources_ifdef(CONFIG_POSIX_CLOCK sleep.c)
zephyr_library_sources_ifdef(CONFIG_POSIX_CLOCK timer.c)
zephyr_library_sources_ifdef(CONFIG_POSIX_CX_STRERROR_R strerror_r.c)
zephyr_library_sources_ifdef(CONFIG_POSIX_FS fs.c)
zephyr_library_sources_ifdef(CONFIG_POSIX_MQUEUE mqueue.c)
zephyr_library_sources_ifdef(CONFIG_POSIX_SIGNAL signal.c ${STRSIGNAL_TABLE_H})
Expand Down
3 changes: 3 additions & 0 deletions lib/posix/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,6 @@ source "lib/posix/Kconfig.signal"
source "lib/posix/Kconfig.spinlock"
source "lib/posix/Kconfig.timer"
source "lib/posix/Kconfig.uname"

# POSIX extensions to ISO C
source "lib/posix/Kconfig.cx.string"
15 changes: 15 additions & 0 deletions lib/posix/Kconfig.cx.string
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright (c) 2023 Meta
#
# SPDX-License-Identifier: Apache-2.0

# CX: Extension to the ISO C standard
# See IEEE 1003.1-2017

config POSIX_CX_STRERROR_R
bool "Get error message string"
default y if POSIX_API
select COMMON_LIBC_STRERROR
help
Enable the strerror_r() POSIX C extension.

This function is a thread-safe version of ISO C strerror().
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,7 @@
* const char *const sys_errlist[sys_nerr];
* const uint8_t sys_errlen[sys_nerr];
*/
#include "libc/minimal/strerror_table.h"

/*
* See https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror.html
*/
char *strerror(int errnum)
{
if (IS_ENABLED(CONFIG_MINIMAL_LIBC_STRING_ERROR_TABLE) &&
errnum >= 0 && errnum < sys_nerr) {
return (char *)sys_errlist[errnum];
}

return (char *) "";
}
#include "libc/common/strerror_table.h"

/*
* See
Expand All @@ -41,7 +28,7 @@ int strerror_r(int errnum, char *strerrbuf, size_t buflen)
size_t msg_len;

if (errnum >= 0 && errnum < sys_nerr) {
if (IS_ENABLED(CONFIG_MINIMAL_LIBC_STRING_ERROR_TABLE)) {
if (IS_ENABLED(CONFIG_COMMON_LIBC_STRING_ERROR_TABLE)) {
msg = sys_errlist[errnum];
msg_len = sys_errlen[errnum];
} else {
Expand Down
26 changes: 23 additions & 3 deletions scripts/build/gen_strerror_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,25 @@ def front_matter(sys_nerr):
#include <zephyr/sys/util.h>
#define sys_nerr {sys_nerr}'''
#define sys_nerr {sys_nerr}
#ifndef __sys_errlist_declare
extern const char *const sys_errlist[{sys_nerr}];
extern const uint8_t sys_errlen[{sys_nerr}];
#else
const char *const sys_errlist[{sys_nerr}];
const uint8_t sys_errlen[{sys_nerr}];
'''

def end_matter():
return f'''
#endif /* __sys_errlist_declare */
'''


def gen_strerror_table(input, output):
Expand Down Expand Up @@ -61,7 +79,7 @@ def gen_strerror_table(input, output):

# Generate string table
print(
f'static const char *const sys_errlist[sys_nerr] = {{', file=outf)
f'const char *const sys_errlist[sys_nerr] = {{', file=outf)
print('[0] = "Success",', file=outf)
for symbol in symbols:
print(f'[{symbol}] = "{msgs[symbol]}",', file=outf)
Expand All @@ -70,13 +88,15 @@ def gen_strerror_table(input, output):

# Generate string lengths (includes trailing '\0')
print(
f'static const uint8_t sys_errlen[sys_nerr] = {{', file=outf)
f'const uint8_t sys_errlen[sys_nerr] = {{', file=outf)
print('[0] = 8,', file=outf)
for symbol in symbols:
print(f'[{symbol}] = {len(msgs[symbol]) + 1},', file=outf)

print('};', file=outf)

print(end_matter(), file=outf)


def parse_args():
parser = argparse.ArgumentParser(allow_abbrev=False)
Expand Down
Loading

0 comments on commit 7d7ac6d

Please sign in to comment.