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 static __attribute__((unused)) 97 char *strchr(const char *s, int c) 98 { 99 while (*s) { 100 if (*s == (char)c) 101 return (char *)s; 102 s++; 103 } 104 return NULL; 105 } 106 107 static __attribute__((unused)) 108 int strcmp(const char *a, const char *b) 109 { 110 unsigned int c; 111 int diff; 112 113 while (!(diff = (unsigned char)*a++ - (c = (unsigned char)*b++)) && c) 114 ; 115 return diff; 116 } 117 118 static __attribute__((unused)) 119 char *strcpy(char *dst, const char *src) 120 { 121 char *ret = dst; 122 123 while ((*dst++ = *src++)); 124 return ret; 125 } 126 127 /* this function is only used with arguments that are not constants or when 128 * it's not known because optimizations are disabled. Note that gcc 12 129 * recognizes an strlen() pattern and replaces it with a jump to strlen(), 130 * thus itself, hence the asm() statement below that's meant to disable this 131 * confusing practice. 132 */ 133 size_t strlen(const char *str); 134 __attribute__((weak,unused,section(".text.nolibc_strlen"))) 135 size_t strlen(const char *str) 136 { 137 size_t len; 138 139 for (len = 0; str[len]; len++) 140 __asm__(""); 141 return len; 142 } 143 144 /* do not trust __builtin_constant_p() at -O0, as clang will emit a test and 145 * the two branches, then will rely on an external definition of strlen(). 146 */ 147 #if defined(__OPTIMIZE__) 148 #define nolibc_strlen(x) strlen(x) 149 #define strlen(str) ({ \ 150 __builtin_constant_p((str)) ? \ 151 __builtin_strlen((str)) : \ 152 nolibc_strlen((str)); \ 153 }) 154 #endif 155 156 static __attribute__((unused)) 157 size_t strnlen(const char *str, size_t maxlen) 158 { 159 size_t len; 160 161 for (len = 0; (len < maxlen) && str[len]; len++); 162 return len; 163 } 164 165 static __attribute__((unused)) 166 char *strdup(const char *str) 167 { 168 size_t len; 169 char *ret; 170 171 len = strlen(str); 172 ret = malloc(len + 1); 173 if (__builtin_expect(ret != NULL, 1)) 174 memcpy(ret, str, len + 1); 175 176 return ret; 177 } 178 179 static __attribute__((unused)) 180 char *strndup(const char *str, size_t maxlen) 181 { 182 size_t len; 183 char *ret; 184 185 len = strnlen(str, maxlen); 186 ret = malloc(len + 1); 187 if (__builtin_expect(ret != NULL, 1)) { 188 memcpy(ret, str, len); 189 ret[len] = '\0'; 190 } 191 192 return ret; 193 } 194 195 static __attribute__((unused)) 196 size_t strlcat(char *dst, const char *src, size_t size) 197 { 198 size_t len = strnlen(dst, size); 199 200 /* 201 * We want len < size-1. But as size is unsigned and can wrap 202 * around, we use len + 1 instead. 203 */ 204 while (len + 1 < size) { 205 dst[len] = *src; 206 if (*src == '\0') 207 break; 208 len++; 209 src++; 210 } 211 212 if (len < size) 213 dst[len] = '\0'; 214 215 while (*src++) 216 len++; 217 218 return len; 219 } 220 221 static __attribute__((unused)) 222 size_t strlcpy(char *dst, const char *src, size_t size) 223 { 224 size_t len; 225 226 for (len = 0; len < size; len++) { 227 dst[len] = src[len]; 228 if (!dst[len]) 229 return len; 230 } 231 if (size) 232 dst[size-1] = '\0'; 233 234 while (src[len]) 235 len++; 236 237 return len; 238 } 239 240 static __attribute__((unused)) 241 char *strncat(char *dst, const char *src, size_t size) 242 { 243 char *orig = dst; 244 245 while (*dst) 246 dst++; 247 248 while (size && (*dst = *src)) { 249 src++; 250 dst++; 251 size--; 252 } 253 254 *dst = 0; 255 return orig; 256 } 257 258 static __attribute__((unused)) 259 int strncmp(const char *a, const char *b, size_t size) 260 { 261 unsigned int c; 262 int diff = 0; 263 264 while (size-- && 265 !(diff = (unsigned char)*a++ - (c = (unsigned char)*b++)) && c) 266 ; 267 268 return diff; 269 } 270 271 static __attribute__((unused)) 272 char *strncpy(char *dst, const char *src, size_t size) 273 { 274 size_t len; 275 276 for (len = 0; len < size; len++) 277 if ((dst[len] = *src)) 278 src++; 279 return dst; 280 } 281 282 static __attribute__((unused)) 283 char *strrchr(const char *s, int c) 284 { 285 const char *ret = NULL; 286 287 while (*s) { 288 if (*s == (char)c) 289 ret = s; 290 s++; 291 } 292 return (char *)ret; 293 } 294 295 static __attribute__((unused)) 296 char *strstr(const char *haystack, const char *needle) 297 { 298 size_t len_haystack, len_needle; 299 300 len_needle = strlen(needle); 301 if (!len_needle) 302 return NULL; 303 304 len_haystack = strlen(haystack); 305 while (len_haystack >= len_needle) { 306 if (!memcmp(haystack, needle, len_needle)) 307 return (char *)haystack; 308 haystack++; 309 len_haystack--; 310 } 311 312 return NULL; 313 } 314 315 static __attribute__((unused)) 316 int tolower(int c) 317 { 318 if (c >= 'A' && c <= 'Z') 319 return c - 'A' + 'a'; 320 return c; 321 } 322 323 static __attribute__((unused)) 324 int toupper(int c) 325 { 326 if (c >= 'a' && c <= 'z') 327 return c - 'a' + 'A'; 328 return c; 329 } 330 331 #endif /* _NOLIBC_STRING_H */ 332