xref: /haiku/src/system/runtime_loader/runtime_loader.cpp (revision 508f54795f39c3e7552d87c95aae9dd8ec6f505b)
1 /*
2  * Copyright 2005-2009, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  *
5  * Copyright 2002, Manuel J. Petit. All rights reserved.
6  * Distributed under the terms of the NewOS License.
7  */
8 
9 
10 #include "runtime_loader_private.h"
11 
12 #include <elf32.h>
13 #include <syscalls.h>
14 #include <user_runtime.h>
15 
16 #include <string.h>
17 #include <stdlib.h>
18 #include <sys/stat.h>
19 
20 #include <algorithm>
21 
22 
23 struct user_space_program_args *gProgramArgs;
24 
25 
26 static const char *
27 search_path_for_type(image_type type)
28 {
29 	const char *path = NULL;
30 
31 	switch (type) {
32 		case B_APP_IMAGE:
33 			path = getenv("PATH");
34 			break;
35 		case B_LIBRARY_IMAGE:
36 			path = getenv("LIBRARY_PATH");
37 			break;
38 		case B_ADD_ON_IMAGE:
39 			path = getenv("ADDON_PATH");
40 			break;
41 
42 		default:
43 			return NULL;
44 	}
45 
46 	if (path != NULL)
47 		return path;
48 
49 	// The environment variables may not have been set yet - in that case,
50 	// we're returning some useful defaults.
51 	// Since the kernel does not set any variables, this is also needed
52 	// to start the root shell.
53 
54 	switch (type) {
55 		case B_APP_IMAGE:
56 			return "/boot/home/config/bin"
57 						// TODO: Remove!
58 				":/boot/common/bin"
59 				":/bin"
60 				":/boot/apps"
61 				":/boot/preferences"
62 				":/boot/system/apps"
63 				":/boot/system/preferences"
64 				":/boot/develop/tools/gnupro/bin";
65 
66 		case B_LIBRARY_IMAGE:
67 			return "%A/lib"
68 				":/boot/home/config/lib"
69 					// TODO: Remove!
70 				":/boot/common/lib:/boot/system/lib";
71 
72 		case B_ADD_ON_IMAGE:
73 			return "%A/add-ons"
74 				":/boot/home/config/add-ons"
75 					// TODO: Remove!
76 				":/boot/common/add-ons"
77 				":/boot/system/add-ons";
78 
79 		default:
80 			return NULL;
81 	}
82 }
83 
84 
85 static int
86 try_open_executable(const char *dir, int dirLength, const char *name,
87 	const char *programPath, const char *compatibilitySubDir, char *path,
88 	size_t pathLength)
89 {
90 	size_t nameLength = strlen(name);
91 	struct stat stat;
92 	status_t status;
93 
94 	// construct the path
95 	if (dirLength > 0) {
96 		char *buffer = path;
97 		size_t subDirLen = 0;
98 
99 		if (programPath == NULL)
100 			programPath = gProgramArgs->program_path;
101 
102 		if (dirLength >= 2 && strncmp(dir, "%A", 2) == 0) {
103 			// Replace %A with current app folder path (of course,
104 			// this must be the first part of the path)
105 			char *lastSlash = strrchr(programPath, '/');
106 			int bytesCopied;
107 
108 			// copy what's left (when the application name is removed)
109 			if (lastSlash != NULL) {
110 				strlcpy(buffer, programPath,
111 					std::min((long)pathLength, lastSlash + 1 - programPath));
112 			} else
113 				strlcpy(buffer, ".", pathLength);
114 
115 			bytesCopied = strlen(buffer);
116 			buffer += bytesCopied;
117 			pathLength -= bytesCopied;
118 			dir += 2;
119 			dirLength -= 2;
120 		} else if (compatibilitySubDir != NULL) {
121 			// We're looking for a library or an add-on and the executable has
122 			// not been compiled with a compiler compatible with the one the
123 			// OS has been built with. Thus we only look in specific subdirs.
124 			subDirLen = strlen(compatibilitySubDir) + 1;
125 		}
126 
127 		if (dirLength + 1 + subDirLen + nameLength >= pathLength)
128 			return B_NAME_TOO_LONG;
129 
130 		memcpy(buffer, dir, dirLength);
131 		buffer[dirLength] = '/';
132 		if (subDirLen > 0) {
133 			memcpy(buffer + dirLength + 1, compatibilitySubDir, subDirLen - 1);
134 			buffer[dirLength + subDirLen] = '/';
135 		}
136 		strcpy(buffer + dirLength + 1 + subDirLen, name);
137 	} else {
138 		if (nameLength >= pathLength)
139 			return B_NAME_TOO_LONG;
140 
141 		strcpy(path + dirLength + 1, name);
142 	}
143 
144 	TRACE(("runtime_loader: try_open_container(): %s\n", path));
145 
146 	// Test if the target is a symbolic link, and correct the path in this case
147 
148 	status = _kern_read_stat(-1, path, false, &stat, sizeof(struct stat));
149 	if (status < B_OK)
150 		return status;
151 
152 	if (S_ISLNK(stat.st_mode)) {
153 		char buffer[PATH_MAX];
154 		size_t length = PATH_MAX - 1;
155 		char *lastSlash;
156 
157 		// it's a link, indeed
158 		status = _kern_read_link(-1, path, buffer, &length);
159 		if (status < B_OK)
160 			return status;
161 		buffer[length] = '\0';
162 
163 		lastSlash = strrchr(path, '/');
164 		if (buffer[0] != '/' && lastSlash != NULL) {
165 			// relative path
166 			strlcpy(lastSlash + 1, buffer, lastSlash + 1 - path + pathLength);
167 		} else
168 			strlcpy(path, buffer, pathLength);
169 	}
170 
171 	return _kern_open(-1, path, O_RDONLY, 0);
172 }
173 
174 
175 static int
176 search_executable_in_path_list(const char *name, const char *pathList,
177 	int pathListLen, const char *programPath, const char *compatibilitySubDir,
178 	char *pathBuffer, size_t pathBufferLength)
179 {
180 	const char *pathListEnd = pathList + pathListLen;
181 	status_t status = B_ENTRY_NOT_FOUND;
182 
183 	TRACE(("runtime_loader: search_container_in_path_list() %s in %.*s\n", name,
184 		pathListLen, pathList));
185 
186 	while (pathListLen > 0) {
187 		const char *pathEnd = pathList;
188 		int fd;
189 
190 		// find the next ':' or run till the end of the string
191 		while (pathEnd < pathListEnd && *pathEnd != ':')
192 			pathEnd++;
193 
194 		fd = try_open_executable(pathList, pathEnd - pathList, name,
195 			programPath, compatibilitySubDir, pathBuffer, pathBufferLength);
196 		if (fd >= 0) {
197 			// see if it's a dir
198 			struct stat stat;
199 			status = _kern_read_stat(fd, NULL, true, &stat, sizeof(struct stat));
200 			if (status == B_OK) {
201 				if (!S_ISDIR(stat.st_mode))
202 					return fd;
203 				status = B_IS_A_DIRECTORY;
204 			}
205 			_kern_close(fd);
206 		}
207 
208 		pathListLen = pathListEnd - pathEnd - 1;
209 		pathList = pathEnd + 1;
210 	}
211 
212 	return status;
213 }
214 
215 
216 int
217 open_executable(char *name, image_type type, const char *rpath,
218 	const char *programPath, const char *compatibilitySubDir)
219 {
220 	const char *paths;
221 	char buffer[PATH_MAX];
222 	int fd = B_ENTRY_NOT_FOUND;
223 
224 	if (strchr(name, '/')) {
225 		// the name already contains a path, we don't have to search for it
226 		fd = _kern_open(-1, name, O_RDONLY, 0);
227 		if (fd >= 0 || type != B_LIBRARY_IMAGE)
228 			return fd;
229 
230 		// Even though ELF specs don't say this, we give shared libraries
231 		// another chance and look them up in the usual search paths - at
232 		// least that seems to be what BeOS does, and since it doesn't hurt...
233 		paths = strrchr(name, '/') + 1;
234 		memmove(name, paths, strlen(paths) + 1);
235 	}
236 
237 	// let's evaluate the system path variables to find the container
238 	paths = search_path_for_type(type);
239 	if (paths) {
240 		fd = search_executable_in_path_list(name, paths, strlen(paths),
241 			programPath, compatibilitySubDir, buffer, sizeof(buffer));
242 
243 		// If not found and a compatibility sub directory has been
244 		// specified, look again in the standard search paths.
245 		if (fd == B_ENTRY_NOT_FOUND && compatibilitySubDir != NULL) {
246 			fd = search_executable_in_path_list(name, paths, strlen(paths),
247 				programPath, NULL, buffer, sizeof(buffer));
248 		}
249 	}
250 
251 	// try rpath (DT_RPATH), if not found yet
252 	if (fd < 0 && rpath != NULL) {
253 		// It consists of a colon-separated search path list. Optionally it
254 		// follows a second search path list, separated from the first by a
255 		// semicolon.
256 		const char *semicolon = strchr(rpath, ';');
257 		const char *firstList = (semicolon ? rpath : NULL);
258 		const char *secondList = (semicolon ? semicolon + 1 : rpath);
259 			// If there is no ';', we set only secondList to simplify things.
260 		if (firstList) {
261 			fd = search_executable_in_path_list(name, firstList,
262 				semicolon - firstList, programPath, NULL, buffer,
263 				sizeof(buffer));
264 		}
265 		if (fd < 0) {
266 			fd = search_executable_in_path_list(name, secondList,
267 				strlen(secondList), programPath, NULL, buffer, sizeof(buffer));
268 		}
269 	}
270 
271 	if (fd >= 0) {
272 		// we found it, copy path!
273 		TRACE(("runtime_loader: open_executable(%s): found at %s\n", name, buffer));
274 		strlcpy(name, buffer, PATH_MAX);
275 	}
276 
277 	return fd;
278 }
279 
280 
281 /*!
282 	Tests if there is an executable file at the provided path. It will
283 	also test if the file has a valid ELF header or is a shell script.
284 	Even if the runtime loader does not need to be able to deal with
285 	both types, the caller will give scripts a proper treatment.
286 */
287 status_t
288 test_executable(const char *name, char *invoker)
289 {
290 	char path[B_PATH_NAME_LENGTH];
291 	char buffer[B_FILE_NAME_LENGTH];
292 		// must be large enough to hold the ELF header
293 	status_t status;
294 	ssize_t length;
295 	int fd;
296 
297 	if (name == NULL)
298 		return B_BAD_VALUE;
299 
300 	strlcpy(path, name, sizeof(path));
301 
302 	fd = open_executable(path, B_APP_IMAGE, NULL, NULL, NULL);
303 	if (fd < B_OK)
304 		return fd;
305 
306 	// see if it's executable at all
307 	status = _kern_access(-1, path, X_OK, false);
308 	if (status != B_OK)
309 		goto out;
310 
311 	// read and verify the ELF header
312 
313 	length = _kern_read(fd, 0, buffer, sizeof(buffer));
314 	if (length < 0) {
315 		status = length;
316 		goto out;
317 	}
318 
319 	status = elf_verify_header(buffer, length);
320 	if (status == B_NOT_AN_EXECUTABLE) {
321 		// test for shell scripts
322 		if (!strncmp(buffer, "#!", 2)) {
323 			char *end;
324 			buffer[min_c((size_t)length, sizeof(buffer) - 1)] = '\0';
325 
326 			end = strchr(buffer, '\n');
327 			if (end == NULL) {
328 				status = E2BIG;
329 				goto out;
330 			} else
331 				end[0] = '\0';
332 
333 			if (invoker)
334 				strcpy(invoker, buffer + 2);
335 
336 			status = B_OK;
337 		}
338 	} else if (status == B_OK) {
339 		struct Elf32_Ehdr *elfHeader = (struct Elf32_Ehdr *)buffer;
340 		if (elfHeader->e_entry == 0) {
341 			// we don't like to open shared libraries
342 			status = B_NOT_AN_EXECUTABLE;
343 		} else if (invoker)
344 			invoker[0] = '\0';
345 	}
346 
347 out:
348 	_kern_close(fd);
349 	return status;
350 }
351 
352 
353 /*!
354 	This is the main entry point of the runtime loader as
355 	specified by its ld-script.
356 */
357 int
358 runtime_loader(void *_args)
359 {
360 	void *entry = NULL;
361 	int returnCode;
362 
363 	gProgramArgs = (struct user_space_program_args *)_args;
364 
365 	// Relocate the args and env arrays -- they are organized in a contiguous
366 	// buffer which the kernel just copied into user space without adjusting the
367 	// pointers.
368 	{
369 		int32 i;
370 		addr_t relocationOffset = 0;
371 
372 		if (gProgramArgs->arg_count > 0)
373 			relocationOffset = (addr_t)gProgramArgs->args[0];
374 		else if (gProgramArgs->env_count > 0)
375 			relocationOffset = (addr_t)gProgramArgs->env[0];
376 
377 		// That's basically: <new buffer address> - <old buffer address>.
378 		// It looks a little complicated, since we don't have the latter one at
379 		// hand and thus need to reconstruct it (<first string pointer> -
380 		// <arguments + environment array sizes>).
381 		relocationOffset = (addr_t)gProgramArgs->args - relocationOffset
382 			+ (gProgramArgs->arg_count + gProgramArgs->env_count + 2)
383 				* sizeof(char*);
384 
385 		for (i = 0; i < gProgramArgs->arg_count; i++)
386 			gProgramArgs->args[i] += relocationOffset;
387 
388 		for (i = 0; i < gProgramArgs->env_count; i++)
389 			gProgramArgs->env[i] += relocationOffset;
390 	}
391 
392 	if (!strcmp(gProgramArgs->program_path, "/boot/system/runtime_loader")) {
393 		// TODO: this is a (temporary) work-around for bug #2273 which causes
394 		// the cache's mutex to be locked twice when starting the runtime_loader
395 		// itself.
396 		return 1;
397 	}
398 
399 #if DEBUG_RLD
400 	close(0); open("/dev/console", 0); /* stdin   */
401 	close(1); open("/dev/console", 0); /* stdout  */
402 	close(2); open("/dev/console", 0); /* stderr  */
403 #endif
404 
405 	if (heap_init() < B_OK)
406 		return 1;
407 
408 	rldexport_init();
409 	rldelf_init();
410 
411 	load_program(gProgramArgs->program_path, &entry);
412 
413 	if (entry == NULL)
414 		return -1;
415 
416 	// call the program entry point (usually _start())
417 	returnCode = ((int (*)(int, void *, void *))entry)(gProgramArgs->arg_count,
418 		gProgramArgs->args, gProgramArgs->env);
419 
420 	terminate_program();
421 
422 	return returnCode;
423 }
424