source: mainline/uspace/srv/loader/main.c@ 8a1fb09

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

Integrate rest of rtld/ into C library.

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