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

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

Merge program-loader related stuff from dynload branch to trunk. (huge)

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