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