292 lines · c
1/* SPDX-License-Identifier: LGPL-2.1 OR MIT */2/*3 * string function definitions for NOLIBC4 * Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu>5 */6 7#ifndef _NOLIBC_STRING_H8#define _NOLIBC_STRING_H9 10#include "arch.h"11#include "std.h"12 13static void *malloc(size_t len);14 15/*16 * As much as possible, please keep functions alphabetically sorted.17 */18 19static __attribute__((unused))20int memcmp(const void *s1, const void *s2, size_t n)21{22 size_t ofs = 0;23 int c1 = 0;24 25 while (ofs < n && !(c1 = ((unsigned char *)s1)[ofs] - ((unsigned char *)s2)[ofs])) {26 ofs++;27 }28 return c1;29}30 31#ifndef NOLIBC_ARCH_HAS_MEMMOVE32/* might be ignored by the compiler without -ffreestanding, then found as33 * missing.34 */35__attribute__((weak,unused,section(".text.nolibc_memmove")))36void *memmove(void *dst, const void *src, size_t len)37{38 size_t dir, pos;39 40 pos = len;41 dir = -1;42 43 if (dst < src) {44 pos = -1;45 dir = 1;46 }47 48 while (len) {49 pos += dir;50 ((char *)dst)[pos] = ((const char *)src)[pos];51 len--;52 }53 return dst;54}55#endif /* #ifndef NOLIBC_ARCH_HAS_MEMMOVE */56 57#ifndef NOLIBC_ARCH_HAS_MEMCPY58/* must be exported, as it's used by libgcc on ARM */59__attribute__((weak,unused,section(".text.nolibc_memcpy")))60void *memcpy(void *dst, const void *src, size_t len)61{62 size_t pos = 0;63 64 while (pos < len) {65 ((char *)dst)[pos] = ((const char *)src)[pos];66 pos++;67 }68 return dst;69}70#endif /* #ifndef NOLIBC_ARCH_HAS_MEMCPY */71 72#ifndef NOLIBC_ARCH_HAS_MEMSET73/* might be ignored by the compiler without -ffreestanding, then found as74 * missing.75 */76__attribute__((weak,unused,section(".text.nolibc_memset")))77void *memset(void *dst, int b, size_t len)78{79 char *p = dst;80 81 while (len--) {82 /* prevent gcc from recognizing memset() here */83 __asm__ volatile("");84 *(p++) = b;85 }86 return dst;87}88#endif /* #ifndef NOLIBC_ARCH_HAS_MEMSET */89 90static __attribute__((unused))91char *strchr(const char *s, int c)92{93 while (*s) {94 if (*s == (char)c)95 return (char *)s;96 s++;97 }98 return NULL;99}100 101static __attribute__((unused))102int strcmp(const char *a, const char *b)103{104 unsigned int c;105 int diff;106 107 while (!(diff = (unsigned char)*a++ - (c = (unsigned char)*b++)) && c)108 ;109 return diff;110}111 112static __attribute__((unused))113char *strcpy(char *dst, const char *src)114{115 char *ret = dst;116 117 while ((*dst++ = *src++));118 return ret;119}120 121/* this function is only used with arguments that are not constants or when122 * it's not known because optimizations are disabled. Note that gcc 12123 * recognizes an strlen() pattern and replaces it with a jump to strlen(),124 * thus itself, hence the asm() statement below that's meant to disable this125 * confusing practice.126 */127__attribute__((weak,unused,section(".text.nolibc_strlen")))128size_t strlen(const char *str)129{130 size_t len;131 132 for (len = 0; str[len]; len++)133 __asm__("");134 return len;135}136 137/* do not trust __builtin_constant_p() at -O0, as clang will emit a test and138 * the two branches, then will rely on an external definition of strlen().139 */140#if defined(__OPTIMIZE__)141#define nolibc_strlen(x) strlen(x)142#define strlen(str) ({ \143 __builtin_constant_p((str)) ? \144 __builtin_strlen((str)) : \145 nolibc_strlen((str)); \146})147#endif148 149static __attribute__((unused))150size_t strnlen(const char *str, size_t maxlen)151{152 size_t len;153 154 for (len = 0; (len < maxlen) && str[len]; len++);155 return len;156}157 158static __attribute__((unused))159char *strdup(const char *str)160{161 size_t len;162 char *ret;163 164 len = strlen(str);165 ret = malloc(len + 1);166 if (__builtin_expect(ret != NULL, 1))167 memcpy(ret, str, len + 1);168 169 return ret;170}171 172static __attribute__((unused))173char *strndup(const char *str, size_t maxlen)174{175 size_t len;176 char *ret;177 178 len = strnlen(str, maxlen);179 ret = malloc(len + 1);180 if (__builtin_expect(ret != NULL, 1)) {181 memcpy(ret, str, len);182 ret[len] = '\0';183 }184 185 return ret;186}187 188static __attribute__((unused))189size_t strlcat(char *dst, const char *src, size_t size)190{191 size_t len = strnlen(dst, size);192 193 /*194 * We want len < size-1. But as size is unsigned and can wrap195 * around, we use len + 1 instead.196 */197 while (len + 1 < size) {198 dst[len] = *src;199 if (*src == '\0')200 break;201 len++;202 src++;203 }204 205 if (len < size)206 dst[len] = '\0';207 208 while (*src++)209 len++;210 211 return len;212}213 214static __attribute__((unused))215size_t strlcpy(char *dst, const char *src, size_t size)216{217 size_t len;218 219 for (len = 0; len < size; len++) {220 dst[len] = src[len];221 if (!dst[len])222 return len;223 }224 if (size)225 dst[size-1] = '\0';226 227 while (src[len])228 len++;229 230 return len;231}232 233static __attribute__((unused))234char *strncat(char *dst, const char *src, size_t size)235{236 char *orig = dst;237 238 while (*dst)239 dst++;240 241 while (size && (*dst = *src)) {242 src++;243 dst++;244 size--;245 }246 247 *dst = 0;248 return orig;249}250 251static __attribute__((unused))252int strncmp(const char *a, const char *b, size_t size)253{254 unsigned int c;255 int diff = 0;256 257 while (size-- &&258 !(diff = (unsigned char)*a++ - (c = (unsigned char)*b++)) && c)259 ;260 261 return diff;262}263 264static __attribute__((unused))265char *strncpy(char *dst, const char *src, size_t size)266{267 size_t len;268 269 for (len = 0; len < size; len++)270 if ((dst[len] = *src))271 src++;272 return dst;273}274 275static __attribute__((unused))276char *strrchr(const char *s, int c)277{278 const char *ret = NULL;279 280 while (*s) {281 if (*s == (char)c)282 ret = s;283 s++;284 }285 return (char *)ret;286}287 288/* make sure to include all global symbols */289#include "nolibc.h"290 291#endif /* _NOLIBC_STRING_H */292