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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b688fd8 was b688fd8, checked in by Martin Decky <martin@…>, 10 years ago

gradually introduce async ports, initial phase

The initial phase is to reimplement the traditional async client connections as an untyped fallback port. This creates the possibility to introduce ports typed by interface type gradually in later changesets.

  • Property mode set to 100644
File size: 10.2 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 <unistd.h>
49#include <stdbool.h>
50#include <fcntl.h>
51#include <sys/types.h>
52#include <ipc/services.h>
53#include <ipc/loader.h>
54#include <ns.h>
55#include <loader/pcb.h>
56#include <entry_point.h>
57#include <errno.h>
58#include <async.h>
59#include <str.h>
60#include <as.h>
61#include <elf/elf.h>
62#include <elf/elf_load.h>
63#include <vfs/vfs.h>
64
65#ifdef CONFIG_RTLD
66#include <rtld/rtld.h>
67#include <rtld/dynamic.h>
68#include <rtld/module.h>
69
70static int ldr_load_dyn_linked(elf_info_t *p_info);
71#endif
72
73#define DPRINTF(...)
74
75/** Pathname of the file that will be loaded */
76static char *pathname = NULL;
77
78/** The Program control block */
79static pcb_t pcb;
80
81/** Current working directory */
82static char *cwd = NULL;
83
84/** Number of arguments */
85static int argc = 0;
86/** Argument vector */
87static char **argv = NULL;
88/** Buffer holding all arguments */
89static char *arg_buf = NULL;
90
91/** Number of preset files */
92static unsigned int filc = 0;
93
94static elf_info_t prog_info;
95
96/** Used to limit number of connections to one. */
97static bool connected = false;
98
99#ifdef CONFIG_RTLD
100/** State structure of the dynamic linker. */
101runtime_env_t dload_re;
102static module_t prog_mod;
103#endif
104
105static void ldr_get_taskid(ipc_callid_t rid, ipc_call_t *request)
106{
107 ipc_callid_t callid;
108 task_id_t task_id;
109 size_t len;
110
111 task_id = task_get_id();
112
113 if (!async_data_read_receive(&callid, &len)) {
114 async_answer_0(callid, EINVAL);
115 async_answer_0(rid, EINVAL);
116 return;
117 }
118
119 if (len > sizeof(task_id))
120 len = sizeof(task_id);
121
122 async_data_read_finalize(callid, &task_id, len);
123 async_answer_0(rid, EOK);
124}
125
126/** Receive a call setting the current working directory.
127 *
128 * @param rid
129 * @param request
130 */
131static void ldr_set_cwd(ipc_callid_t rid, ipc_call_t *request)
132{
133 char *buf;
134 int rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, NULL);
135
136 if (rc == EOK) {
137 if (cwd != NULL)
138 free(cwd);
139
140 cwd = buf;
141 }
142
143 async_answer_0(rid, rc);
144}
145
146/** Receive a call setting pathname of the program to execute.
147 *
148 * @param rid
149 * @param request
150 */
151static void ldr_set_pathname(ipc_callid_t rid, ipc_call_t *request)
152{
153 char *buf;
154 int rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, NULL);
155
156 if (rc == EOK) {
157 if (pathname != NULL)
158 free(pathname);
159
160 pathname = buf;
161 }
162
163 async_answer_0(rid, rc);
164}
165
166/** Receive a call setting arguments of the program to execute.
167 *
168 * @param rid
169 * @param request
170 */
171static void ldr_set_args(ipc_callid_t rid, ipc_call_t *request)
172{
173 char *buf;
174 size_t buf_size;
175 int rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, &buf_size);
176
177 if (rc == EOK) {
178 /*
179 * Count number of arguments
180 */
181 char *cur = buf;
182 int count = 0;
183
184 while (cur < buf + buf_size) {
185 size_t arg_size = str_size(cur);
186 cur += arg_size + 1;
187 count++;
188 }
189
190 /*
191 * Allocate new argv
192 */
193 char **_argv = (char **) malloc((count + 1) * sizeof(char *));
194 if (_argv == NULL) {
195 free(buf);
196 async_answer_0(rid, ENOMEM);
197 return;
198 }
199
200 /*
201 * Fill the new argv with argument pointers
202 */
203 cur = buf;
204 count = 0;
205 while (cur < buf + buf_size) {
206 _argv[count] = cur;
207
208 size_t arg_size = str_size(cur);
209 cur += arg_size + 1;
210 count++;
211 }
212 _argv[count] = NULL;
213
214 /*
215 * Copy temporary data to global variables
216 */
217 if (arg_buf != NULL)
218 free(arg_buf);
219
220 if (argv != NULL)
221 free(argv);
222
223 argc = count;
224 arg_buf = buf;
225 argv = _argv;
226 }
227
228 async_answer_0(rid, rc);
229}
230
231/** Receive a call setting preset files of the program to execute.
232 *
233 * @param rid
234 * @param request
235 */
236static void ldr_set_files(ipc_callid_t rid, ipc_call_t *request)
237{
238 size_t count = IPC_GET_ARG1(*request);
239
240 async_exch_t *vfs_exch = vfs_exchange_begin();
241
242 for (filc = 0; filc < count; filc++) {
243 ipc_callid_t callid;
244 int fd;
245
246 if (!async_state_change_receive(&callid, NULL, NULL, NULL)) {
247 async_answer_0(callid, EINVAL);
248 break;
249 }
250 async_state_change_finalize(callid, vfs_exch);
251 fd = fd_wait();
252 assert(fd == (int) filc);
253 }
254
255 vfs_exchange_end(vfs_exch);
256
257 async_answer_0(rid, EOK);
258}
259
260/** Load the previously selected program.
261 *
262 * @param rid
263 * @param request
264 * @return 0 on success, !0 on error.
265 */
266static int ldr_load(ipc_callid_t rid, ipc_call_t *request)
267{
268 int rc;
269
270 rc = elf_load_file(pathname, 0, 0, &prog_info);
271 if (rc != EE_OK) {
272 DPRINTF("Failed to load executable '%s'.\n", pathname);
273 async_answer_0(rid, EINVAL);
274 return 1;
275 }
276
277 elf_create_pcb(&prog_info, &pcb);
278
279 pcb.cwd = cwd;
280
281 pcb.argc = argc;
282 pcb.argv = argv;
283
284 pcb.filc = filc;
285
286 if (prog_info.interp == NULL) {
287 /* Statically linked program */
288 async_answer_0(rid, EOK);
289 return 0;
290 }
291
292 DPRINTF("Binary is dynamically linked.\n");
293#ifdef CONFIG_RTLD
294 DPRINTF(" - pcb address: %p\n", &pcb);
295 DPRINTF( "- prog dynamic: %p\n", prog_info.dynamic);
296
297 rc = ldr_load_dyn_linked(&prog_info);
298#else
299 rc = ENOTSUP;
300#endif
301 async_answer_0(rid, rc);
302 return 0;
303}
304
305#ifdef CONFIG_RTLD
306
307static int ldr_load_dyn_linked(elf_info_t *p_info)
308{
309 runtime_env = &dload_re;
310
311 DPRINTF("Load dynamically linked program.\n");
312
313 /*
314 * First we need to process dynamic sections of the executable
315 * program and insert it into the module graph.
316 */
317
318 DPRINTF("Parse program .dynamic section at %p\n", p_info->dynamic);
319 dynamic_parse(p_info->dynamic, 0, &prog_mod.dyn);
320 prog_mod.bias = 0;
321 prog_mod.dyn.soname = "[program]";
322
323 /* Initialize list of loaded modules */
324 list_initialize(&runtime_env->modules);
325 list_append(&prog_mod.modules_link, &runtime_env->modules);
326
327 /* Pointer to program module. Used as root of the module graph. */
328 runtime_env->program = &prog_mod;
329
330 /* Work around non-existent memory space allocation. */
331 runtime_env->next_bias = 0x1000000;
332
333 /*
334 * Now we can continue with loading all other modules.
335 */
336
337 DPRINTF("Load all program dependencies\n");
338 module_load_deps(&prog_mod);
339
340 /*
341 * Now relocate/link all modules together.
342 */
343
344 /* Process relocations in all modules */
345 DPRINTF("Relocate all modules\n");
346 modules_process_relocs(&prog_mod);
347
348 /* Pass runtime evironment pointer through PCB. */
349 pcb.rtld_runtime = (void *) runtime_env;
350
351 return 0;
352}
353#endif
354
355/** Run the previously loaded program.
356 *
357 * @param rid
358 * @param request
359 * @return 0 on success, !0 on error.
360 */
361static void ldr_run(ipc_callid_t rid, ipc_call_t *request)
362{
363 const char *cp;
364
365 DPRINTF("Set task name\n");
366
367 /* Set the task name. */
368 cp = str_rchr(pathname, '/');
369 cp = (cp == NULL) ? pathname : (cp + 1);
370 task_set_name(cp);
371
372 /* Run program */
373 DPRINTF("Reply OK\n");
374 async_answer_0(rid, EOK);
375 DPRINTF("Jump to entry point at %p\n", pcb.entry);
376 entry_point_jmp(prog_info.entry, &pcb);
377
378 /* Not reached */
379}
380
381/** Handle loader connection.
382 *
383 * Receive and carry out commands (of which the last one should be
384 * to execute the loaded program).
385 */
386static void ldr_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
387{
388 /* Already have a connection? */
389 if (connected) {
390 async_answer_0(iid, ELIMIT);
391 return;
392 }
393
394 connected = true;
395
396 /* Accept the connection */
397 async_answer_0(iid, EOK);
398
399 /* Ignore parameters, the connection is already open */
400 (void) icall;
401
402 while (true) {
403 int retval;
404 ipc_call_t call;
405 ipc_callid_t callid = async_get_call(&call);
406
407 if (!IPC_GET_IMETHOD(call))
408 exit(0);
409
410 switch (IPC_GET_IMETHOD(call)) {
411 case LOADER_GET_TASKID:
412 ldr_get_taskid(callid, &call);
413 continue;
414 case LOADER_SET_CWD:
415 ldr_set_cwd(callid, &call);
416 continue;
417 case LOADER_SET_PATHNAME:
418 ldr_set_pathname(callid, &call);
419 continue;
420 case LOADER_SET_ARGS:
421 ldr_set_args(callid, &call);
422 continue;
423 case LOADER_SET_FILES:
424 ldr_set_files(callid, &call);
425 continue;
426 case LOADER_LOAD:
427 ldr_load(callid, &call);
428 continue;
429 case LOADER_RUN:
430 ldr_run(callid, &call);
431 /* Not reached */
432 default:
433 retval = EINVAL;
434 break;
435 }
436
437 async_answer_0(callid, retval);
438 }
439}
440
441/** Program loader main function.
442 */
443int main(int argc, char *argv[])
444{
445 /* Set a handler of incomming connections. */
446 async_set_fallback_port_handler(ldr_connection, NULL);
447
448 /* Introduce this task to the NS (give it our task ID). */
449 task_id_t id = task_get_id();
450 int rc = ns_intro(id);
451 if (rc != EOK)
452 return rc;
453
454 /* Register at naming service. */
455 rc = service_register(SERVICE_LOAD);
456 if (rc != EOK)
457 return rc;
458
459 async_manager();
460
461 /* Never reached */
462 return 0;
463}
464
465/** @}
466 */
Note: See TracBrowser for help on using the repository browser.