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

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

Rework the way how open files are passed from parent task to child.

  • Instead of passing fdi_node_t's, pass handles directly using the IPC_M_STATE_CHANGE_AUTHORIZE mechanism.
  • Remove open_node(), fd_node(), fdi_node_t.
  • Replace fopen_node() with fopen_handle().
  • Replace fnode() with fhandle().
  • The child task does not synchronize with VFS yet.
  • 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
245 if (!async_state_change_receive(&callid, NULL, NULL, NULL)) {
246 async_answer_0(callid, EINVAL);
247 break;
248 }
249 async_state_change_finalize(callid, vfs_exch);
250 }
251
252 vfs_exchange_end(vfs_exch);
253
254 async_answer_0(rid, EOK);
255}
256
257/** Load the previously selected program.
258 *
259 * @param rid
260 * @param request
261 * @return 0 on success, !0 on error.
262 */
263static int ldr_load(ipc_callid_t rid, ipc_call_t *request)
264{
265 int rc;
266
267 rc = elf_load_file(pathname, 0, 0, &prog_info);
268 if (rc != EE_OK) {
269 DPRINTF("Failed to load executable '%s'.\n", pathname);
270 async_answer_0(rid, EINVAL);
271 return 1;
272 }
273
274 elf_create_pcb(&prog_info, &pcb);
275
276 pcb.cwd = cwd;
277
278 pcb.argc = argc;
279 pcb.argv = argv;
280
281 pcb.filc = filc;
282
283 if (prog_info.interp == NULL) {
284 /* Statically linked program */
285 async_answer_0(rid, EOK);
286 return 0;
287 }
288
289 DPRINTF("Binary is dynamically linked.\n");
290#ifdef CONFIG_RTLD
291 DPRINTF(" - pcb address: %p\n", &pcb);
292 DPRINTF( "- prog dynamic: %p\n", prog_info.dynamic);
293
294 rc = ldr_load_dyn_linked(&prog_info);
295#else
296 rc = ENOTSUP;
297#endif
298 async_answer_0(rid, rc);
299 return 0;
300}
301
302#ifdef CONFIG_RTLD
303
304static int ldr_load_dyn_linked(elf_info_t *p_info)
305{
306 runtime_env = &dload_re;
307
308 DPRINTF("Load dynamically linked program.\n");
309
310 /*
311 * First we need to process dynamic sections of the executable
312 * program and insert it into the module graph.
313 */
314
315 DPRINTF("Parse program .dynamic section at %p\n", p_info->dynamic);
316 dynamic_parse(p_info->dynamic, 0, &prog_mod.dyn);
317 prog_mod.bias = 0;
318 prog_mod.dyn.soname = "[program]";
319
320 /* Initialize list of loaded modules */
321 list_initialize(&runtime_env->modules);
322 list_append(&prog_mod.modules_link, &runtime_env->modules);
323
324 /* Pointer to program module. Used as root of the module graph. */
325 runtime_env->program = &prog_mod;
326
327 /* Work around non-existent memory space allocation. */
328 runtime_env->next_bias = 0x1000000;
329
330 /*
331 * Now we can continue with loading all other modules.
332 */
333
334 DPRINTF("Load all program dependencies\n");
335 module_load_deps(&prog_mod);
336
337 /*
338 * Now relocate/link all modules together.
339 */
340
341 /* Process relocations in all modules */
342 DPRINTF("Relocate all modules\n");
343 modules_process_relocs(&prog_mod);
344
345 /* Pass runtime evironment pointer through PCB. */
346 pcb.rtld_runtime = (void *) runtime_env;
347
348 return 0;
349}
350#endif
351
352/** Run the previously loaded program.
353 *
354 * @param rid
355 * @param request
356 * @return 0 on success, !0 on error.
357 */
358static void ldr_run(ipc_callid_t rid, ipc_call_t *request)
359{
360 const char *cp;
361
362 DPRINTF("Set task name\n");
363
364 /* Set the task name. */
365 cp = str_rchr(pathname, '/');
366 cp = (cp == NULL) ? pathname : (cp + 1);
367 task_set_name(cp);
368
369 /* Run program */
370 DPRINTF("Reply OK\n");
371 async_answer_0(rid, EOK);
372 DPRINTF("Jump to entry point at %p\n", pcb.entry);
373 entry_point_jmp(prog_info.entry, &pcb);
374
375 /* Not reached */
376}
377
378/** Handle loader connection.
379 *
380 * Receive and carry out commands (of which the last one should be
381 * to execute the loaded program).
382 */
383static void ldr_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
384{
385 /* Already have a connection? */
386 if (connected) {
387 async_answer_0(iid, ELIMIT);
388 return;
389 }
390
391 connected = true;
392
393 /* Accept the connection */
394 async_answer_0(iid, EOK);
395
396 /* Ignore parameters, the connection is already open */
397 (void) icall;
398
399 while (true) {
400 int retval;
401 ipc_call_t call;
402 ipc_callid_t callid = async_get_call(&call);
403
404 if (!IPC_GET_IMETHOD(call))
405 exit(0);
406
407 switch (IPC_GET_IMETHOD(call)) {
408 case LOADER_GET_TASKID:
409 ldr_get_taskid(callid, &call);
410 continue;
411 case LOADER_SET_CWD:
412 ldr_set_cwd(callid, &call);
413 continue;
414 case LOADER_SET_PATHNAME:
415 ldr_set_pathname(callid, &call);
416 continue;
417 case LOADER_SET_ARGS:
418 ldr_set_args(callid, &call);
419 continue;
420 case LOADER_SET_FILES:
421 ldr_set_files(callid, &call);
422 continue;
423 case LOADER_LOAD:
424 ldr_load(callid, &call);
425 continue;
426 case LOADER_RUN:
427 ldr_run(callid, &call);
428 /* Not reached */
429 default:
430 retval = EINVAL;
431 break;
432 }
433
434 async_answer_0(callid, retval);
435 }
436}
437
438/** Program loader main function.
439 */
440int main(int argc, char *argv[])
441{
442 /* Set a handler of incomming connections. */
443 async_set_client_connection(ldr_connection);
444
445 /* Introduce this task to the NS (give it our task ID). */
446 task_id_t id = task_get_id();
447 int rc = ns_intro(id);
448 if (rc != EOK)
449 return -1;
450
451 /* Register at naming service. */
452 if (service_register(SERVICE_LOAD) != EOK)
453 return -2;
454
455 async_manager();
456
457 /* Never reached */
458 return 0;
459}
460
461/** @}
462 */
Note: See TracBrowser for help on using the repository browser.