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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d2a6a04b was 7fb3f1c, checked in by Jiri Svoboda <jiri@…>, 14 years ago

Make all architectures buildable. Only allow enabling dynamic linking on ia32.

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