source: mainline/uspace/srv/loader/main.c@ 3e6a98c5

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

Standards-compliant boolean type.

  • Property mode set to 100644
File size: 10.2 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
[2f57690]30 * @brief Loads and runs programs from VFS.
[c98e6ee]31 * @{
[2f57690]32 */
[c98e6ee]33/**
34 * @file
[2f57690]35 * @brief Loads and runs programs from VFS.
[c98e6ee]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>
[3e6a98c5]49#include <stdbool.h>
[c98e6ee]50#include <fcntl.h>
51#include <sys/types.h>
[bfd1546]52#include <ipc/services.h>
[c98e6ee]53#include <ipc/loader.h>
[79ae36dd]54#include <ns.h>
[c98e6ee]55#include <loader/pcb.h>
[f798178]56#include <entry_point.h>
[c98e6ee]57#include <errno.h>
58#include <async.h>
[19f857a]59#include <str.h>
[c98e6ee]60#include <as.h>
[90b8d58]61#include <elf/elf.h>
[bfdb5af1]62#include <elf/elf_load.h>
[7171760]63#include <vfs/vfs.h>
[c98e6ee]64
[7fb3f1c]65#ifdef CONFIG_RTLD
[8a1fb09]66#include <rtld/rtld.h>
67#include <rtld/dynamic.h>
68#include <rtld/module.h>
[a6dffb8]69
70static int ldr_load_dyn_linked(elf_info_t *p_info);
[7fb3f1c]71#endif
72
73#define DPRINTF(...)
[1ea99cc]74
[c98e6ee]75/** Pathname of the file that will be loaded */
76static char *pathname = NULL;
77
78/** The Program control block */
79static pcb_t pcb;
80
[622cdbe]81/** Current working directory */
82static char *cwd = NULL;
83
[c98e6ee]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
[bbdbf86]91/** Number of preset files */
[7171760]92static unsigned int filc = 0;
[bbdbf86]93
[4470e26]94static elf_info_t prog_info;
95
[bfd1546]96/** Used to limit number of connections to one. */
[007e6efa]97static bool connected = false;
[4470e26]98
[7fb3f1c]99#ifdef CONFIG_RTLD
[a6dffb8]100/** State structure of the dynamic linker. */
101runtime_env_t dload_re;
102static module_t prog_mod;
[7fb3f1c]103#endif
[a6dffb8]104
[bbdbf86]105static void ldr_get_taskid(ipc_callid_t rid, ipc_call_t *request)
[47e0a05b]106{
107 ipc_callid_t callid;
108 task_id_t task_id;
109 size_t len;
[2f57690]110
[47e0a05b]111 task_id = task_get_id();
[2f57690]112
[0da4e41]113 if (!async_data_read_receive(&callid, &len)) {
[ffa2c8ef]114 async_answer_0(callid, EINVAL);
115 async_answer_0(rid, EINVAL);
[47e0a05b]116 return;
117 }
[2f57690]118
119 if (len > sizeof(task_id))
120 len = sizeof(task_id);
121
[0da4e41]122 async_data_read_finalize(callid, &task_id, len);
[ffa2c8ef]123 async_answer_0(rid, EOK);
[47e0a05b]124}
125
[622cdbe]126/** Receive a call setting the current working directory.
[c98e6ee]127 *
128 * @param rid
129 * @param request
130 */
[622cdbe]131static void ldr_set_cwd(ipc_callid_t rid, ipc_call_t *request)
[c98e6ee]132{
[472c09d]133 char *buf;
[eda925a]134 int rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, NULL);
[2f57690]135
[472c09d]136 if (rc == EOK) {
137 if (cwd != NULL)
138 free(cwd);
139
140 cwd = buf;
[c98e6ee]141 }
[2f57690]142
[ffa2c8ef]143 async_answer_0(rid, rc);
[622cdbe]144}
[47e0a05b]145
[c98e6ee]146/** Receive a call setting pathname of the program to execute.
147 *
148 * @param rid
149 * @param request
150 */
[bbdbf86]151static void ldr_set_pathname(ipc_callid_t rid, ipc_call_t *request)
[c98e6ee]152{
[472c09d]153 char *buf;
[eda925a]154 int rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, NULL);
[2f57690]155
[472c09d]156 if (rc == EOK) {
157 if (pathname != NULL)
158 free(pathname);
159
160 pathname = buf;
[c98e6ee]161 }
[2f57690]162
[ffa2c8ef]163 async_answer_0(rid, rc);
[c98e6ee]164}
165
166/** Receive a call setting arguments of the program to execute.
167 *
168 * @param rid
169 * @param request
170 */
[bbdbf86]171static void ldr_set_args(ipc_callid_t rid, ipc_call_t *request)
[c98e6ee]172{
[472c09d]173 char *buf;
174 size_t buf_size;
[eda925a]175 int rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, &buf_size);
[472c09d]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 }
[2f57690]189
[472c09d]190 /*
191 * Allocate new argv
192 */
193 char **_argv = (char **) malloc((count + 1) * sizeof(char *));
194 if (_argv == NULL) {
195 free(buf);
[ffa2c8ef]196 async_answer_0(rid, ENOMEM);
[472c09d]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;
[2f57690]213
[472c09d]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;
[c98e6ee]226 }
[2f57690]227
[ffa2c8ef]228 async_answer_0(rid, rc);
[c98e6ee]229}
230
[bbdbf86]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{
[7171760]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;
[27b76ca]244 int fd;
[7171760]245
246 if (!async_state_change_receive(&callid, NULL, NULL, NULL)) {
247 async_answer_0(callid, EINVAL);
248 break;
[472c09d]249 }
[7171760]250 async_state_change_finalize(callid, vfs_exch);
[27b76ca]251 fd = fd_wait();
252 assert(fd == (int) filc);
[bbdbf86]253 }
[7171760]254
255 vfs_exchange_end(vfs_exch);
256
[ffa2c8ef]257 async_answer_0(rid, EOK);
[bbdbf86]258}
259
[4470e26]260/** Load the previously selected program.
[c98e6ee]261 *
262 * @param rid
263 * @param request
264 * @return 0 on success, !0 on error.
265 */
[bbdbf86]266static int ldr_load(ipc_callid_t rid, ipc_call_t *request)
[c98e6ee]267{
268 int rc;
[2f57690]269
[1ea99cc]270 rc = elf_load_file(pathname, 0, 0, &prog_info);
[409b0d6]271 if (rc != EE_OK) {
[06b2b7f]272 DPRINTF("Failed to load executable '%s'.\n", pathname);
[ffa2c8ef]273 async_answer_0(rid, EINVAL);
[c98e6ee]274 return 1;
275 }
[2f57690]276
[c98e6ee]277 elf_create_pcb(&prog_info, &pcb);
[2f57690]278
[622cdbe]279 pcb.cwd = cwd;
280
[c98e6ee]281 pcb.argc = argc;
282 pcb.argv = argv;
[2f57690]283
[bbdbf86]284 pcb.filc = filc;
285
[c98e6ee]286 if (prog_info.interp == NULL) {
287 /* Statically linked program */
[ffa2c8ef]288 async_answer_0(rid, EOK);
[c98e6ee]289 return 0;
290 }
[2f57690]291
[a6dffb8]292 DPRINTF("Binary is dynamically linked.\n");
[7fb3f1c]293#ifdef CONFIG_RTLD
[a6dffb8]294 DPRINTF(" - pcb address: %p\n", &pcb);
295 DPRINTF( "- prog dynamic: %p\n", prog_info.dynamic);
[1ea99cc]296
[a6dffb8]297 rc = ldr_load_dyn_linked(&prog_info);
[7fb3f1c]298#else
299 rc = ENOTSUP;
300#endif
[a6dffb8]301 async_answer_0(rid, rc);
[c98e6ee]302 return 0;
303}
304
[7fb3f1c]305#ifdef CONFIG_RTLD
306
[a6dffb8]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 */
[bfdb5af1]324 list_initialize(&runtime_env->modules);
325 list_append(&prog_mod.modules_link, &runtime_env->modules);
[a6dffb8]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}
[7fb3f1c]353#endif
[4470e26]354
355/** Run the previously loaded program.
356 *
357 * @param rid
358 * @param request
359 * @return 0 on success, !0 on error.
360 */
[bbdbf86]361static void ldr_run(ipc_callid_t rid, ipc_call_t *request)
[4470e26]362{
[20f1597]363 const char *cp;
[2f57690]364
[a6dffb8]365 DPRINTF("Set task name\n");
366
[bc18d63]367 /* Set the task name. */
[7afb4a5]368 cp = str_rchr(pathname, '/');
[20f1597]369 cp = (cp == NULL) ? pathname : (cp + 1);
370 task_set_name(cp);
[2f57690]371
[a6dffb8]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);
[f798178]376 entry_point_jmp(prog_info.entry, &pcb);
[bbdbf86]377
[4470e26]378 /* Not reached */
379}
380
[c98e6ee]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 */
[9934f7d]386static void ldr_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
[c98e6ee]387{
[bfd1546]388 /* Already have a connection? */
389 if (connected) {
[ffa2c8ef]390 async_answer_0(iid, ELIMIT);
[bfd1546]391 return;
392 }
[2f57690]393
[bfd1546]394 connected = true;
395
396 /* Accept the connection */
[ffa2c8ef]397 async_answer_0(iid, EOK);
[2f57690]398
[c98e6ee]399 /* Ignore parameters, the connection is already open */
[2f57690]400 (void) icall;
401
[79ae36dd]402 while (true) {
403 int retval;
404 ipc_call_t call;
405 ipc_callid_t callid = async_get_call(&call);
[2f57690]406
[79ae36dd]407 if (!IPC_GET_IMETHOD(call))
[86e3d62]408 exit(0);
[79ae36dd]409
410 switch (IPC_GET_IMETHOD(call)) {
[47e0a05b]411 case LOADER_GET_TASKID:
[bbdbf86]412 ldr_get_taskid(callid, &call);
[47e0a05b]413 continue;
[622cdbe]414 case LOADER_SET_CWD:
415 ldr_set_cwd(callid, &call);
416 continue;
[c98e6ee]417 case LOADER_SET_PATHNAME:
[bbdbf86]418 ldr_set_pathname(callid, &call);
[c98e6ee]419 continue;
420 case LOADER_SET_ARGS:
[bbdbf86]421 ldr_set_args(callid, &call);
422 continue;
423 case LOADER_SET_FILES:
424 ldr_set_files(callid, &call);
[4470e26]425 continue;
426 case LOADER_LOAD:
[bbdbf86]427 ldr_load(callid, &call);
[4470e26]428 continue;
[c98e6ee]429 case LOADER_RUN:
[bbdbf86]430 ldr_run(callid, &call);
[4470e26]431 /* Not reached */
[c98e6ee]432 default:
[8c3bc75]433 retval = EINVAL;
[c98e6ee]434 break;
435 }
[8c3bc75]436
[79ae36dd]437 async_answer_0(callid, retval);
[c98e6ee]438 }
439}
440
441/** Program loader main function.
442 */
443int main(int argc, char *argv[])
444{
[007e6efa]445 /* Set a handler of incomming connections. */
446 async_set_client_connection(ldr_connection);
447
[5d96851b]448 /* Introduce this task to the NS (give it our task ID). */
[007e6efa]449 task_id_t id = task_get_id();
[79ae36dd]450 int rc = ns_intro(id);
[5d96851b]451 if (rc != EOK)
[b39b5cb]452 return rc;
[2f57690]453
[bfd1546]454 /* Register at naming service. */
[b39b5cb]455 rc = service_register(SERVICE_LOAD);
456 if (rc != EOK)
457 return rc;
[007e6efa]458
[c98e6ee]459 async_manager();
[2f57690]460
[bfd1546]461 /* Never reached */
[c98e6ee]462 return 0;
463}
464
465/** @}
466 */
Note: See TracBrowser for help on using the repository browser.