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

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

Fix build with dynamic linking enabled.
Eliminate surplus copy of elf_load.c, elf_load.h.

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