-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathstackman_switch.h
More file actions
136 lines (119 loc) · 5 KB
/
stackman_switch.h
File metadata and controls
136 lines (119 loc) · 5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#pragma once
#ifndef STACKMAN_SWITCH_H
#define STACKMAN_SWITCH_H
/* Must include the platform here to know if the switch function
* is implemented in assembler or nt
*/
#include "platforms/platform.h"
/* we can request that C functions be implemented with static linkage
* when including in other libraries
*/
#if defined(STACKMAN_LINKAGE_STATIC)
#if !defined(STACKMAN_ASSEMBLY_SRC) && !STACKMAN_SWITCH_C
#define STACKMAN_LINKAGE_SWITCH static
#endif
#endif
#if !defined(STACKMAN_LINKAGE_SWITCH)
/* then it is extern */
#if defined(__cplusplus)
#define STACKMAN_LINKAGE_SWITCH extern "C"
#else
#define STACKMAN_LINKAGE_SWITCH extern
#endif
#endif
/* do we need to have indirection for stackman_switch?
* we do, if the implementation is inline and we are not
* building a library, because an optimizing compiler may
* decide to inline functions that contain in-line assembler
*/
#if (!defined(STACKMAN_ASSEMBLY_SRC) && !defined(STACKMAN_BUILD_LIB))
#define STACKMAN_SWITCH_NEED_INDIRECT 1
#else
#define STACKMAN_SWITCH_NEED_INDIRECT 0
#endif
#if STACKMAN_SWITCH_NEED_INDIRECT
#define STACKMAN_SWITCH_INASM_NAME _stackman_switch_inasm
#define STACKMAN_LINKAGE_SWITCH_INASM static
#else
#define STACKMAN_SWITCH_INASM_NAME stackman_switch
#define STACKMAN_LINKAGE_SWITCH_INASM STACKMAN_LINKAGE_SWITCH
#endif
/* raw stack switching function. The caller supplies a callback
* and context to perform determine the new stack pointer and
* perform cleanup of old stack and initialization of the new
* stack.
* The platform specific implementation of the function is responsible
* for saving and restoring any machine registers that are assumed
* to be unchanged across function calls and to change the actual
* stack pointer.
*
* The implementation must simply:
* 1) store all platform and cpu state on the stack (callee-stored
* registers, exception state, etc)
* 2) call the callback with the context, opcode STACKMAN_OP_SAVE and
* the current stack pointer. This allows the application to do additional
* cleanup and determine the new stack pointer.
* 3) replace the stack pointer with the returned value from the callback.
* 4) call the callback again with opcode STACKMAN_OP_RESTORE and the new
* stack pointer. This allows the application to initialize its state
after the switch.
* 5) Pop the platform state back from stack.
* 6) return the result from the second call of the callback.
*
* Note that the callback and context are _not_ guaranteed
* to be stack local. That is, even if the stack pointer is modified
* according to the first call to the callback, the same callback and
* context may be used during the second callback, in the new stack.
* Therefore, the same callback and context pointers should always be used
* when switching between stacks. This
* restriction could be lifted by pushing both values onto the stack
* before switching but the added complexity of platform implementations
* doesn't seem to warrant that for such an exotic use case.
*
* an appropriate implementation is included by
* platforms/platform.h
* A template implementation is available in
* platforms/switch_template.h
*/
/* Opcodes for the callback function */
typedef enum stackman_op_t {
/* The callback receives the current stack pointer and should
* save any state that it wishes, for example the contents of the
* stack, before returning a new stack pointer.
*/
STACKMAN_OP_SAVE = 0,
/* The callback receives the new stack pointer and should restore
* any state for it, e.g. fillint the stack with the correct data.
* what it returns will be the return value from stackman_switch().
*/
STACKMAN_OP_RESTORE = 1,
/* The callback is being invoked from a stackman_call() invokation.
* Stack_pointer is the pointer that was provided to stackman_call()
*/
STACKMAN_OP_CALL = 2,
} stackman_op_t;
/* The switch callback. It receives the context passed to
* stackman_switch(), an opcode (above) and a stack pointer.
* The opcode is passed as an int, to avoid portability problems
* with the ambiguous size of an enum.
*/
typedef void *(*stackman_cb_t)(
void *context, int opcode, void *stack_pointer);
/* The actual stack switching function.
* It saves state, switches stack pointer, and restores state
*/
STACKMAN_LINKAGE_SWITCH
void *stackman_switch(stackman_cb_t callback, void *context);
/* A function to call a function with a different stack location.
* Instead of a normal function call, where the return address and arguments
* are pushed on the current stack, first the stack pointer is changed to
* a new location. This is useful for example to move the stack into heap memory.
* 'callback' will be invoked with the
* stack already set to the new position as specified by 'stack'. The third
* argument to the callback is the *previous* stack pointer.
* The callback can then proceed to 'save' the new stack for future switching.
* Returning from the callback will work normally.
*/
STACKMAN_LINKAGE_SWITCH
void *stackman_call(stackman_cb_t callback, void *context, void *stack);
#endif /* STACKMAN_SWITCH_H */