forked from checkpoint-restore/criu
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathvdso-compat.c
74 lines (64 loc) · 2.3 KB
/
vdso-compat.c
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
#include <sys/syscall.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include "types.h"
#include "parasite-syscall.h"
#include "parasite.h"
#include "vdso.h"
static void exit_on(int ret, int err_fd, char *reason)
{
if (ret) {
syscall(__NR_write, err_fd, reason, strlen(reason));
syscall(__NR_exit, ret);
}
}
/*
* Because of restrictions of ARCH_MAP_VDSO_* API, new vDSO blob
* can be mapped only if there is no vDSO blob present for a process.
* This is a helper process, it unmaps 64-bit vDSO and maps 32-bit vDSO.
* Then it copies vDSO blob to shared with CRIU mapping.
*
* The purpose is to fill compat vdso's symtable (vdso_compat_rt).
* It's an optimization to fill symtable only once at CRIU restore
* for all restored tasks.
*
* @native - 64-bit vDSO blob (for easy unmap)
* @pipe_fd - to get size of compat blob from /proc/.../maps
* @err_fd - to print error messages
* @vdso_buf, buf_size - shared with CRIU buffer
*
* WARN: This helper shouldn't call pr_err() or any syscall with
* Glibc's wrapper function - it may very likely blow up.
*/
void compat_vdso_helper(struct vdso_maps *native, int pipe_fd,
int err_fd, void *vdso_buf, size_t buf_size)
{
void *vdso_addr;
long vdso_size;
long ret;
if (native->vdso_start != VDSO_BAD_ADDR) {
ret = syscall(__NR_munmap,
native->vdso_start, native->sym.vdso_size);
exit_on(ret, err_fd, "Error: Failed to unmap native vdso\n");
}
if (native->vvar_start != VVAR_BAD_ADDR) {
ret = syscall(__NR_munmap,
native->vvar_start, native->sym.vvar_size);
exit_on(ret, err_fd, "Error: Failed to unmap native vvar\n");
}
ret = syscall(__NR_arch_prctl, ARCH_MAP_VDSO_32, native->vdso_start);
if (ret < 0)
exit_on(ret, err_fd, "Error: ARCH_MAP_VDSO failed\n");
vdso_size = ret;
if (vdso_size > buf_size)
exit_on(-1, err_fd, "Error: Compatible vdso's size is bigger than reserved buf\n");
/* Stop so CRIU could parse smaps to find 32-bit vdso's size */
ret = syscall(__NR_kill, syscall(__NR_getpid), SIGSTOP);
exit_on(ret, err_fd, "Error: Can't stop myself with SIGSTOP (having a good time)\n");
ret = syscall(__NR_read, pipe_fd, &vdso_addr, sizeof(void *));
if (ret != sizeof(void *))
exit_on(-1, err_fd, "Error: Can't read size of mmaped vdso from pipe\n");
memcpy(vdso_buf, vdso_addr, vdso_size);
syscall(__NR_exit, 0);
}