| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- /* Licensed under Apache License v2.0 - see LICENSE file for details */
- #ifndef CCAN_ALTSTACK_H
- #define CCAN_ALTSTACK_H
- #include "config.h"
- #if ! __x86_64__
- #error "This code expects the AMD64 ABI, but __x86_64__ is false."
- #endif
- #include <stddef.h>
- #include <sys/resource.h>
- #define ALTSTACK_ERR_MAXLEN 128
- /**
- * altstack - run a function with a dedicated stack, and then release the memory
- * @max: the maximum size of the new stack
- * @fn: a function to run
- * @arg: an argument passed to fn
- * @out: where to store the return of fn, optional
- *
- * rlimit is set to @max, and an anonymous noreserve mapping is made.
- * A jump buffer is setup and a signal handler established for SIGSEGV.
- * The rsp register is set to the mapped address, with the old rsp value
- * pushed onto the new stack. The provided @fn is called, with @arg as
- * its only argument, from non-stack addresses. Once @fn returns,
- * rsp is popped off the stack. If @out is non-null, it gets the return
- * value from @fn. The region is unmapped and the other changes undone.
- *
- * Error messages are appended to a buffer available via altstack_geterr()
- * and altstack_perror(). errno is set by the failing call, or set to
- * EOVERFLOW in case SIGSEGV is caught.
- *
- * altstack() uses thread-local storage, and should not be nested.
- *
- * Example:
- * // permit recursion depth over a million
- * // a contrived example! (-O2 replaces the recursion with a loop)
- * #include <assert.h>
- * #include <stdio.h>
- * #include <stdlib.h>
- * #include <ccan/altstack/altstack.h>
- *
- * unsigned depth;
- *
- * static void dn(unsigned long i)
- * {
- * depth++;
- * if (i) dn(--i);
- * }
- *
- * static void *wrap(void *i)
- * {
- * dn((unsigned long) i);
- * return 0;
- * }
- *
- * #define MiB (1024UL*1024UL)
- * int main(int argc, char *argv[])
- * {
- * unsigned long n;
- * assert(argc == 2);
- * n = strtoul(argv[1], 0, 0);
- *
- * if (altstack(32*MiB, wrap, (void *) n, 0) != 0)
- * altstack_perror();
- *
- * printf("%d\n", depth);
- *
- * return 0;
- * }
- *
- * Returns: -1 on error; 0 on success; 1 on error after @fn returns
- */
- int altstack(rlim_t max, void *(*fn)(void *), void *arg, void **out);
- /**
- * altstack_perror - print error messages to stderr
- */
- void altstack_perror(void);
- /**
- * altstack_geterr - return the error buffer
- *
- * The error buffer is static thread-local storage.
- * The buffer is reset with each altstack() call.
- *
- * Returns: pointer to the error buffer
- */
- char *altstack_geterr(void);
- /**
- * altstack_used - return amount of stack used
- *
- * This captures the current rsp value and returns
- * the difference from the initial rsp value.
- *
- * Note: this can be used with any stack, including the original.
- * When using with a non-altstack stack, call altstack_rsp_save()
- * as early as possible to establish the initial value.
- *
- * Returns: difference of rsp values
- */
- ptrdiff_t altstack_used(void);
- /**
- * altstack_max - return usable stack size
- *
- * Returns: max value from altstack() call
- */
- rlim_t altstack_max(void);
- /**
- * altstack_remn - return amount of stack remaining
- *
- * Returns: altstack_max() minus altstack_used()
- */
- #define altstack_remn() (altstack_max() - altstack_used())
- /**
- * altstack_rsp_save - set initial rsp value
- *
- * Capture the current value of rsp for future altstack_used()
- * calculations. altstack() also saves the initial rsp, so
- * this should only be used in non-altstack contexts.
- */
- void altstack_rsp_save(void);
- #endif
|