This repository has been archived by the owner on Jan 20, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 261
/
Copy pathshim_internal.h
381 lines (324 loc) · 12 KB
/
shim_internal.h
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
/* SPDX-License-Identifier: LGPL-3.0-or-later */
/* Copyright (C) 2014 Stony Brook University */
#ifndef _SHIM_INTERNAL_H_
#define _SHIM_INTERNAL_H_
#include <stdbool.h>
#include <stdnoreturn.h>
#include "api.h"
#include "assert.h"
#include "atomic.h"
#include "shim_defs.h"
#include "shim_internal-arch.h"
#include "shim_tcb.h"
#include "shim_types.h"
void* shim_init(int argc, void* args);
/* important macros and static inline functions */
#define PAL_NATIVE_ERRNO() SHIM_TCB_GET(pal_errno)
#define INTERNAL_TID_BASE ((IDTYPE)1 << (sizeof(IDTYPE) * 8 - 1))
static inline bool is_internal_tid(unsigned int tid) {
return tid >= INTERNAL_TID_BASE;
}
struct debug_buf {
int start;
int end;
char buf[DEBUGBUF_SIZE];
};
#include "pal.h"
#include "pal_debug.h"
#include "pal_error.h"
extern bool g_debug_log_enabled;
#include <stdarg.h>
void debug_printf(const char* fmt, ...) __attribute__((format(printf, 1, 2)));
void debug_puts(const char* str);
void debug_putch(int ch);
void debug_vprintf(const char* fmt, va_list ap) __attribute__((format(printf, 1, 0)));
#define debug(fmt, ...) \
do { \
if (g_debug_log_enabled) \
debug_printf(fmt, ##__VA_ARGS__); \
} while (0)
#if 0
#define DEBUG_BREAK_ON_FAILURE() DEBUG_BREAK()
#else
#define DEBUG_BREAK_ON_FAILURE() do {} while (0)
#endif
#define BUG() \
do { \
warn("BUG() " __FILE__ ":%d\n", __LINE__); \
DEBUG_BREAK_ON_FAILURE(); \
/* Crash the process. */ \
CRASH_PROCESS(); \
} while (0)
#define DEBUG_HERE() \
do { \
debug("%s (" __FILE__ ":%d)\n", __func__, __LINE__); \
} while (0)
/*!
* \brief LibOS syscall emulation entrypoint.
*
* Actual implementation and ABI are architecture specific, but generally should dump the CPU
* context and call `shim_do_syscall`.
*/
void syscalldb(void);
/*!
* \brief High-level syscall emulation entrypoint.
*
* \param context CPU context at syscall entry.
*
* Emulates the syscall given the entry \p context.
*/
noreturn void shim_do_syscall(PAL_CONTEXT* context);
/*!
* \brief Restore the CPU context.
*
* \param context CPU context to restore.
*
* This function restores the given \p context. It is only called on returning from a syscall, so
* it does not need to be reentrant/atomic (there is no such thing as nested syscalls), but it
* cannot assume that the CPU context is the same as at the entry to the syscall (e.g. sigreturn,
* or signal handling may change it).
*/
noreturn void return_from_syscall(PAL_CONTEXT* context);
/*!
* \brief Restore the context after clone/fork.
*
* \param context LibOS context to restore.
*
* Restores LibOS \p context after a successful clone or fork.
*/
noreturn void restore_child_context_after_clone(struct shim_context* context);
/*!
* \brief Creates a signal frame
*
* \param context CPU context
* \param siginfo signal to be delivered
* \param handler pointer to the user app signal handler
* \param restorer pointer to the restorer function
* \param use_altstack `true` if alternative stack can be used
* \param old_mask old signal mask (to be stored in the signal frame)
*
* Creates a signal frame on the user app stack (either normal or alternative stack, depending on
* \p use_altstack and the currently used stack). Arranges \p context so that restoring it jumps
* to \p handler with appropriate arguments and returning from \p handler will jump to \p restorer,
* (which usually just calls `sigreturn` syscall). On most (all?) architectures old \p context,
* \p siginfo and \p old_mask are saved into the signal frame.
*/
void prepare_sigframe(PAL_CONTEXT* context, siginfo_t* siginfo, uint64_t handler,
uint64_t restorer, bool use_altstack, __sigset_t* old_mask);
/*!
* \brief Restart a syscall
*
* \param context CPU context
* \param syscall_nr syscall number
*
* Arranges \p context so that upon return to it redoes the \p syscall_nr syscall.
*/
void restart_syscall(PAL_CONTEXT* context, uint64_t syscall_nr);
/*!
* \brief Restores a sigreturn context
*
* \param context original CPU context
* \param new_mask (OUT) new signal mask
*
* Restores cpu context in a architecure specific way. On entry to this funciton \p context holds
* initial CPU context and this function extracts signal frame (generated by `prepare_sigframe`)
* and restores it into \p context. The new signal mask (to be set) is written into \p new_mask.
*/
void restore_sigreturn_context(PAL_CONTEXT* context, __sigset_t* new_mask);
/*!
* \brief Emulate a syscall
*
* \param context CPU context
*
* If the current instruction pointer in \p context points to a syscall instruction, arrange
* \p context so that it is emulated.
* Returns `true` if it was a syscall instruction (hence it was emulated).
* Note that this function merely changes context, so the actual emulation is done upon returning
* to that context.
* Used e.g. in Linux-SGX Pal to handle `syscall` instruction.
*/
bool maybe_emulate_syscall(PAL_CONTEXT* context);
/*!
* \brief Handle a signal
*
* \param context CPU context
* \param old_mask_ptr pointer to the old signal mask
*
* If there is a signal to be handled, this function arranges its delivery using `prepare_sigframe`.
* If \p old_mask_ptr is not `NULL`, it is stored into the signal frame, otherwise the current
* signal mask is used.
* Returns `true` if a not-ignored signal was handled (hence \p context wasa changed), `false`
* otherwise.
*/
bool handle_signal(PAL_CONTEXT* context, __sigset_t* old_mask_ptr);
long convert_pal_errno(long err);
#define PAL_ERRNO() convert_pal_errno(PAL_NATIVE_ERRNO())
void debug_print_syscall_before(int sysno, ...);
void debug_print_syscall_after(int sysno, ...);
#define PAL_CB(member) (pal_control.member)
/*
* These events have counting semaphore semantics:
* - `set_event(e, n)` increases value of the semaphore by `n`,
* - `wait_event(e)` decreases value by 1 (blocking if it's 0),
* - `clear_event(e)` decreases value to 0, without blocking - this operation is not atomic.
* Note that using `clear_event` probably requires external locking to avoid races.
*/
static inline int create_event(AEVENTTYPE* e) {
e->event = DkStreamOpen(URI_PREFIX_PIPE, PAL_ACCESS_RDWR, 0, 0, 0);
if (!e->event) {
return -PAL_ERRNO();
}
return 0;
}
static inline PAL_HANDLE event_handle(AEVENTTYPE* e) {
return e->event;
}
static inline void destroy_event(AEVENTTYPE* e) {
if (e->event) {
DkObjectClose(e->event);
e->event = NULL;
}
}
static inline int set_event(AEVENTTYPE* e, size_t n) {
/* TODO: this should be changed into an assert, once we make sure it does not happen (old
* version handled it). */
if (!e->event) {
return -EINVAL;
}
char bytes[n];
memset(bytes, '\0', n);
while (n > 0) {
PAL_NUM ret = DkStreamWrite(e->event, 0, n, bytes, NULL);
if (ret == PAL_STREAM_ERROR) {
int err = PAL_ERRNO();
if (err == EINTR || err == EAGAIN || err == EWOULDBLOCK) {
continue;
}
return -err;
}
n -= ret;
}
return 0;
}
static inline int wait_event(AEVENTTYPE* e) {
/* TODO: this should be changed into an assert, once we make sure it does not happen (old
* version handled it). */
if (!e->event) {
return -EINVAL;
}
int err = 0;
do {
char byte;
PAL_NUM ret = DkStreamRead(e->event, 0, 1, &byte, NULL, 0);
err = ret == PAL_STREAM_ERROR ? PAL_ERRNO() : 0;
} while (err == EINTR || err == EAGAIN || err == EWOULDBLOCK);
return -err;
}
static inline int clear_event(AEVENTTYPE* e) {
/* TODO: this should be changed into an assert, once we make sure it does not happen (old
* version handled it). */
if (!e->event) {
return -EINVAL;
}
while (1) {
PAL_HANDLE handle = e->event;
PAL_FLG ievent = PAL_WAIT_READ;
PAL_FLG revent = 0;
shim_get_tcb()->pal_errno = PAL_ERROR_SUCCESS;
PAL_BOL ret = DkStreamsWaitEvents(1, &handle, &ievent, &revent, /*timeout=*/0);
if (!ret) {
int err = PAL_ERRNO();
if (err == EINTR) {
continue;
} else if (!err || err == EAGAIN || err == EWOULDBLOCK) {
break;
}
return -err;
}
/* Even if `revent` has `PAL_WAIT_ERROR` marked, let `DkSitreamRead()` report the error
* below. */
assert(revent);
char bytes[100];
PAL_NUM n = DkStreamRead(e->event, 0, sizeof(bytes), bytes, NULL, 0);
if (n == PAL_STREAM_ERROR) {
int err = PAL_ERRNO();
if (err == EINTR) {
continue;
} else if (err == EAGAIN || err == EWOULDBLOCK) {
/* This should not happen, since we polled above ... */
break;
}
return -err;
}
}
return 0;
}
/* reference counter APIs */
#define REF_GET(ref) __atomic_load_n(&(ref).counter, __ATOMIC_SEQ_CST)
#define REF_SET(ref, count) __atomic_store_n(&(ref).counter, count, __ATOMIC_SEQ_CST);
static inline int __ref_inc(REFTYPE* ref) {
int64_t _c;
do {
_c = __atomic_load_n(&ref->counter, __ATOMIC_SEQ_CST);
assert(_c >= 0);
} while (!__atomic_compare_exchange_n(&ref->counter, &_c, _c + 1, /*weak=*/false,
__ATOMIC_SEQ_CST, __ATOMIC_RELAXED));
return _c + 1;
}
#define REF_INC(ref) __ref_inc(&(ref))
static inline int __ref_dec(REFTYPE* ref) {
int64_t _c;
do {
_c = __atomic_load_n(&ref->counter, __ATOMIC_SEQ_CST);
if (!_c) {
debug("Fail: Trying to drop reference count below 0\n");
BUG();
return 0;
}
} while (!__atomic_compare_exchange_n(&ref->counter, &_c, _c - 1, /*weak=*/false,
__ATOMIC_SEQ_CST, __ATOMIC_RELAXED));
return _c - 1;
}
#define REF_DEC(ref) __ref_dec(&(ref))
#ifndef __alloca
#define __alloca __builtin_alloca
#endif
extern size_t g_pal_alloc_align;
#define ALLOC_ALIGNMENT g_pal_alloc_align
#define IS_ALLOC_ALIGNED(x) IS_ALIGNED_POW2(x, g_pal_alloc_align)
#define IS_ALLOC_ALIGNED_PTR(x) IS_ALIGNED_PTR_POW2(x, g_pal_alloc_align)
#define ALLOC_ALIGN_DOWN(x) ALIGN_DOWN_POW2(x, g_pal_alloc_align)
#define ALLOC_ALIGN_UP(x) ALIGN_UP_POW2(x, g_pal_alloc_align)
#define ALLOC_ALIGN_DOWN_PTR(x) ALIGN_DOWN_PTR_POW2(x, g_pal_alloc_align)
#define ALLOC_ALIGN_UP_PTR(x) ALIGN_UP_PTR_POW2(x, g_pal_alloc_align)
void* __system_malloc(size_t size);
void __system_free(void* addr, size_t size);
#define system_malloc __system_malloc
#define system_free __system_free
extern void* migrated_memory_start;
extern void* migrated_memory_end;
static inline bool memory_migrated(void* mem) {
return mem >= migrated_memory_start && mem < migrated_memory_end;
}
extern void* __load_address;
extern void* __load_address_end;
extern void* __code_address;
extern void* __code_address_end;
extern const char** migrated_envp;
struct shim_handle;
int init_brk_from_executable(struct shim_handle* exec);
int init_brk_region(void* brk_region, size_t data_segment_size);
void reset_brk(void);
int init_internal_map(void);
int init_loader(void);
int init_rlimit(void);
bool test_user_memory(void* addr, size_t size, bool write);
bool test_user_string(const char* addr);
uint64_t get_rlimit_cur(int resource);
void set_rlimit_cur(int resource, uint64_t rlim);
int object_wait_with_retry(PAL_HANDLE handle);
void _update_epolls(struct shim_handle* handle);
void delete_from_epoll_handles(struct shim_handle* handle);
void* allocate_stack(size_t size, size_t protect_size, bool user);
int init_stack(const char** argv, const char** envp, const char*** out_argp, elf_auxv_t** out_auxv);
#endif /* _SHIM_INTERNAL_H_ */