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