libc: minimal: Add rand() and srand()

rand() and srand() are pseudo-random number generator functions
defined in ISO C. This implementation uses the Linear Congruential
Generator (LCG) algorithm with the following parameters, which are the
same as used in GNU Libc "TYPE_0" algorithm.

  Modulus 2^31
  Multiplier 1103515245
  Increment 12345
  Output Bits 30..0

Note that the default algorithm used by GNU Libc is not TYPE_0, and
TYPE_0 should be selected first by an initstate() call as shown below.

All global variables in a C library must be routed to a memory
partition in order to be used by user-mode applications when
CONFIG_USERSPACE is enabled.  Thus, srand_seed is marked as
such. z_libc_partition is originally used by the Newlib C library but
it's generic enough to be used by either the minimal libc or the
newlib.

All other functions in the Minimal C library, however, don't require
global variables/states.  Unconditionally using z_libc_partition with
the minimal libc might be a problem for applications utilizing many
custom memory partitions on platforms with a limited number of MPU
regions (eg. Cortex M0/M3). This commit introduces a kconfig option
CONFIG_MINIMAL_LIBC_RAND so that applications can enable the
functions if needed.  The option is disabled by default.

Because this commit _does_ implement rand() and srand(), our coding
guideline check on GitHub Action finds it as a violation.

    Error: lib/libc/minimal/include/stdlib.h:45:WARNING: Violation to
    rule 21.2 (Should not used a reserved identifier) - srand

But this is false positive.

The following is a simple test program for LCG with GNU Libc.

  #include <stdio.h>
  #include <stdlib.h>

  int main()
  {
          static char state[8];

          /* Switch GLIBC to use LCG/TYPE_0 generator type. */
          initstate(0, state, sizeof(state));

          srand(1);  /* Or any other value. */
          printf("%d\n", rand());
          printf("%d\n", rand());

          return 0;
  }

See initstate(3p) for more detail about how to use LCG in GLIBC.

Signed-off-by: Yasushi SHOJI <yashi@spacecubics.com>
This commit is contained in:
Yasushi SHOJI 2021-05-08 23:52:33 +09:00 committed by Christopher Friedt
commit b2fde24c4c
5 changed files with 57 additions and 5 deletions

View file

@ -69,11 +69,14 @@ extern struct k_mem_partition z_malloc_partition;
#if defined(CONFIG_NEWLIB_LIBC) || defined(CONFIG_STACK_CANARIES) || \ #if defined(CONFIG_NEWLIB_LIBC) || defined(CONFIG_STACK_CANARIES) || \
defined(CONFIG_NEED_LIBC_MEM_PARTITION) defined(CONFIG_NEED_LIBC_MEM_PARTITION)
/* Minimal libc has no globals. We do put the stack canary global in the /* - All newlib globals will be placed into z_libc_partition.
* libc partition since it is not worth placing in a partition of its own. * - Minimal C library globals, if any, will be placed into
* * z_libc_partition.
* Some architectures require a global pointer for thread local storage, * - Stack canary globals will be placed into z_libc_partition since
* which is placed inside the libc partition. * it is not worth placing in its own partition.
* - Some architectures may place the global pointer to the thread local
* storage in z_libc_partition since it is not worth placing in its
* own partition.
*/ */
#define Z_LIBC_PARTITION_EXISTS 1 #define Z_LIBC_PARTITION_EXISTS 1

View file

@ -145,6 +145,19 @@ config MINIMAL_LIBC_OPTIMIZE_STRING_FOR_SIZE
Enable smaller but potentially slower implementations of memcpy and Enable smaller but potentially slower implementations of memcpy and
memset. On the Cortex-M0+ this reduces the total code size by 120 bytes. memset. On the Cortex-M0+ this reduces the total code size by 120 bytes.
config MINIMAL_LIBC_RAND
bool "Enables rand and srand functions"
select NEED_LIBC_MEM_PARTITION
help
Enable rand() and srand() for the minimal libc. The
functions implicitly access global/static data. Such data
must be put into a memory partition if CONFIG_USERSPACE=y,
and disabling this option may save an entry for application
defining many custom partitions.
Say 'Y' here if you need rand() and srand(). This might require
an additional memory partition.
endif # MINIMAL_LIBC endif # MINIMAL_LIBC
config STDOUT_CONSOLE config STDOUT_CONSOLE

View file

@ -22,3 +22,4 @@ zephyr_library_sources(
) )
zephyr_library_sources_ifdef(CONFIG_POSIX_CLOCK source/time/time.c) zephyr_library_sources_ifdef(CONFIG_POSIX_CLOCK source/time/time.c)
zephyr_library_sources_ifdef(CONFIG_MINIMAL_LIBC_RAND source/stdlib/rand.c)

View file

@ -10,6 +10,8 @@
#define ZEPHYR_LIB_LIBC_MINIMAL_INCLUDE_STDLIB_H_ #define ZEPHYR_LIB_LIBC_MINIMAL_INCLUDE_STDLIB_H_
#include <stddef.h> #include <stddef.h>
#include <limits.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -37,7 +39,11 @@ static inline void exit(int status)
} }
void abort(void); void abort(void);
#ifdef CONFIG_MINIMAL_LIBC_RAND
#define RAND_MAX INT_MAX
int rand(void); int rand(void);
void srand(unsigned int seed);
#endif /* CONFIG_MINIMAL_LIBC_RAND */
static inline int abs(int __n) static inline int abs(int __n)
{ {

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2021 Space Cubics, LLC.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <sys/libc-hooks.h>
#include <app_memory/app_memdomain.h>
#define LIBC_DATA K_APP_DMEM(z_libc_partition)
#define OUTPUT_BITS (0x7fffffffU)
#define MULTIPLIER (1103515245U)
#define INCREMENT (12345U)
static LIBC_DATA unsigned long srand_seed = 1;
void srand(unsigned int s)
{
srand_seed = s;
}
int rand(void)
{
srand_seed = (MULTIPLIER * srand_seed + INCREMENT) & OUTPUT_BITS;
return srand_seed;
}