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