summaryrefslogtreecommitdiff
path: root/tools/include/nolibc/stdlib.h
diff options
context:
space:
mode:
Diffstat (limited to 'tools/include/nolibc/stdlib.h')
-rw-r--r--tools/include/nolibc/stdlib.h232
1 files changed, 111 insertions, 121 deletions
diff --git a/tools/include/nolibc/stdlib.h b/tools/include/nolibc/stdlib.h
index 75aa273c23a6..1816c2368b68 100644
--- a/tools/include/nolibc/stdlib.h
+++ b/tools/include/nolibc/stdlib.h
@@ -4,6 +4,9 @@
* Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu>
*/
+/* make sure to include all global symbols */
+#include "nolibc.h"
+
#ifndef _NOLIBC_STDLIB_H
#define _NOLIBC_STDLIB_H
@@ -29,11 +32,30 @@ static __attribute__((unused)) char itoa_buffer[21];
* As much as possible, please keep functions alphabetically sorted.
*/
+static __inline__
+int abs(int j)
+{
+ return j >= 0 ? j : -j;
+}
+
+static __inline__
+long labs(long j)
+{
+ return j >= 0 ? j : -j;
+}
+
+static __inline__
+long long llabs(long long j)
+{
+ return j >= 0 ? j : -j;
+}
+
/* must be exported, as it's used by libgcc for various divide functions */
+void abort(void);
__attribute__((weak,unused,noreturn,section(".text.nolibc_abort")))
void abort(void)
{
- sys_kill(sys_getpid(), SIGABRT);
+ _sys_kill(_sys_getpid(), SIGABRT);
for (;;);
}
@@ -78,6 +100,7 @@ void free(void *ptr)
munmap(heap, heap->len);
}
+#ifndef NOLIBC_NO_RUNTIME
/* getenv() tries to find the environment variable named <name> in the
* environment array pointed to by global variable "environ" which must be
* declared as a char **, and must be terminated by a NULL (it is recommended
@@ -100,32 +123,7 @@ char *getenv(const char *name)
}
return NULL;
}
-
-static __attribute__((unused))
-unsigned long getauxval(unsigned long type)
-{
- const unsigned long *auxv = _auxv;
- unsigned long ret;
-
- if (!auxv)
- return 0;
-
- while (1) {
- if (!auxv[0] && !auxv[1]) {
- ret = 0;
- break;
- }
-
- if (auxv[0] == type) {
- ret = auxv[1];
- break;
- }
-
- auxv += 2;
- }
-
- return ret;
-}
+#endif /* NOLIBC_NO_RUNTIME */
static __attribute__((unused))
void *malloc(size_t len)
@@ -147,9 +145,9 @@ void *malloc(size_t len)
static __attribute__((unused))
void *calloc(size_t size, size_t nmemb)
{
- size_t x = size * nmemb;
+ size_t x;
- if (__builtin_expect(size && ((x / size) != nmemb), 0)) {
+ if (__builtin_expect(__builtin_mul_overflow(size, nmemb, &x), 0)) {
SET_ERRNO(ENOMEM);
return NULL;
}
@@ -190,36 +188,91 @@ void *realloc(void *old_ptr, size_t new_size)
return ret;
}
-/* Converts the unsigned long integer <in> to its hex representation into
+/* Converts the unsigned 64bit integer <in> to base <base> ascii into
* buffer <buffer>, which must be long enough to store the number and the
- * trailing zero (17 bytes for "ffffffffffffffff" or 9 for "ffffffff"). The
- * buffer is filled from the first byte, and the number of characters emitted
- * (not counting the trailing zero) is returned. The function is constructed
- * in a way to optimize the code size and avoid any divide that could add a
- * dependency on large external functions.
+ * trailing zero. The buffer is filled from the first byte, and the number
+ * of characters emitted (not counting the trailing zero) is returned.
+ * The function uses 'multiply by reciprocal' for the divisions and
+ * requires the caller pass the correct reciprocal.
+ *
+ * Note that unlike __div64_const32() in asm-generic/div64.h there isn't
+ * an extra shift done (by ___p), the reciprocal has to be lower resulting
+ * in a slightly low quotient.
+ * Keep things simple by correcting for the error.
+ * This also saves calculating the 'low * low' product (e2 below) which is
+ * very unlikely to be significant.
+ *
+ * Some maths:
+ * recip = p2 / base - e1; // With e1 < base.
+ * q = (recip * in - e2) / p2; // With e2 < p2.
+ * = base / in - (e1 * in + e2) / p2;
+ * > base / in - (e1 * p2 + p2) / p2;
+ * = base / in - ((e1 + 1) * p2) / p2;
+ * > base / in - base;
+ * So the maximum error is less than 'base'.
+ * Hence the largest possible digit is '2 * base - 1'.
+ * For base 10 e1 is 6 and you can get digits of 15 (eg from 2**64-1).
+ * Error e1 is largest for a base that is a factor of 2**64+1, the smallest is 274177
+ * and converting 2**42-1 in base 274177 does generate a digit of 274177+274175.
+ * This all means only a single correction is needed rather than a loop.
+ *
+ * __int128 isn't used for mips because gcc prior to 10.0 will call
+ * __multi3 for MIPS64r6. The same also happens for SPARC and clang.
*/
-static __attribute__((unused))
-int utoh_r(unsigned long in, char *buffer)
+#define _NOLIBC_U64TOA_RECIP(base) ((base) & 1 ? ~0ull / (base) : (1ull << 63) / ((base) / 2))
+static __attribute__((unused, noinline))
+int _nolibc_u64toa_base(uint64_t in, char *buffer, unsigned int base, uint64_t recip)
{
- signed char pos = (~0UL > 0xfffffffful) ? 60 : 28;
- int digits = 0;
- int dig;
+ unsigned int digits = 0;
+ unsigned int dig;
+ uint64_t q;
+ char *p;
+ /* Generate least significant digit first */
do {
- dig = in >> pos;
- in -= (uint64_t)dig << pos;
- pos -= 4;
- if (dig || digits || pos < 0) {
- if (dig > 9)
- dig += 'a' - '0' - 10;
- buffer[digits++] = '0' + dig;
+#if defined(__SIZEOF_INT128__) && !defined(__mips__) && !defined(__sparc__)
+ q = ((unsigned __int128)in * recip) >> 64;
+#else
+ uint64_t p = (uint32_t)in * (recip >> 32);
+ q = (in >> 32) * (recip >> 32) + (p >> 32);
+ p = (uint32_t)p + (in >> 32) * (uint32_t)recip;
+ q += p >> 32;
+#endif
+ dig = in - q * base;
+ /* Correct for any rounding errors */
+ if (dig >= base) {
+ dig -= base;
+ q++;
}
- } while (pos >= 0);
+ if (dig > 9)
+ dig += 'a' - '0' - 10;
+ buffer[digits++] = '0' + dig;
+ } while ((in = q));
buffer[digits] = 0;
+
+ /* Order reverse to result */
+ for (p = buffer + digits - 1; p > buffer; buffer++, p--) {
+ dig = *buffer;
+ *buffer = *p;
+ *p = dig;
+ }
+
return digits;
}
+/* Converts the unsigned long integer <in> to its hex representation into
+ * buffer <buffer>, which must be long enough to store the number and the
+ * trailing zero (17 bytes for "ffffffffffffffff" or 9 for "ffffffff"). The
+ * buffer is filled from the first byte, and the number of characters emitted
+ * (not counting the trailing zero) is returned.
+ */
+static __inline__ __attribute__((unused))
+int utoh_r(unsigned long in, char *buffer)
+{
+ return _nolibc_u64toa_base(in, buffer, 16, _NOLIBC_U64TOA_RECIP(16));
+}
+
/* converts unsigned long <in> to an hex string using the static itoa_buffer
* and returns the pointer to that string.
*/
@@ -235,30 +288,11 @@ char *utoh(unsigned long in)
* trailing zero (21 bytes for 18446744073709551615 in 64-bit, 11 for
* 4294967295 in 32-bit). The buffer is filled from the first byte, and the
* number of characters emitted (not counting the trailing zero) is returned.
- * The function is constructed in a way to optimize the code size and avoid
- * any divide that could add a dependency on large external functions.
*/
-static __attribute__((unused))
+static __inline__ __attribute__((unused))
int utoa_r(unsigned long in, char *buffer)
{
- unsigned long lim;
- int digits = 0;
- int pos = (~0UL > 0xfffffffful) ? 19 : 9;
- int dig;
-
- do {
- for (dig = 0, lim = 1; dig < pos; dig++)
- lim *= 10;
-
- if (digits || in >= lim || !pos) {
- for (dig = 0; in >= lim; dig++)
- in -= lim;
- buffer[digits++] = '0' + dig;
- }
- } while (pos--);
-
- buffer[digits] = 0;
- return digits;
+ return _nolibc_u64toa_base(in, buffer, 10, _NOLIBC_U64TOA_RECIP(10));
}
/* Converts the signed long integer <in> to its string representation into
@@ -274,7 +308,7 @@ int itoa_r(long in, char *buffer)
int len = 0;
if (in < 0) {
- in = -in;
+ in = -(unsigned long)in;
*(ptr++) = '-';
len++;
}
@@ -326,34 +360,12 @@ char *utoa(unsigned long in)
* buffer <buffer>, which must be long enough to store the number and the
* trailing zero (17 bytes for "ffffffffffffffff"). The buffer is filled from
* the first byte, and the number of characters emitted (not counting the
- * trailing zero) is returned. The function is constructed in a way to optimize
- * the code size and avoid any divide that could add a dependency on large
- * external functions.
+ * trailing zero) is returned.
*/
-static __attribute__((unused))
+static __inline__ __attribute__((unused))
int u64toh_r(uint64_t in, char *buffer)
{
- signed char pos = 60;
- int digits = 0;
- int dig;
-
- do {
- if (sizeof(long) >= 8) {
- dig = (in >> pos) & 0xF;
- } else {
- /* 32-bit platforms: avoid a 64-bit shift */
- uint32_t d = (pos >= 32) ? (in >> 32) : in;
- dig = (d >> (pos & 31)) & 0xF;
- }
- if (dig > 9)
- dig += 'a' - '0' - 10;
- pos -= 4;
- if (dig || digits || pos < 0)
- buffer[digits++] = '0' + dig;
- } while (pos >= 0);
-
- buffer[digits] = 0;
- return digits;
+ return _nolibc_u64toa_base(in, buffer, 16, _NOLIBC_U64TOA_RECIP(16));
}
/* converts uint64_t <in> to an hex string using the static itoa_buffer and
@@ -370,31 +382,12 @@ char *u64toh(uint64_t in)
* buffer <buffer>, which must be long enough to store the number and the
* trailing zero (21 bytes for 18446744073709551615). The buffer is filled from
* the first byte, and the number of characters emitted (not counting the
- * trailing zero) is returned. The function is constructed in a way to optimize
- * the code size and avoid any divide that could add a dependency on large
- * external functions.
+ * trailing zero) is returned.
*/
-static __attribute__((unused))
+static __inline__ __attribute__((unused))
int u64toa_r(uint64_t in, char *buffer)
{
- unsigned long long lim;
- int digits = 0;
- int pos = 19; /* start with the highest possible digit */
- int dig;
-
- do {
- for (dig = 0, lim = 1; dig < pos; dig++)
- lim *= 10;
-
- if (digits || in >= lim || !pos) {
- for (dig = 0; in >= lim; dig++)
- in -= lim;
- buffer[digits++] = '0' + dig;
- }
- } while (pos--);
-
- buffer[digits] = 0;
- return digits;
+ return _nolibc_u64toa_base(in, buffer, 10, _NOLIBC_U64TOA_RECIP(10));
}
/* Converts the signed 64-bit integer <in> to its string representation into
@@ -410,7 +403,7 @@ int i64toa_r(int64_t in, char *buffer)
int len = 0;
if (in < 0) {
- in = -in;
+ in = -(uint64_t)in;
*(ptr++) = '-';
len++;
}
@@ -547,7 +540,4 @@ uintmax_t strtoumax(const char *nptr, char **endptr, int base)
return __strtox(nptr, endptr, base, 0, UINTMAX_MAX);
}
-/* make sure to include all global symbols */
-#include "nolibc.h"
-
#endif /* _NOLIBC_STDLIB_H */