source: mainline/uspace/srv/loader/main.c@ 27b76ca

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 27b76ca was 27b76ca, checked in by Jakub Jermar <jakub@…>, 14 years ago

Synchronize VFS handle acceptor with VFS.

  • Introduce VFS_IN_WAIT_HANDLE.
  • Add vfs_wait_handle() to VFS.
  • Add fd_wait() to libc.
  • Use fd_wait() in loader.
  • Property mode set to 100644
File size: 10.1 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#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_client_connection(ldr_connection);
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 -1;
453
454 /* Register at naming service. */
455 if (service_register(SERVICE_LOAD) != EOK)
456 return -2;
457
458 async_manager();
459
460 /* Never reached */
461 return 0;
462}
463
464/** @}
465 */
Note: See TracBrowser for help on using the repository browser.