diff options
Diffstat (limited to 'builtin/stdlib.c')
-rw-r--r-- | builtin/stdlib.c | 465 |
1 files changed, 465 insertions, 0 deletions
diff --git a/builtin/stdlib.c b/builtin/stdlib.c new file mode 100644 index 0000000000..0d654f0395 --- /dev/null +++ b/builtin/stdlib.c @@ -0,0 +1,465 @@ +/* Copyright 2021 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Standard library utility functions for Chrome EC */ + +#include "common.h" +#include "console.h" +#include "printf.h" +#include "util.h" + +#include <stdio.h> + +/* + * The following macros are defined in stdlib.h in the C standard library, which + * conflict with the definitions in this file. + */ +#undef isspace +#undef isdigit +#undef isalpha +#undef isupper +#undef isprint +#undef tolower + +/* Context for snprintf() */ +struct snprintf_context { + char *str; + int size; +}; + +/** + * Add a character to the string context. + * + * @param context Context receiving character + * @param c Character to add + * @return 0 if character added, 1 if character dropped because no space. + */ +static int snprintf_addchar(void *context, int c) +{ + struct snprintf_context *ctx = (struct snprintf_context *)context; + + if (!ctx->size) + return 1; + + *(ctx->str++) = c; + ctx->size--; + return 0; +} + +int crec_vsnprintf(char *str, size_t size, const char *format, va_list args) +{ + struct snprintf_context ctx; + int rv; + + if (!str || !format || size <= 0) + return -EC_ERROR_INVAL; + + ctx.str = str; + ctx.size = size - 1; /* Reserve space for terminating '\0' */ + + rv = vfnprintf(snprintf_addchar, &ctx, format, args); + + /* Terminate string */ + *ctx.str = '\0'; + + return (rv == EC_SUCCESS) ? (ctx.str - str) : -rv; +} +#ifndef CONFIG_ZEPHYR +int vsnprintf(char *str, size_t size, const char *format, va_list args) + __attribute__((weak, alias("crec_vsnprintf"))); +#endif /* CONFIG_ZEPHYR */ + +int crec_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list args; + int rv; + + va_start(args, format); + rv = crec_vsnprintf(str, size, format, args); + va_end(args); + + return rv; +} +#ifndef CONFIG_ZEPHYR +int snprintf(char *str, size_t size, const char *format, ...) + __attribute__((weak, alias("crec_snprintf"))); +#endif /* CONFIG_ZEPHYR */ + +/* + * TODO(b/237712836): Zephyr's libc should provide strcasecmp. For now we'll + * use the EC implementation. + */ +__stdlib_compat int strcasecmp(const char *s1, const char *s2) +{ + int diff; + + do { + diff = tolower(*s1) - tolower(*s2); + if (diff) + return diff; + } while (*(s1++) && *(s2++)); + return 0; +} + +/* + * TODO(b/237712836): Remove this conditional once strcasecmp is added to + * Zephyr's libc. + */ +#ifndef CONFIG_ZEPHYR +__stdlib_compat size_t strlen(const char *s) +{ + int len = 0; + + while (*s++) + len++; + + return len; +} + +__stdlib_compat size_t strnlen(const char *s, size_t maxlen) +{ + size_t len = 0; + + while (len < maxlen && *s) { + s++; + len++; + } + return len; +} + +__stdlib_compat size_t strcspn(const char *s, const char *reject) +{ + size_t i; + size_t reject_len = strlen(reject); + + for (i = 0; s[i] != 0; i++) + for (size_t j = 0; j < reject_len; j++) + if (s[i] == reject[j]) + return i; + return i; +} + +__stdlib_compat int isspace(int c) +{ + return c == ' ' || c == '\t' || c == '\r' || c == '\n'; +} + +__stdlib_compat int isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + +__stdlib_compat int isalpha(int c) +{ + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); +} + +__stdlib_compat int isupper(int c) +{ + return c >= 'A' && c <= 'Z'; +} + +__stdlib_compat int isprint(int c) +{ + return c >= ' ' && c <= '~'; +} + +__stdlib_compat int tolower(int c) +{ + return c >= 'A' && c <= 'Z' ? c + 'a' - 'A' : c; +} + +__stdlib_compat int strncasecmp(const char *s1, const char *s2, size_t size) +{ + int diff; + + if (!size) + return 0; + + do { + diff = tolower(*s1) - tolower(*s2); + if (diff) + return diff; + } while (*(s1++) && *(s2++) && --size); + return 0; +} + +__stdlib_compat char *strstr(const char *s1, const char *s2) +{ + const char *p, *q, *r; + size_t len1 = strlen(s1); + size_t len2 = strlen(s2); + + if (len1 == 0 || len2 == 0 || len1 < len2) + return NULL; + + r = s1 + len1 - len2 + 1; + for (; s1 < r; s1++) { + if (*s1 == *s2) { + p = s1 + 1; + q = s2 + 1; + for (; q < s2 + len2;) { + if (*p++ != *q++) + break; + } + if (*q == '\0') + return (char *)s1; + } + } + return NULL; +} + +__stdlib_compat unsigned long long int strtoull(const char *nptr, char **endptr, + int base) +{ + uint64_t result = 0; + int c = '\0'; + + while ((c = *nptr++) && isspace(c)) + ; + + if (c == '+') { + c = *nptr++; + } else if (c == '-') { + if (endptr) + *endptr = (char *)nptr - 1; + return result; + } + + base = find_base(base, &c, &nptr); + + while (c) { + if (c >= '0' && c < '0' + MIN(base, 10)) + result = result * base + (c - '0'); + else if (c >= 'A' && c < 'A' + base - 10) + result = result * base + (c - 'A' + 10); + else if (c >= 'a' && c < 'a' + base - 10) + result = result * base + (c - 'a' + 10); + else + break; + + c = *nptr++; + } + + if (endptr) + *endptr = (char *)nptr - 1; + return result; +} +BUILD_ASSERT(sizeof(unsigned long long int) == sizeof(uint64_t)); + +__stdlib_compat int atoi(const char *nptr) +{ + int result = 0; + int neg = 0; + char c = '\0'; + + while ((c = *nptr++) && isspace(c)) + ; + + if (c == '-') { + neg = 1; + c = *nptr++; + } + + while (isdigit(c)) { + result = result * 10 + (c - '0'); + c = *nptr++; + } + + return neg ? -result : result; +} + +__keep __stdlib_compat int memcmp(const void *s1, const void *s2, size_t len) +{ + const char *sa = s1; + const char *sb = s2; + int diff = 0; + + while (len-- > 0) { + diff = *(sa++) - *(sb++); + if (diff) + return diff; + } + + return 0; +} + +#if !(__has_feature(address_sanitizer) || __has_feature(memory_sanitizer)) +__keep __stdlib_compat void *memcpy(void *dest, const void *src, size_t len) +{ + char *d = (char *)dest; + const char *s = (const char *)src; + uint32_t *dw; + const uint32_t *sw; + char *head; + char *const tail = (char *)dest + len; + /* Set 'body' to the last word boundary */ + uint32_t *const body = (uint32_t *)((uintptr_t)tail & ~3); + + if (((uintptr_t)dest & 3) != ((uintptr_t)src & 3)) { + /* Misaligned. no body, no tail. */ + head = tail; + } else { + /* Aligned */ + if ((uintptr_t)tail < (((uintptr_t)d + 3) & ~3)) + /* len is shorter than the first word boundary */ + head = tail; + else + /* Set 'head' to the first word boundary */ + head = (char *)(((uintptr_t)d + 3) & ~3); + } + + /* Copy head */ + while (d < head) + *(d++) = *(s++); + + /* Copy body */ + dw = (uint32_t *)d; + sw = (uint32_t *)s; + while (dw < body) + *(dw++) = *(sw++); + + /* Copy tail */ + d = (char *)dw; + s = (const char *)sw; + while (d < tail) + *(d++) = *(s++); + + return dest; +} +#endif /* address_sanitizer || memory_sanitizer */ + +#if !(__has_feature(address_sanitizer) || __has_feature(memory_sanitizer)) +__keep __stdlib_compat __visible void *memset(void *dest, int c, size_t len) +{ + char *d = (char *)dest; + uint32_t cccc; + uint32_t *dw; + char *head; + char *const tail = (char *)dest + len; + /* Set 'body' to the last word boundary */ + uint32_t *const body = (uint32_t *)((uintptr_t)tail & ~3); + + c &= 0xff; /* Clear upper bits before ORing below */ + cccc = c | (c << 8) | (c << 16) | (c << 24); + + if ((uintptr_t)tail < (((uintptr_t)d + 3) & ~3)) + /* len is shorter than the first word boundary */ + head = tail; + else + /* Set 'head' to the first word boundary */ + head = (char *)(((uintptr_t)d + 3) & ~3); + + /* Copy head */ + while (d < head) + *(d++) = c; + + /* Copy body */ + dw = (uint32_t *)d; + while (dw < body) + *(dw++) = cccc; + + /* Copy tail */ + d = (char *)dw; + while (d < tail) + *(d++) = c; + + return dest; +} +#endif /* address_sanitizer || memory_sanitizer */ + +#if !(__has_feature(address_sanitizer) || __has_feature(memory_sanitizer)) +__keep __stdlib_compat void *memmove(void *dest, const void *src, size_t len) +{ + if ((uintptr_t)dest <= (uintptr_t)src || + (uintptr_t)dest >= (uintptr_t)src + len) { + /* Start of destination doesn't overlap source, so just use + * memcpy(). + */ + return memcpy(dest, src, len); + } else { + /* Need to copy from tail because there is overlap. */ + char *d = (char *)dest + len; + const char *s = (const char *)src + len; + uint32_t *dw; + const uint32_t *sw; + char *head; + char *const tail = (char *)dest; + /* Set 'body' to the last word boundary */ + uint32_t *const body = (uint32_t *)(((uintptr_t)tail + 3) & ~3); + + if (((uintptr_t)dest & 3) != ((uintptr_t)src & 3)) { + /* Misaligned. no body, no tail. */ + head = tail; + } else { + /* Aligned */ + if ((uintptr_t)tail > ((uintptr_t)d & ~3)) + /* Shorter than the first word boundary */ + head = tail; + else + /* Set 'head' to the first word boundary */ + head = (char *)((uintptr_t)d & ~3); + } + + /* Copy head */ + while (d > head) + *(--d) = *(--s); + + /* Copy body */ + dw = (uint32_t *)d; + sw = (uint32_t *)s; + while (dw > body) + *(--dw) = *(--sw); + + /* Copy tail */ + d = (char *)dw; + s = (const char *)sw; + while (d > tail) + *(--d) = *(--s); + + return dest; + } +} +#endif /* address_sanitizer || memory_sanitizer */ + +__stdlib_compat void *memchr(const void *buffer, int c, size_t n) +{ + char *current = (char *)buffer; + char *end = current + n; + + while (current != end) { + if (*current == c) + return current; + current++; + } + return NULL; +} + +__stdlib_compat char *strncpy(char *dest, const char *src, size_t n) +{ + char *d = dest; + + while (n && *src) { + *d++ = *src++; + n--; + } + if (n) + *d = '\0'; + return dest; +} + +__stdlib_compat int strncmp(const char *s1, const char *s2, size_t n) +{ + while (n--) { + if (*s1 != *s2) + return *s1 - *s2; + if (!*s1) + break; + s1++; + s2++; + } + return 0; +} +#endif /* !CONFIG_ZEPHYR */ |