source: mainline/uspace/srv/loader/main.c@ f787c8e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f787c8e was 40abf56a, checked in by Jiří Zárevúcky <jiri.zarevucky@…>, 7 years ago

Make sure that a thread with uninitialized TLS does not need to call malloc()
to initialize it.

For threads and tasks created by loader, we create TLS beforehand and pass
it to the child. For tasks spawned directly by the kernel, we require it is
a static executable and allocate the initial TLS using as_area_create() instead
of the libc allocator.

  • Property mode set to 100644
File size: 9.1 KB
Line 
1/*
2 * Copyright (c) 2008 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup loader
30 * @brief Loads and runs programs from VFS.
31 * @{
32 */
33/**
34 * @file
35 * @brief Loads and runs programs from VFS.
36 *
37 * The program loader is a special init binary. Its image is used
38 * to create a new task upon a @c task_spawn syscall. The syscall
39 * returns the id of a phone connected to the newly created task.
40 *
41 * The caller uses this phone to send the pathname and various other
42 * information to the loader. This is normally done by the C library
43 * and completely hidden from applications.
44 */
45
46#include <stdio.h>
47#include <stdlib.h>
48#include <stdbool.h>
49#include <stddef.h>
50#include <ipc/services.h>
51#include <ipc/loader.h>
52#include <ns.h>
53#include <loader/pcb.h>
54#include <entry_point.h>
55#include <errno.h>
56#include <async.h>
57#include <str.h>
58#include <as.h>
59#include <elf/elf.h>
60#include <elf/elf_load.h>
61#include <vfs/vfs.h>
62#include <vfs/inbox.h>
63
64#ifdef CONFIG_RTLD
65#include <rtld/rtld.h>
66#endif
67
68#define DPRINTF(...) ((void) 0)
69
70/** File that will be loaded */
71static char *progname = NULL;
72static int program_fd = -1;
73
74/** The Program control block */
75static pcb_t pcb;
76
77/** Current working directory */
78static char *cwd = NULL;
79
80/** Number of arguments */
81static int argc = 0;
82/** Argument vector */
83static char **argv = NULL;
84/** Buffer holding all arguments */
85static char *arg_buf = NULL;
86
87/** Inbox entries. */
88static struct pcb_inbox_entry inbox[INBOX_MAX_ENTRIES];
89static int inbox_entries = 0;
90
91static elf_info_t prog_info;
92
93/** Used to limit number of connections to one. */
94static bool connected = false;
95
96static void ldr_get_taskid(ipc_call_t *req)
97{
98 ipc_call_t call;
99 task_id_t task_id;
100 size_t len;
101
102 task_id = task_get_id();
103
104 if (!async_data_read_receive(&call, &len)) {
105 async_answer_0(&call, EINVAL);
106 async_answer_0(req, EINVAL);
107 return;
108 }
109
110 if (len > sizeof(task_id))
111 len = sizeof(task_id);
112
113 DPRINTF("LOADER_GET_TASKID() = %lu\n", (unsigned long) task_id);
114 async_data_read_finalize(&call, &task_id, len);
115 async_answer_0(req, EOK);
116}
117
118/** Receive a call setting the current working directory.
119 *
120 */
121static void ldr_set_cwd(ipc_call_t *req)
122{
123 char *buf;
124 errno_t rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, NULL);
125
126 if (rc == EOK) {
127 if (cwd != NULL)
128 free(cwd);
129
130 cwd = buf;
131 }
132
133 DPRINTF("LOADER_SET_CWD('%s')\n", cwd);
134 async_answer_0(req, rc);
135}
136
137/** Receive a call setting the program to execute.
138 *
139 */
140static void ldr_set_program(ipc_call_t *req)
141{
142 ipc_call_t call;
143 size_t namesize;
144 if (!async_data_write_receive(&call, &namesize)) {
145 async_answer_0(req, EINVAL);
146 return;
147 }
148
149 char *name = malloc(namesize);
150 // FIXME: check return value
151
152 errno_t rc = async_data_write_finalize(&call, name, namesize);
153 if (rc != EOK) {
154 async_answer_0(req, EINVAL);
155 return;
156 }
157
158 int file;
159 rc = vfs_receive_handle(true, &file);
160 if (rc != EOK) {
161 async_answer_0(req, EINVAL);
162 return;
163 }
164
165 DPRINTF("LOADER_SET_PROGRAM('%s')\n", name);
166
167 progname = name;
168 program_fd = file;
169 async_answer_0(req, EOK);
170}
171
172/** Receive a call setting arguments of the program to execute.
173 *
174 */
175static void ldr_set_args(ipc_call_t *req)
176{
177 char *buf;
178 size_t buf_size;
179 errno_t rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, &buf_size);
180
181 if (rc == EOK) {
182 /*
183 * Count number of arguments
184 */
185 char *cur = buf;
186 int count = 0;
187
188 while (cur < buf + buf_size) {
189 size_t arg_size = str_size(cur);
190 cur += arg_size + 1;
191 count++;
192 }
193
194 /*
195 * Allocate new argv
196 */
197 char **_argv = (char **) malloc((count + 1) * sizeof(char *));
198 if (_argv == NULL) {
199 free(buf);
200 async_answer_0(req, ENOMEM);
201 return;
202 }
203
204 /*
205 * Fill the new argv with argument pointers
206 */
207 cur = buf;
208 count = 0;
209 while (cur < buf + buf_size) {
210 _argv[count] = cur;
211
212 size_t arg_size = str_size(cur);
213 cur += arg_size + 1;
214 count++;
215 }
216 _argv[count] = NULL;
217
218 /*
219 * Copy temporary data to global variables
220 */
221 if (arg_buf != NULL)
222 free(arg_buf);
223
224 if (argv != NULL)
225 free(argv);
226
227 for (int i = 0; i < count; i++)
228 DPRINTF("LOADER_SET_ARGS('%s')\n", _argv[i]);
229
230 argc = count;
231 arg_buf = buf;
232 argv = _argv;
233 }
234
235 async_answer_0(req, rc);
236}
237
238/** Receive a call setting inbox files of the program to execute.
239 *
240 */
241static void ldr_add_inbox(ipc_call_t *req)
242{
243 if (inbox_entries == INBOX_MAX_ENTRIES) {
244 async_answer_0(req, ERANGE);
245 return;
246 }
247
248 ipc_call_t call;
249 size_t namesize;
250 if (!async_data_write_receive(&call, &namesize)) {
251 async_answer_0(req, EINVAL);
252 return;
253 }
254
255 char *name = malloc(namesize);
256 errno_t rc = async_data_write_finalize(&call, name, namesize);
257 if (rc != EOK) {
258 async_answer_0(req, EINVAL);
259 return;
260 }
261
262 int file;
263 rc = vfs_receive_handle(true, &file);
264 if (rc != EOK) {
265 async_answer_0(req, EINVAL);
266 return;
267 }
268
269 DPRINTF("LOADER_ADD_INBOX('%s')\n", name);
270
271 /*
272 * We need to set the root early for dynamically linked binaries so
273 * that the loader can use it too.
274 */
275 if (str_cmp(name, "root") == 0)
276 vfs_root_set(file);
277
278 inbox[inbox_entries].name = name;
279 inbox[inbox_entries].file = file;
280 inbox_entries++;
281 async_answer_0(req, EOK);
282}
283
284/** Load the previously selected program.
285 *
286 * @return 0 on success, !0 on error.
287 *
288 */
289static int ldr_load(ipc_call_t *req)
290{
291 DPRINTF("LOADER_LOAD()\n");
292
293 int rc = elf_load(program_fd, &prog_info);
294 if (rc != EE_OK) {
295 DPRINTF("Failed to load executable for '%s'.\n", progname);
296 async_answer_0(req, EINVAL);
297 return 1;
298 }
299
300 DPRINTF("Loaded.\n");
301
302#ifdef CONFIG_RTLD
303 if (prog_info.env) {
304 pcb.tcb = rtld_tls_make(prog_info.env);
305 } else {
306 pcb.tcb = tls_make(prog_info.finfo.base);
307 }
308#else
309 pcb.tcb = tls_make(prog_info.finfo.base);
310#endif
311
312 elf_set_pcb(&prog_info, &pcb);
313
314 DPRINTF("PCB set.\n");
315
316 pcb.cwd = cwd;
317
318 pcb.argc = argc;
319 pcb.argv = argv;
320
321 pcb.inbox = inbox;
322 pcb.inbox_entries = inbox_entries;
323
324 DPRINTF("Answering.\n");
325 async_answer_0(req, EOK);
326 return 0;
327}
328
329/** Run the previously loaded program.
330 *
331 * @return 0 on success, !0 on error.
332 *
333 */
334static __attribute__((noreturn)) void ldr_run(ipc_call_t *req)
335{
336 DPRINTF("Set task name\n");
337
338 /* Set the task name. */
339 task_set_name(progname);
340
341 /* Run program */
342 DPRINTF("Reply OK\n");
343 async_answer_0(req, EOK);
344
345 DPRINTF("Jump to entry point at %p\n", pcb.entry);
346
347 __tcb_reset();
348 entry_point_jmp(prog_info.finfo.entry, &pcb);
349
350 /* Not reached */
351}
352
353/** Handle loader connection.
354 *
355 * Receive and carry out commands (of which the last one should be
356 * to execute the loaded program).
357 *
358 */
359static void ldr_connection(ipc_call_t *icall, void *arg)
360{
361 /* Already have a connection? */
362 if (connected) {
363 async_answer_0(icall, ELIMIT);
364 return;
365 }
366
367 connected = true;
368
369 /* Accept the connection */
370 async_answer_0(icall, EOK);
371
372 /* Ignore parameters, the connection is already open */
373 (void) icall;
374
375 while (true) {
376 errno_t retval;
377 ipc_call_t call;
378 async_get_call(&call);
379
380 if (!IPC_GET_IMETHOD(call))
381 exit(0);
382
383 switch (IPC_GET_IMETHOD(call)) {
384 case LOADER_GET_TASKID:
385 ldr_get_taskid(&call);
386 continue;
387 case LOADER_SET_CWD:
388 ldr_set_cwd(&call);
389 continue;
390 case LOADER_SET_PROGRAM:
391 ldr_set_program(&call);
392 continue;
393 case LOADER_SET_ARGS:
394 ldr_set_args(&call);
395 continue;
396 case LOADER_ADD_INBOX:
397 ldr_add_inbox(&call);
398 continue;
399 case LOADER_LOAD:
400 ldr_load(&call);
401 continue;
402 case LOADER_RUN:
403 ldr_run(&call);
404 /* Not reached */
405 default:
406 retval = EINVAL;
407 break;
408 }
409
410 async_answer_0(&call, retval);
411 }
412}
413
414/** Program loader main function.
415 */
416int main(int argc, char *argv[])
417{
418 /* Introduce this task to the NS (give it our task ID). */
419 task_id_t id = task_get_id();
420 errno_t rc = ns_intro(id);
421 if (rc != EOK)
422 return rc;
423
424 /* Register at naming service. */
425 rc = service_register(SERVICE_LOADER, INTERFACE_LOADER,
426 ldr_connection, NULL);
427 if (rc != EOK)
428 return rc;
429
430 async_manager();
431
432 /* Never reached */
433 return 0;
434}
435
436/** @}
437 */
Note: See TracBrowser for help on using the repository browser.