source: mainline/uspace/srv/loader/main.c@ 47e0a05b

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 47e0a05b was 47e0a05b, checked in by Jiri Svoboda <jirik.svoboda@…>, 17 years ago

Allow trace to run programs and trace them (no more task IDs)

  • Property mode set to 100644
File size: 7.9 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 <fcntl.h>
50#include <sys/types.h>
51#include <ipc/ipc.h>
52#include <ipc/loader.h>
53#include <loader/pcb.h>
54#include <errno.h>
55#include <async.h>
56#include <as.h>
57
58#include <elf.h>
59#include <elf_load.h>
60
61/**
62 * Bias used for loading the dynamic linker. This will be soon replaced
63 * by automatic placement.
64 */
65#define RTLD_BIAS 0x80000
66
67/** Pathname of the file that will be loaded */
68static char *pathname = NULL;
69
70/** The Program control block */
71static pcb_t pcb;
72
73/** Number of arguments */
74static int argc = 0;
75/** Argument vector */
76static char **argv = NULL;
77/** Buffer holding all arguments */
78static char *arg_buf = NULL;
79
80static int loader_get_taskid(ipc_callid_t rid, ipc_call_t *request)
81{
82 ipc_callid_t callid;
83 task_id_t task_id;
84 size_t len;
85
86 task_id = task_get_id();
87
88 if (!ipc_data_read_receive(&callid, &len)) {
89 ipc_answer_0(callid, EINVAL);
90 ipc_answer_0(rid, EINVAL);
91 return;
92 }
93
94 if (len > sizeof(task_id)) len = sizeof(task_id);
95
96 ipc_data_write_finalize(callid, &task_id, len);
97 ipc_answer_0(rid, EOK);
98}
99
100
101/** Receive a call setting pathname of the program to execute.
102 *
103 * @param rid
104 * @param request
105 */
106static void loader_set_pathname(ipc_callid_t rid, ipc_call_t *request)
107{
108 ipc_callid_t callid;
109 size_t len;
110 char *name_buf;
111
112 if (!ipc_data_write_receive(&callid, &len)) {
113 ipc_answer_0(callid, EINVAL);
114 ipc_answer_0(rid, EINVAL);
115 return;
116 }
117
118 name_buf = malloc(len + 1);
119 if (!name_buf) {
120 ipc_answer_0(callid, ENOMEM);
121 ipc_answer_0(rid, ENOMEM);
122 return;
123 }
124
125 ipc_data_write_finalize(callid, name_buf, len);
126 ipc_answer_0(rid, EOK);
127
128 if (pathname != NULL) {
129 free(pathname);
130 pathname = NULL;
131 }
132
133 name_buf[len] = '\0';
134 pathname = name_buf;
135}
136
137/** Receive a call setting arguments of the program to execute.
138 *
139 * @param rid
140 * @param request
141 */
142static void loader_set_args(ipc_callid_t rid, ipc_call_t *request)
143{
144 ipc_callid_t callid;
145 size_t buf_len, arg_len;
146 char *p;
147 int n;
148
149 if (!ipc_data_write_receive(&callid, &buf_len)) {
150 ipc_answer_0(callid, EINVAL);
151 ipc_answer_0(rid, EINVAL);
152 return;
153 }
154
155 if (arg_buf != NULL) {
156 free(arg_buf);
157 arg_buf = NULL;
158 }
159
160 if (argv != NULL) {
161 free(argv);
162 argv = NULL;
163 }
164
165 arg_buf = malloc(buf_len + 1);
166 if (!arg_buf) {
167 ipc_answer_0(callid, ENOMEM);
168 ipc_answer_0(rid, ENOMEM);
169 return;
170 }
171
172 ipc_data_write_finalize(callid, arg_buf, buf_len);
173 ipc_answer_0(rid, EOK);
174
175 arg_buf[buf_len] = '\0';
176
177 /*
178 * Count number of arguments
179 */
180 p = arg_buf;
181 n = 0;
182 while (p < arg_buf + buf_len) {
183 arg_len = strlen(p);
184 p = p + arg_len + 1;
185 ++n;
186 }
187
188 /* Allocate argv */
189 argv = malloc((n + 1) * sizeof(char *));
190
191 if (argv == NULL) {
192 free(arg_buf);
193 ipc_answer_0(callid, ENOMEM);
194 ipc_answer_0(rid, ENOMEM);
195 return;
196 }
197
198 /*
199 * Fill argv with argument pointers
200 */
201 p = arg_buf;
202 n = 0;
203 while (p < arg_buf + buf_len) {
204 argv[n] = p;
205
206 arg_len = strlen(p);
207 p = p + arg_len + 1;
208 ++n;
209 }
210
211 argc = n;
212 argv[n] = NULL;
213}
214
215
216/** Load and run the previously selected program.
217 *
218 * @param rid
219 * @param request
220 * @return 0 on success, !0 on error.
221 */
222static int loader_run(ipc_callid_t rid, ipc_call_t *request)
223{
224 int rc;
225
226 elf_info_t prog_info;
227 elf_info_t interp_info;
228
229// printf("Load program '%s'\n", pathname);
230
231 rc = elf_load_file(pathname, 0, &prog_info);
232 if (rc < 0) {
233 printf("failed to load program\n");
234 ipc_answer_0(rid, EINVAL);
235 return 1;
236 }
237
238// printf("Create PCB\n");
239 elf_create_pcb(&prog_info, &pcb);
240
241 pcb.argc = argc;
242 pcb.argv = argv;
243
244 if (prog_info.interp == NULL) {
245 /* Statically linked program */
246// printf("Run statically linked program\n");
247// printf("entry point: 0x%llx\n", prog_info.entry);
248 ipc_answer_0(rid, EOK);
249 close_console();
250 elf_run(&prog_info, &pcb);
251 return 0;
252 }
253
254 printf("Load dynamic linker '%s'\n", prog_info.interp);
255 rc = elf_load_file("/rtld.so", RTLD_BIAS, &interp_info);
256 if (rc < 0) {
257 printf("failed to load dynamic linker\n");
258 ipc_answer_0(rid, EINVAL);
259 return 1;
260 }
261
262 /*
263 * Provide dynamic linker with some useful data
264 */
265 pcb.rtld_dynamic = interp_info.dynamic;
266 pcb.rtld_bias = RTLD_BIAS;
267
268 printf("run dynamic linker\n");
269 printf("entry point: 0x%llx\n", interp_info.entry);
270 close_console();
271
272 ipc_answer_0(rid, EOK);
273 elf_run(&interp_info, &pcb);
274
275 /* Not reached */
276 return 0;
277}
278
279/** Handle loader connection.
280 *
281 * Receive and carry out commands (of which the last one should be
282 * to execute the loaded program).
283 */
284static void loader_connection(ipc_callid_t iid, ipc_call_t *icall)
285{
286 ipc_callid_t callid;
287 ipc_call_t call;
288 int retval;
289
290 /* Ignore parameters, the connection is already open */
291 (void)iid; (void)icall;
292
293 while (1) {
294 callid = async_get_call(&call);
295// printf("received call from phone %d, method=%d\n",
296// call.in_phone_hash, IPC_GET_METHOD(call));
297 switch (IPC_GET_METHOD(call)) {
298 case LOADER_GET_TASKID:
299 loader_get_taskid(callid, &call);
300 continue;
301 case LOADER_SET_PATHNAME:
302 loader_set_pathname(callid, &call);
303 continue;
304 case LOADER_SET_ARGS:
305 loader_set_args(callid, &call);
306 case LOADER_RUN:
307 loader_run(callid, &call);
308 exit(0);
309 continue;
310 default:
311 retval = ENOENT;
312 break;
313 }
314 if ((callid & IPC_CALLID_NOTIFICATION) == 0 &&
315 IPC_GET_METHOD(call) != IPC_M_PHONE_HUNGUP) {
316 printf("responding EINVAL to method %d\n",
317 IPC_GET_METHOD(call));
318 ipc_answer_0(callid, EINVAL);
319 }
320 }
321}
322
323/** Program loader main function.
324 */
325int main(int argc, char *argv[])
326{
327 ipc_callid_t callid;
328 ipc_call_t call;
329 ipcarg_t phone_hash;
330
331 /* The first call only communicates the incoming phone hash */
332 callid = ipc_wait_for_call(&call);
333
334 if (IPC_GET_METHOD(call) != LOADER_HELLO) {
335 if (IPC_GET_METHOD(call) != IPC_M_PHONE_HUNGUP)
336 ipc_answer_0(callid, EINVAL);
337 return 1;
338 }
339
340 ipc_answer_0(callid, EOK);
341 phone_hash = call.in_phone_hash;
342
343 /*
344 * Up until now async must not be used as it couldn't
345 * handle incoming requests. (Which means e.g. printf()
346 * cannot be used)
347 */
348 async_new_connection(phone_hash, 0, NULL, loader_connection);
349 async_manager();
350
351 /* not reached */
352 return 0;
353}
354
355/** @}
356 */
Note: See TracBrowser for help on using the repository browser.