]> git.ozlabs.org Git - ccan/blob - ccan/altstack/altstack.h
5570e7b53031ad1716729a2596a31b90eb29ae53
[ccan] / ccan / altstack / altstack.h
1 /* Licensed under Apache License v2.0 - see LICENSE file for details */
2 #ifndef CCAN_ALTSTACK_H
3 #define CCAN_ALTSTACK_H
4 #include "config.h"
5
6 #if ! __x86_64__
7 #error "This code expects the AMD64 ABI, but __x86_64__ is false."
8 #endif
9
10 #include <stddef.h>
11 #include <sys/resource.h>
12
13 #define ALTSTACK_ERR_MAXLEN 128
14
15 /**
16  * altstack - run a function with a dedicated stack, and then release the memory
17  * @max: the maximum size of the new stack
18  * @fn: a function to run
19  * @arg: an argument passed to fn
20  * @out: where to store the return of fn, optional
21  *
22  * rlimit is set to @max, and an anonymous noreserve mapping is made.
23  * A jump buffer is setup and a signal handler established for SIGSEGV.
24  * The rsp register is set to the mapped address, with the old rsp value
25  * pushed onto the new stack. The provided @fn is called, with @arg as
26  * its only argument, from non-stack addresses. Once @fn returns,
27  * rsp is popped off the stack. If @out is non-null, it gets the return
28  * value from @fn. The region is unmapped and the other changes undone.
29  *
30  * Error messages are appended to a buffer available via altstack_geterr()
31  * and altstack_perror(). errno is set by the failing call, or set to
32  * EOVERFLOW in case SIGSEGV is caught.
33  *
34  * altstack() uses thread-local storage, and should not be nested.
35  *
36  * Example:
37  *      // permit recursion depth over a million
38  *      // a contrived example! (-O2 replaces the recursion with a loop)
39  *      #include <assert.h>
40  *      #include <stdio.h>
41  *      #include <stdlib.h>
42  *      #include <ccan/altstack/altstack.h>
43  *
44  *      unsigned depth;
45  *
46  *      static void dn(unsigned long i)
47  *      {
48  *              depth++;
49  *              if (i) dn(--i);
50  *      }
51  *
52  *      static void *wrap(void *i)
53  *      {
54  *              dn((unsigned long) i);
55  *              return 0;
56  *      }
57  *
58  *      #define MiB (1024UL*1024UL)
59  *      int main(int argc, char *argv[])
60  *      {
61  *              unsigned long n;
62  *              assert(argc == 2);
63  *              n = strtoul(argv[1], 0, 0);
64  *
65  *              if (altstack(32*MiB, wrap, (void *) n, 0) != 0)
66  *                      altstack_perror();
67  *
68  *              printf("%d\n", depth);
69  *
70  *              return 0;
71  *      }
72  *
73  * Returns: -1 on error; 0 on success; 1 on error after @fn returns
74  */
75 int altstack(rlim_t max, void *(*fn)(void *), void *arg, void **out);
76
77 /**
78  * altstack_perror - print error messages to stderr
79  */
80 void altstack_perror(void);
81
82 /**
83  * altstack_geterr - return the error buffer
84  *
85  * The error buffer is static thread-local storage.
86  * The buffer is reset with each altstack() call.
87  *
88  * Returns: pointer to the error buffer
89  */
90 char *altstack_geterr(void);
91
92 /**
93  * altstack_used - return amount of stack used
94  *
95  * This captures the current rsp value and returns
96  * the difference from the initial rsp value.
97  *
98  * Note: this can be used with any stack, including the original.
99  * When using with a non-altstack stack, call altstack_rsp_save()
100  * as early as possible to establish the initial value.
101  *
102  * Returns: difference of rsp values
103  */
104 ptrdiff_t altstack_used(void);
105
106 /**
107  * altstack_rsp_save - set initial rsp value
108  *
109  * Capture the current value of rsp for future altstack_used()
110  * calculations. altstack() also saves the initial rsp, so
111  * this should only be used in non-altstack contexts.
112  */
113 void altstack_rsp_save(void);
114 #endif