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

Last change on this file since bcd7775 was 76c8209, checked in by Matthieu Riolo <matthieu.riolo@…>, 6 years ago

correcting typos

  • Property mode set to 100644
File size: 9.8 KB
RevLine 
[c98e6ee]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 * @{
[2f57690]31 */
[c98e6ee]32/**
33 * @file
34 *
35 * The program loader is a special init binary. Its image is used
[0a8f070]36 * to create a new task upon a @c task_spawn syscall. It has a phone connected
[012dd8e]37 * to the caller of the syscall. The formal caller (taskman) performs a
[0a8f070]38 * handshake with loader so that apparent caller can communicate with the
39 * loader.
[c98e6ee]40 *
[0a8f070]41 * The apparent caller uses his phone to send the pathname and various other
[c98e6ee]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>
[3e6a98c5]48#include <stdbool.h>
[8d2dd7f2]49#include <stddef.h>
[bfd1546]50#include <ipc/services.h>
[c98e6ee]51#include <as.h>
[0a8f070]52#include <async.h>
[90b8d58]53#include <elf/elf.h>
[bfdb5af1]54#include <elf/elf_load.h>
[0a8f070]55#include <entry_point.h>
56#include <errno.h>
57#include <fibril_synch.h>
58#include <ipc/loader.h>
59#include <loader/pcb.h>
[012dd8e]60#include <ns.h>
[0a8f070]61#include <str.h>
[012dd8e]62#include <task.h>
[0a8f070]63#include <taskman.h>
[7171760]64#include <vfs/vfs.h>
[bb9ec2d]65#include <vfs/inbox.h>
[25f6bddb]66#include <libc.h>
[c98e6ee]67
[40abf56a]68#ifdef CONFIG_RTLD
69#include <rtld/rtld.h>
70#endif
71
[012dd8e]72#define NAME "loader"
[e3787a0]73#define DPRINTF(...) ((void) 0)
[1ea99cc]74
[bb9ec2d]75/** File that will be loaded */
76static char *progname = NULL;
77static int program_fd = -1;
[c98e6ee]78
79/** The Program control block */
80static pcb_t pcb;
81
[622cdbe]82/** Current working directory */
83static char *cwd = NULL;
84
[c98e6ee]85/** Number of arguments */
86static int argc = 0;
87/** Argument vector */
88static char **argv = NULL;
89/** Buffer holding all arguments */
90static char *arg_buf = NULL;
91
[bb9ec2d]92/** Inbox entries. */
93static struct pcb_inbox_entry inbox[INBOX_MAX_ENTRIES];
94static int inbox_entries = 0;
[bbdbf86]95
[4470e26]96static elf_info_t prog_info;
97
[bfd1546]98/** Used to limit number of connections to one. */
[007e6efa]99static bool connected = false;
[4470e26]100
[984a9ba]101static void ldr_get_taskid(ipc_call_t *req)
[47e0a05b]102{
[984a9ba]103 ipc_call_t call;
[47e0a05b]104 task_id_t task_id;
105 size_t len;
[a35b458]106
[47e0a05b]107 task_id = task_get_id();
[a35b458]108
[984a9ba]109 if (!async_data_read_receive(&call, &len)) {
110 async_answer_0(&call, EINVAL);
111 async_answer_0(req, EINVAL);
[47e0a05b]112 return;
113 }
[a35b458]114
[2f57690]115 if (len > sizeof(task_id))
116 len = sizeof(task_id);
[a35b458]117
[e3787a0]118 DPRINTF("LOADER_GET_TASKID() = %lu\n", (unsigned long) task_id);
[984a9ba]119 async_data_read_finalize(&call, &task_id, len);
120 async_answer_0(req, EOK);
[47e0a05b]121}
122
[622cdbe]123/** Receive a call setting the current working directory.
[c98e6ee]124 *
125 */
[984a9ba]126static void ldr_set_cwd(ipc_call_t *req)
[c98e6ee]127{
[472c09d]128 char *buf;
[b7fd2a0]129 errno_t rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, NULL);
[a35b458]130
[472c09d]131 if (rc == EOK) {
132 if (cwd != NULL)
133 free(cwd);
[a35b458]134
[472c09d]135 cwd = buf;
[c98e6ee]136 }
[a35b458]137
[e3787a0]138 DPRINTF("LOADER_SET_CWD('%s')\n", cwd);
[984a9ba]139 async_answer_0(req, rc);
[622cdbe]140}
[47e0a05b]141
[bb9ec2d]142/** Receive a call setting the program to execute.
[c98e6ee]143 *
144 */
[984a9ba]145static void ldr_set_program(ipc_call_t *req)
[c98e6ee]146{
[984a9ba]147 ipc_call_t call;
[bb9ec2d]148 size_t namesize;
[984a9ba]149 if (!async_data_write_receive(&call, &namesize)) {
150 async_answer_0(req, EINVAL);
[bb9ec2d]151 return;
152 }
153
[3bacee1]154 char *name = malloc(namesize);
[984a9ba]155 // FIXME: check return value
156
157 errno_t rc = async_data_write_finalize(&call, name, namesize);
[bb9ec2d]158 if (rc != EOK) {
[984a9ba]159 async_answer_0(req, EINVAL);
[bb9ec2d]160 return;
161 }
162
[f77c1c9]163 int file;
164 rc = vfs_receive_handle(true, &file);
165 if (rc != EOK) {
[984a9ba]166 async_answer_0(req, EINVAL);
[bb9ec2d]167 return;
[c98e6ee]168 }
[a35b458]169
[e3787a0]170 DPRINTF("LOADER_SET_PROGRAM('%s')\n", name);
171
[bb9ec2d]172 progname = name;
173 program_fd = file;
[984a9ba]174 async_answer_0(req, EOK);
[c98e6ee]175}
176
177/** Receive a call setting arguments of the program to execute.
178 *
179 */
[984a9ba]180static void ldr_set_args(ipc_call_t *req)
[c98e6ee]181{
[472c09d]182 char *buf;
183 size_t buf_size;
[b7fd2a0]184 errno_t rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, &buf_size);
[a35b458]185
[472c09d]186 if (rc == EOK) {
187 /*
188 * Count number of arguments
189 */
190 char *cur = buf;
191 int count = 0;
[a35b458]192
[472c09d]193 while (cur < buf + buf_size) {
194 size_t arg_size = str_size(cur);
195 cur += arg_size + 1;
196 count++;
197 }
[a35b458]198
[472c09d]199 /*
200 * Allocate new argv
201 */
202 char **_argv = (char **) malloc((count + 1) * sizeof(char *));
203 if (_argv == NULL) {
204 free(buf);
[984a9ba]205 async_answer_0(req, ENOMEM);
[472c09d]206 return;
207 }
[a35b458]208
[472c09d]209 /*
210 * Fill the new argv with argument pointers
211 */
212 cur = buf;
213 count = 0;
214 while (cur < buf + buf_size) {
215 _argv[count] = cur;
[a35b458]216
[472c09d]217 size_t arg_size = str_size(cur);
218 cur += arg_size + 1;
219 count++;
220 }
221 _argv[count] = NULL;
[a35b458]222
[472c09d]223 /*
224 * Copy temporary data to global variables
225 */
226 if (arg_buf != NULL)
227 free(arg_buf);
[a35b458]228
[472c09d]229 if (argv != NULL)
230 free(argv);
[a35b458]231
[e3787a0]232 for (int i = 0; i < count; i++)
233 DPRINTF("LOADER_SET_ARGS('%s')\n", _argv[i]);
234
[472c09d]235 argc = count;
236 arg_buf = buf;
237 argv = _argv;
[c98e6ee]238 }
[a35b458]239
[984a9ba]240 async_answer_0(req, rc);
[c98e6ee]241}
242
[bb9ec2d]243/** Receive a call setting inbox files of the program to execute.
[bbdbf86]244 *
245 */
[984a9ba]246static void ldr_add_inbox(ipc_call_t *req)
[bbdbf86]247{
[bb9ec2d]248 if (inbox_entries == INBOX_MAX_ENTRIES) {
[984a9ba]249 async_answer_0(req, ERANGE);
[b7f69f2]250 return;
[bb9ec2d]251 }
[7171760]252
[984a9ba]253 ipc_call_t call;
[bb9ec2d]254 size_t namesize;
[984a9ba]255 if (!async_data_write_receive(&call, &namesize)) {
256 async_answer_0(req, EINVAL);
[bb9ec2d]257 return;
258 }
259
[3bacee1]260 char *name = malloc(namesize);
[984a9ba]261 errno_t rc = async_data_write_finalize(&call, name, namesize);
[bb9ec2d]262 if (rc != EOK) {
[984a9ba]263 async_answer_0(req, EINVAL);
[bb9ec2d]264 return;
265 }
266
[f77c1c9]267 int file;
268 rc = vfs_receive_handle(true, &file);
269 if (rc != EOK) {
[984a9ba]270 async_answer_0(req, EINVAL);
[bb9ec2d]271 return;
[bbdbf86]272 }
[7171760]273
[e3787a0]274 DPRINTF("LOADER_ADD_INBOX('%s')\n", name);
275
[ea56098]276 /*
277 * We need to set the root early for dynamically linked binaries so
278 * that the loader can use it too.
279 */
280 if (str_cmp(name, "root") == 0)
281 vfs_root_set(file);
282
[bb9ec2d]283 inbox[inbox_entries].name = name;
284 inbox[inbox_entries].file = file;
285 inbox_entries++;
[984a9ba]286 async_answer_0(req, EOK);
[bbdbf86]287}
288
[4470e26]289/** Load the previously selected program.
[c98e6ee]290 *
291 * @return 0 on success, !0 on error.
[984a9ba]292 *
[c98e6ee]293 */
[0939097a]294static void ldr_load(ipc_call_t *req)
[c98e6ee]295{
[e3787a0]296 DPRINTF("LOADER_LOAD()\n");
297
[bdca26a]298 errno_t rc = elf_load(program_fd, &prog_info);
299 if (rc != EOK) {
[bb9ec2d]300 DPRINTF("Failed to load executable for '%s'.\n", progname);
[984a9ba]301 async_answer_0(req, EINVAL);
[0939097a]302 return;
[c98e6ee]303 }
[a35b458]304
[e3787a0]305 DPRINTF("Loaded.\n");
306
[40abf56a]307#ifdef CONFIG_RTLD
308 if (prog_info.env) {
309 pcb.tcb = rtld_tls_make(prog_info.env);
310 } else {
311 pcb.tcb = tls_make(prog_info.finfo.base);
312 }
313#else
314 pcb.tcb = tls_make(prog_info.finfo.base);
315#endif
316
[c74b9de]317 if (!pcb.tcb) {
318 DPRINTF("Failed to make TLS for '%s'.\n", progname);
319 async_answer_0(req, ENOMEM);
[0939097a]320 return;
[c74b9de]321 }
322
[17341d4]323 elf_set_pcb(&prog_info, &pcb);
[a35b458]324
[e3787a0]325 DPRINTF("PCB set.\n");
326
[012dd8e]327 pcb.session_taskman = taskman_get_session();
[0a8f070]328
[622cdbe]329 pcb.cwd = cwd;
[a35b458]330
[c98e6ee]331 pcb.argc = argc;
332 pcb.argv = argv;
[a35b458]333
[bb9ec2d]334 pcb.inbox = inbox;
335 pcb.inbox_entries = inbox_entries;
[a35b458]336
[e3787a0]337 DPRINTF("Answering.\n");
[984a9ba]338 async_answer_0(req, EOK);
[c98e6ee]339}
340
[4470e26]341/** Run the previously loaded program.
342 *
343 * @return 0 on success, !0 on error.
[984a9ba]344 *
[4470e26]345 */
[984a9ba]346static __attribute__((noreturn)) void ldr_run(ipc_call_t *req)
[4470e26]347{
[a6dffb8]348 DPRINTF("Set task name\n");
349
[bc18d63]350 /* Set the task name. */
[bb9ec2d]351 task_set_name(progname);
[a35b458]352
[a6dffb8]353 /* Run program */
354 DPRINTF("Reply OK\n");
[984a9ba]355 async_answer_0(req, EOK);
[40abf56a]356
[faf19d4]357 /*
358 * Wait for the hangup from the other side in order not to leave any
359 * unanswered IPC_M_PHONE_HUNGUP messages behind.
360 */
361 async_get_call(req);
[fafb8e5]362 assert(!ipc_get_imethod(req));
[faf19d4]363 async_answer_0(req, EOK);
364
[a6dffb8]365 DPRINTF("Jump to entry point at %p\n", pcb.entry);
[40abf56a]366
[25f6bddb]367 __libc_fini();
[40abf56a]368 __tcb_reset();
[17341d4]369 entry_point_jmp(prog_info.finfo.entry, &pcb);
[a35b458]370
[4470e26]371 /* Not reached */
372}
373
[c98e6ee]374/** Handle loader connection.
375 *
376 * Receive and carry out commands (of which the last one should be
377 * to execute the loaded program).
[984a9ba]378 *
[c98e6ee]379 */
[984a9ba]380static void ldr_connection(ipc_call_t *icall, void *arg)
[c98e6ee]381{
[bfd1546]382 /* Already have a connection? */
383 if (connected) {
[984a9ba]384 async_answer_0(icall, ELIMIT);
[bfd1546]385 return;
386 }
[a35b458]387
[bfd1546]388 connected = true;
[a35b458]389
[bfd1546]390 /* Accept the connection */
[beb83c1]391 async_accept_0(icall);
[a35b458]392
[c98e6ee]393 /* Ignore parameters, the connection is already open */
[2f57690]394 (void) icall;
[a35b458]395
[79ae36dd]396 while (true) {
[b7fd2a0]397 errno_t retval;
[79ae36dd]398 ipc_call_t call;
[984a9ba]399 async_get_call(&call);
[a35b458]400
[fafb8e5]401 if (!ipc_get_imethod(&call)) {
[889cdb1]402 async_answer_0(&call, EOK);
[86e3d62]403 exit(0);
[889cdb1]404 }
[a35b458]405
[fafb8e5]406 switch (ipc_get_imethod(&call)) {
[47e0a05b]407 case LOADER_GET_TASKID:
[984a9ba]408 ldr_get_taskid(&call);
[47e0a05b]409 continue;
[622cdbe]410 case LOADER_SET_CWD:
[984a9ba]411 ldr_set_cwd(&call);
[622cdbe]412 continue;
[bb9ec2d]413 case LOADER_SET_PROGRAM:
[984a9ba]414 ldr_set_program(&call);
[c98e6ee]415 continue;
416 case LOADER_SET_ARGS:
[984a9ba]417 ldr_set_args(&call);
[bbdbf86]418 continue;
[bb9ec2d]419 case LOADER_ADD_INBOX:
[984a9ba]420 ldr_add_inbox(&call);
[4470e26]421 continue;
422 case LOADER_LOAD:
[984a9ba]423 ldr_load(&call);
[4470e26]424 continue;
[c98e6ee]425 case LOADER_RUN:
[984a9ba]426 ldr_run(&call);
[4470e26]427 /* Not reached */
[c98e6ee]428 default:
[8c3bc75]429 retval = EINVAL;
[c98e6ee]430 break;
431 }
[a35b458]432
[984a9ba]433 async_answer_0(&call, retval);
[c98e6ee]434 }
435}
436
437/** Program loader main function.
438 */
439int main(int argc, char *argv[])
440{
[0a8f070]441 /* Set a handler of incomming connections. */
442 async_set_fallback_port_handler(ldr_connection, NULL);
[102f641]443
[012dd8e]444 /* Announce to taskman. */
445 errno_t rc = taskman_intro_loader();
[0a8f070]446 if (rc != EOK) {
[76c8209]447 printf("%s: did not receive connection from taskman (%i)\n",
[012dd8e]448 NAME, rc);
[b39b5cb]449 return rc;
[0a8f070]450 }
[a35b458]451
[012dd8e]452 /*
453 * We are not a regular server, thus no retval is set, just wait for
454 * forwarded connections by taskman.
455 */
[c98e6ee]456 async_manager();
[102f641]457
[bfd1546]458 /* Never reached */
[c98e6ee]459 return 0;
460}
461
462/** @}
463 */
Note: See TracBrowser for help on using the repository browser.