source: mainline/uspace/srv/loader/main.c@ 1ea99cc

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

Merge changes from original Subversion dynload branch.

  • Property mode set to 100644
File size: 10.0 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/ipc.h>
53#include <ipc/services.h>
54#include <ipc/loader.h>
55#include <ipc/ns.h>
56#include <macros.h>
57#include <loader/pcb.h>
58#include <errno.h>
59#include <async.h>
60#include <string.h>
61#include <as.h>
62
63#include <elf.h>
64#include <elf_load.h>
65
66#define DPRINTF(...)
67
68void program_run(void *entry, pcb_t *pcb);
69
70/** Pathname of the file that will be loaded */
71static char *pathname = NULL;
72
73/** The Program control block */
74static pcb_t pcb;
75
76/** Number of arguments */
77static int argc = 0;
78/** Argument vector */
79static char **argv = NULL;
80/** Buffer holding all arguments */
81static char *arg_buf = NULL;
82
83/** Number of preset files */
84static int filc = 0;
85/** Preset files vector */
86static fdi_node_t **filv = NULL;
87/** Buffer holding all preset files */
88static fdi_node_t *fil_buf = NULL;
89
90static elf_info_t prog_info;
91static elf_info_t interp_info;
92
93static bool is_dyn_linked;
94
95/** Used to limit number of connections to one. */
96static bool connected;
97
98static void ldr_get_taskid(ipc_callid_t rid, ipc_call_t *request)
99{
100 ipc_callid_t callid;
101 task_id_t task_id;
102 size_t len;
103
104 task_id = task_get_id();
105
106 if (!ipc_data_read_receive(&callid, &len)) {
107 ipc_answer_0(callid, EINVAL);
108 ipc_answer_0(rid, EINVAL);
109 return;
110 }
111
112 if (len > sizeof(task_id))
113 len = sizeof(task_id);
114
115 ipc_data_read_finalize(callid, &task_id, len);
116 ipc_answer_0(rid, EOK);
117}
118
119
120/** Receive a call setting pathname of the program to execute.
121 *
122 * @param rid
123 * @param request
124 */
125static void ldr_set_pathname(ipc_callid_t rid, ipc_call_t *request)
126{
127 ipc_callid_t callid;
128 size_t len;
129 char *name_buf;
130
131 if (!ipc_data_write_receive(&callid, &len)) {
132 ipc_answer_0(callid, EINVAL);
133 ipc_answer_0(rid, EINVAL);
134 return;
135 }
136
137 name_buf = malloc(len + 1);
138 if (!name_buf) {
139 ipc_answer_0(callid, ENOMEM);
140 ipc_answer_0(rid, ENOMEM);
141 return;
142 }
143
144 ipc_data_write_finalize(callid, name_buf, len);
145 ipc_answer_0(rid, EOK);
146
147 if (pathname != NULL) {
148 free(pathname);
149 pathname = NULL;
150 }
151
152 name_buf[len] = '\0';
153 pathname = name_buf;
154}
155
156/** Receive a call setting arguments of the program to execute.
157 *
158 * @param rid
159 * @param request
160 */
161static void ldr_set_args(ipc_callid_t rid, ipc_call_t *request)
162{
163 ipc_callid_t callid;
164 size_t buf_size, arg_size;
165 char *p;
166 int n;
167
168 if (!ipc_data_write_receive(&callid, &buf_size)) {
169 ipc_answer_0(callid, EINVAL);
170 ipc_answer_0(rid, EINVAL);
171 return;
172 }
173
174 if (arg_buf != NULL) {
175 free(arg_buf);
176 arg_buf = NULL;
177 }
178
179 if (argv != NULL) {
180 free(argv);
181 argv = NULL;
182 }
183
184 arg_buf = malloc(buf_size + 1);
185 if (!arg_buf) {
186 ipc_answer_0(callid, ENOMEM);
187 ipc_answer_0(rid, ENOMEM);
188 return;
189 }
190
191 ipc_data_write_finalize(callid, arg_buf, buf_size);
192
193 arg_buf[buf_size] = '\0';
194
195 /*
196 * Count number of arguments
197 */
198 p = arg_buf;
199 n = 0;
200 while (p < arg_buf + buf_size) {
201 arg_size = str_size(p);
202 p = p + arg_size + 1;
203 ++n;
204 }
205
206 /* Allocate argv */
207 argv = malloc((n + 1) * sizeof(char *));
208
209 if (argv == NULL) {
210 free(arg_buf);
211 ipc_answer_0(rid, ENOMEM);
212 return;
213 }
214
215 /*
216 * Fill argv with argument pointers
217 */
218 p = arg_buf;
219 n = 0;
220 while (p < arg_buf + buf_size) {
221 argv[n] = p;
222
223 arg_size = str_size(p);
224 p = p + arg_size + 1;
225 ++n;
226 }
227
228 argc = n;
229 argv[n] = NULL;
230
231 ipc_answer_0(rid, EOK);
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 ipc_callid_t callid;
242 size_t buf_size;
243 if (!ipc_data_write_receive(&callid, &buf_size)) {
244 ipc_answer_0(callid, EINVAL);
245 ipc_answer_0(rid, EINVAL);
246 return;
247 }
248
249 if ((buf_size % sizeof(fdi_node_t)) != 0) {
250 ipc_answer_0(callid, EINVAL);
251 ipc_answer_0(rid, EINVAL);
252 return;
253 }
254
255 if (fil_buf != NULL) {
256 free(fil_buf);
257 fil_buf = NULL;
258 }
259
260 if (filv != NULL) {
261 free(filv);
262 filv = NULL;
263 }
264
265 fil_buf = malloc(buf_size);
266 if (!fil_buf) {
267 ipc_answer_0(callid, ENOMEM);
268 ipc_answer_0(rid, ENOMEM);
269 return;
270 }
271
272 ipc_data_write_finalize(callid, fil_buf, buf_size);
273
274 int count = buf_size / sizeof(fdi_node_t);
275
276 /* Allocate filvv */
277 filv = malloc((count + 1) * sizeof(fdi_node_t *));
278
279 if (filv == NULL) {
280 free(fil_buf);
281 ipc_answer_0(rid, ENOMEM);
282 return;
283 }
284
285 /*
286 * Fill filv with argument pointers
287 */
288 int i;
289 for (i = 0; i < count; i++)
290 filv[i] = &fil_buf[i];
291
292 filc = count;
293 filv[count] = NULL;
294
295 ipc_answer_0(rid, EOK);
296}
297
298/** Load the previously selected program.
299 *
300 * @param rid
301 * @param request
302 * @return 0 on success, !0 on error.
303 */
304static int ldr_load(ipc_callid_t rid, ipc_call_t *request)
305{
306 int rc;
307
308 rc = elf_load_file(pathname, 0, 0, &prog_info);
309 if (rc != EE_OK) {
310 DPRINTF("Failed to load executable '%s'.\n", pathname);
311 ipc_answer_0(rid, EINVAL);
312 return 1;
313 }
314
315 elf_create_pcb(&prog_info, &pcb);
316
317 pcb.argc = argc;
318 pcb.argv = argv;
319
320 pcb.filc = filc;
321 pcb.filv = filv;
322
323 if (prog_info.interp == NULL) {
324 /* Statically linked program */
325 is_dyn_linked = false;
326 ipc_answer_0(rid, EOK);
327 return 0;
328 }
329
330 printf("Load ELF interpreter '%s'\n", prog_info.interp);
331 rc = elf_load_file(prog_info.interp, 0, 0, &interp_info);
332 if (rc != EE_OK) {
333 DPRINTF("Failed to load interpreter '%s.'\n",
334 prog_info.interp);
335 ipc_answer_0(rid, EINVAL);
336 return 1;
337 }
338
339 printf("Run interpreter.\n");
340 printf("entry point: 0x%lx\n", interp_info.entry);
341 printf("pcb address: 0x%lx\n", &pcb);
342 printf("prog dynamic: 0x%lx\n", prog_info.dynamic);
343
344 is_dyn_linked = true;
345 ipc_answer_0(rid, EOK);
346
347 return 0;
348}
349
350
351/** Run the previously loaded program.
352 *
353 * @param rid
354 * @param request
355 * @return 0 on success, !0 on error.
356 */
357static void ldr_run(ipc_callid_t rid, ipc_call_t *request)
358{
359 const char *cp;
360
361 /* Set the task name. */
362 cp = str_rchr(pathname, '/');
363 cp = (cp == NULL) ? pathname : (cp + 1);
364 task_set_name(cp);
365
366 if (is_dyn_linked == true) {
367 /* Dynamically linked program */
368 DPRINTF("Run ELF interpreter.\n");
369 DPRINTF("Entry point: 0x%lx\n", interp_info.entry);
370
371 ipc_answer_0(rid, EOK);
372 program_run(interp_info.entry, &pcb);
373 } else {
374 /* Statically linked program */
375 ipc_answer_0(rid, EOK);
376 program_run(prog_info.entry, &pcb);
377 }
378
379 /* Not reached */
380}
381
382/** Handle loader connection.
383 *
384 * Receive and carry out commands (of which the last one should be
385 * to execute the loaded program).
386 */
387static void ldr_connection(ipc_callid_t iid, ipc_call_t *icall)
388{
389 ipc_callid_t callid;
390 ipc_call_t call;
391 int retval;
392
393 /* Already have a connection? */
394 if (connected) {
395 ipc_answer_0(iid, ELIMIT);
396 return;
397 }
398
399 connected = true;
400
401 /* Accept the connection */
402 ipc_answer_0(iid, EOK);
403
404 /* Ignore parameters, the connection is already open */
405 (void) iid;
406 (void) icall;
407
408 while (1) {
409 callid = async_get_call(&call);
410
411 switch (IPC_GET_METHOD(call)) {
412 case IPC_M_PHONE_HUNGUP:
413 exit(0);
414 case LOADER_GET_TASKID:
415 ldr_get_taskid(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 = ENOENT;
434 break;
435 }
436 if ((callid & IPC_CALLID_NOTIFICATION) == 0 &&
437 IPC_GET_METHOD(call) != IPC_M_PHONE_HUNGUP) {
438 DPRINTF("Responding EINVAL to method %d.\n",
439 IPC_GET_METHOD(call));
440 ipc_answer_0(callid, EINVAL);
441 }
442 }
443}
444
445/** Program loader main function.
446 */
447int main(int argc, char *argv[])
448{
449 ipcarg_t phonead;
450 task_id_t id;
451 int rc;
452
453 connected = false;
454
455 /* Introduce this task to the NS (give it our task ID). */
456 id = task_get_id();
457 rc = async_req_2_0(PHONE_NS, NS_ID_INTRO, LOWER32(id), UPPER32(id));
458 if (rc != EOK)
459 return -1;
460
461 /* Set a handler of incomming connections. */
462 async_set_client_connection(ldr_connection);
463
464 /* Register at naming service. */
465 if (ipc_connect_to_me(PHONE_NS, SERVICE_LOAD, 0, 0, &phonead) != 0)
466 return -2;
467
468 async_manager();
469
470 /* Never reached */
471 return 0;
472}
473
474/** @}
475 */
Note: See TracBrowser for help on using the repository browser.