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