]>
Commit | Line | Data |
---|---|---|
f1a1313b | 1 | Discussion: |
a5affa61 | 2 | https://resin.io/blog/building-arm-containers-on-any-x86-machine-even-dockerhub/ |
f1a1313b | 3 | |
a5affa61 ER |
4 | https://github.com/resin-io/qemu/commit/782e5bb77014ff136f7bb6133a911e5f53e914a7 |
5 | ||
102e18f7 ER |
6 | https://github.com/resin-io/qemu/commit/782e5bb77014ff136f7bb6133a911e5f53e914a7#commitcomment-17193923 |
7 | It has gone through review[1][2][3] and I'm waiting for the maintainer of the linux-user subsystem to accept it in his tree. | |
8 | ||
9 | [1] https://patchwork.ozlabs.org/patch/569452/ | |
10 | [2] https://patchwork.ozlabs.org/patch/573877/ | |
11 | [3] https://patchwork.ozlabs.org/patch/582756/ | |
12 | ||
13 | From patchwork Mon Feb 15 05:51:47 2016 | |
14 | Content-Type: text/plain; charset="utf-8" | |
15 | MIME-Version: 1.0 | |
16 | Content-Transfer-Encoding: 7bit | |
17 | Subject: [v3] linux-user: add option to intercept execve() syscalls | |
a5affa61 | 18 | From: Petros Angelatos <petrosagg@resin.io> |
102e18f7 ER |
19 | X-Patchwork-Id: 582756 |
20 | Message-Id: <1455515507-26877-1-git-send-email-petrosagg@resin.io> | |
21 | To: qemu-devel@nongnu.org | |
22 | Cc: lucas.kaldstrom@hotmail.co.uk, peter.maydell@linaro.org, | |
23 | riku.voipio@iki.fi, | |
24 | laurent@vivier.eu, Petros Angelatos <petrosagg@resin.io> | |
25 | Date: Sun, 14 Feb 2016 21:51:47 -0800 | |
a5affa61 ER |
26 | |
27 | In order for one to use QEMU user mode emulation under a chroot, it is | |
28 | required to use binfmt_misc. This can be avoided by QEMU never doing a | |
29 | raw execve() to the host system. | |
30 | ||
31 | Introduce a new option, -execve, that uses the current QEMU interpreter | |
32 | to intercept execve(). | |
33 | ||
34 | qemu_execve() will prepend the interpreter path , similar to what | |
35 | binfmt_misc would do, and then pass the modified execve() to the host. | |
36 | ||
37 | It is necessary to parse hashbang scripts in that function otherwise | |
38 | the kernel will try to run the interpreter of a script without QEMU and | |
39 | get an invalid exec format error. | |
40 | ||
41 | Signed-off-by: Petros Angelatos <petrosagg@resin.io> | |
102e18f7 ER |
42 | Tested-by: Laurent Vivier <laurent@vivier.eu> |
43 | Reviewed-by: Laurent Vivier <laurent@vivier.eu> | |
a5affa61 | 44 | --- |
102e18f7 ER |
45 | v3 changes: |
46 | - rebase the patchset against current code | |
47 | ||
f1a1313b ER |
48 | diff --git a/linux-user/main.c b/linux-user/main.c |
49 | index ee12035..5951279 100644 | |
50 | --- a/linux-user/main.c | |
51 | +++ b/linux-user/main.c | |
52 | @@ -79,6 +79,7 @@ static void usage(int exitcode); | |
a5affa61 ER |
53 | |
54 | static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; | |
55 | const char *qemu_uname_release; | |
56 | +const char *qemu_execve_path; | |
57 | ||
58 | /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so | |
59 | we allocate a bigger stack. Need a better solution, for example | |
f1a1313b | 60 | @@ -3828,6 +3829,11 @@ static void handle_arg_guest_base(const char *arg) |
a5affa61 ER |
61 | have_guest_base = 1; |
62 | } | |
63 | ||
64 | +static void handle_arg_execve(const char *arg) | |
65 | +{ | |
f1a1313b | 66 | + qemu_execve_path = strdup(arg); |
a5affa61 ER |
67 | +} |
68 | + | |
69 | static void handle_arg_reserved_va(const char *arg) | |
70 | { | |
71 | char *p; | |
f1a1313b | 72 | @@ -3913,6 +3919,8 @@ static const struct qemu_argument arg_table[] = { |
a5affa61 ER |
73 | "uname", "set qemu uname release string to 'uname'"}, |
74 | {"B", "QEMU_GUEST_BASE", true, handle_arg_guest_base, | |
75 | "address", "set guest_base address to 'address'"}, | |
f1a1313b ER |
76 | + {"execve", "QEMU_EXECVE", true, handle_arg_execve, |
77 | + "path", "use interpreter at 'path' when a process calls execve()"}, | |
a5affa61 ER |
78 | {"R", "QEMU_RESERVED_VA", true, handle_arg_reserved_va, |
79 | "size", "reserve 'size' bytes for guest virtual address space"}, | |
80 | {"d", "QEMU_LOG", true, handle_arg_log, | |
81 | diff --git a/linux-user/qemu.h b/linux-user/qemu.h | |
102e18f7 | 82 | index bd90cc3..0d9b058 100644 |
a5affa61 ER |
83 | --- a/linux-user/qemu.h |
84 | +++ b/linux-user/qemu.h | |
102e18f7 | 85 | @@ -140,6 +140,7 @@ void init_task_state(TaskState *ts); |
a5affa61 ER |
86 | void task_settid(TaskState *); |
87 | void stop_all_tasks(void); | |
88 | extern const char *qemu_uname_release; | |
89 | +extern const char *qemu_execve_path; | |
90 | extern unsigned long mmap_min_addr; | |
91 | ||
92 | /* ??? See if we can avoid exposing so much of the loader internals. */ | |
99df371e ER |
93 | --- qemu-2.12.0/linux-user/syscall.c~ 2018-04-30 21:43:39.000000000 +0300 |
94 | +++ qemu-2.12.0/linux-user/syscall.c 2018-04-30 21:46:36.362935706 +0300 | |
f1a1313b | 95 | @@ -5854,6 +5854,109 @@ static target_timer_t get_timer_id(abi_long arg) |
a5affa61 ER |
96 | return timerid; |
97 | } | |
98 | ||
f1a1313b ER |
99 | +#define BINPRM_BUF_SIZE 128 |
100 | + | |
a5affa61 ER |
101 | +/* qemu_execve() Must return target values and target errnos. */ |
102 | +static abi_long qemu_execve(char *filename, char *argv[], | |
103 | + char *envp[]) | |
104 | +{ | |
105 | + char *i_arg = NULL, *i_name = NULL; | |
106 | + char **new_argp; | |
107 | + int argc, fd, ret, i, offset = 3; | |
108 | + char *cp; | |
109 | + char buf[BINPRM_BUF_SIZE]; | |
110 | + | |
a5affa61 ER |
111 | + for (argc = 0; argv[argc] != NULL; argc++) { |
112 | + /* nothing */ ; | |
113 | + } | |
114 | + | |
115 | + fd = open(filename, O_RDONLY); | |
116 | + if (fd == -1) { | |
f1a1313b | 117 | + return -ENOENT; |
a5affa61 ER |
118 | + } |
119 | + | |
120 | + ret = read(fd, buf, BINPRM_BUF_SIZE); | |
121 | + if (ret == -1) { | |
122 | + close(fd); | |
f1a1313b | 123 | + return -ENOENT; |
a5affa61 ER |
124 | + } |
125 | + | |
126 | + close(fd); | |
127 | + | |
128 | + /* adapted from the kernel | |
129 | + * https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/binfmt_script.c | |
130 | + */ | |
131 | + if ((buf[0] == '#') && (buf[1] == '!')) { | |
132 | + /* | |
133 | + * This section does the #! interpretation. | |
134 | + * Sorta complicated, but hopefully it will work. -TYT | |
135 | + */ | |
136 | + | |
137 | + buf[BINPRM_BUF_SIZE - 1] = '\0'; | |
138 | + cp = strchr(buf, '\n'); | |
139 | + if (cp == NULL) { | |
f1a1313b | 140 | + cp = buf+BINPRM_BUF_SIZE-1; |
a5affa61 ER |
141 | + } |
142 | + *cp = '\0'; | |
143 | + while (cp > buf) { | |
144 | + cp--; | |
145 | + if ((*cp == ' ') || (*cp == '\t')) { | |
146 | + *cp = '\0'; | |
147 | + } else { | |
148 | + break; | |
149 | + } | |
150 | + } | |
f1a1313b | 151 | + for (cp = buf+2; (*cp == ' ') || (*cp == '\t'); cp++) { |
a5affa61 ER |
152 | + /* nothing */ ; |
153 | + } | |
154 | + if (*cp == '\0') { | |
155 | + return -ENOEXEC; /* No interpreter name found */ | |
156 | + } | |
157 | + i_name = cp; | |
158 | + i_arg = NULL; | |
159 | + for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) { | |
160 | + /* nothing */ ; | |
161 | + } | |
162 | + while ((*cp == ' ') || (*cp == '\t')) { | |
163 | + *cp++ = '\0'; | |
164 | + } | |
165 | + if (*cp) { | |
166 | + i_arg = cp; | |
167 | + } | |
168 | + | |
169 | + if (i_arg) { | |
170 | + offset = 5; | |
171 | + } else { | |
172 | + offset = 4; | |
173 | + } | |
174 | + } | |
175 | + | |
176 | + new_argp = alloca((argc + offset + 1) * sizeof(void *)); | |
177 | + | |
178 | + /* Copy the original arguments with offset */ | |
179 | + for (i = 0; i < argc; i++) { | |
180 | + new_argp[i + offset] = argv[i]; | |
181 | + } | |
182 | + | |
183 | + new_argp[0] = strdup(qemu_execve_path); | |
184 | + new_argp[1] = strdup("-0"); | |
185 | + new_argp[offset] = filename; | |
186 | + new_argp[argc + offset] = NULL; | |
187 | + | |
188 | + if (i_name) { | |
189 | + new_argp[2] = i_name; | |
190 | + new_argp[3] = i_name; | |
191 | + | |
192 | + if (i_arg) { | |
193 | + new_argp[4] = i_arg; | |
194 | + } | |
195 | + } else { | |
196 | + new_argp[2] = argv[0]; | |
197 | + } | |
198 | + | |
99df371e | 199 | + return get_errno(safe_execve(qemu_execve_path, new_argp, envp)); |
a5affa61 ER |
200 | +} |
201 | + | |
f4914ae2 JP |
202 | static int target_to_host_cpu_mask(unsigned long *host_mask, |
203 | size_t host_size, | |
204 | abi_ulong target_addr, | |
99df371e ER |
205 | @@ -8257,7 +8257,12 @@ |
206 | * before the execve completes and makes it the other | |
207 | * program's problem. | |
208 | */ | |
209 | - ret = get_errno(safe_execve(p, argp, envp)); | |
f1a1313b ER |
210 | + if (qemu_execve_path && *qemu_execve_path) { |
211 | + ret = get_errno(qemu_execve(p, argp, envp)); | |
212 | + } else { | |
99df371e | 213 | + ret = get_errno(safe_execve(p, argp, envp)); |
f1a1313b ER |
214 | + } |
215 | + | |
a5affa61 ER |
216 | unlock_user(p, arg1, 0); |
217 | ||
218 | goto execve_end; |