2317bd7ddSAxel Dörfler * Copyright 2005-2009, Axel D��rfler, axeld@pinc-software.de.
3cd6c2db4SAxel Dörfler * Distributed under the terms of the MIT License.
4cd6c2db4SAxel Dörfler *
5cd6c2db4SAxel Dörfler * Copyright 2002, Manuel J. Petit. All rights reserved.
6cd6c2db4SAxel Dörfler * Distributed under the terms of the NewOS License.
7cd6c2db4SAxel Dörfler */
9f351df08SAxel Dörfler
1076cd9b19SAxel Dörfler#include "runtime_loader_private.h"
1176cd9b19SAxel Dörfler
12be22452fSAxel Dörfler#include <string.h>
13be22452fSAxel Dörfler#include <stdlib.h>
14be22452fSAxel Dörfler#include <sys/stat.h>
15be22452fSAxel Dörfler
16003ebb0eSIngo Weinhold#include <algorithm>
17003ebb0eSIngo Weinhold
18cf7e2ad8SIngo Weinhold#include <ByteOrder.h>
19cf7e2ad8SIngo Weinhold
20cf7e2ad8SIngo Weinhold#include <directories.h>
21ea7e2601SJessica Hamilton#include <find_directory_private.h>
22cf7e2ad8SIngo Weinhold#include <image_defs.h>
23cf7e2ad8SIngo Weinhold#include <syscalls.h>
24cf7e2ad8SIngo Weinhold#include <user_runtime.h>
25cf7e2ad8SIngo Weinhold#include <vm_defs.h>
26cf7e2ad8SIngo Weinhold
27cf7e2ad8SIngo Weinhold#include "elf_symbol_lookup.h"
28aa4b5749SAlexander von Gluck IV#include "pe.h"
29cf7e2ad8SIngo Weinhold
30be22452fSAxel Dörfler
3174c0424aSAxel Dörflerstruct user_space_program_args *gProgramArgs;
32e85e399fSPawel Dziepakvoid *__gCommPageAddress;
339fc69d1bSJonathan Schleifervoid *__dso_handle;
34be22452fSAxel Dörfler
357e1c4534SPawel Dziepakint32 __gCPUCount = 1;
367e1c4534SPawel Dziepak
37ea7e2601SJessica Hamiltonconst directory_which kLibraryDirectories[] = {
38ea7e2601SJessica Hamilton	B_SYSTEM_LIB_DIRECTORY,
40ea7e2601SJessica Hamilton	B_USER_LIB_DIRECTORY,
42ea7e2601SJessica Hamilton};
43ea7e2601SJessica Hamilton
44be22452fSAxel Dörfler
45be22452fSAxel Dörflerstatic const char *
46be22452fSAxel Dörflersearch_path_for_type(image_type type)
47be22452fSAxel Dörfler{
48be22452fSAxel Dörfler	const char *path = NULL;
49be22452fSAxel Dörfler
5033f7f287SAugustin Cavalier	// If "user add-ons" are disabled via safemode settings, we bypass the
5133f7f287SAugustin Cavalier	// environment and defaults and return a different set of paths without
5233f7f287SAugustin Cavalier	// the user or non-packaged ones.
5333f7f287SAugustin Cavalier	if (gProgramArgs->disable_user_addons) {
5433f7f287SAugustin Cavalier		switch (type) {
5533f7f287SAugustin Cavalier			case B_APP_IMAGE:
5633f7f287SAugustin Cavalier				return kGlobalBinDirectory
5733f7f287SAugustin Cavalier					":" kSystemAppsDirectory
5833f7f287SAugustin Cavalier					":" kSystemPreferencesDirectory;
5933f7f287SAugustin Cavalier
6033f7f287SAugustin Cavalier			case B_LIBRARY_IMAGE:
6133f7f287SAugustin Cavalier				return kAppLocalLibDirectory
6233f7f287SAugustin Cavalier					":" kSystemLibDirectory;
6333f7f287SAugustin Cavalier
6433f7f287SAugustin Cavalier			case B_ADD_ON_IMAGE:
6533f7f287SAugustin Cavalier				return kAppLocalAddonsDirectory
6633f7f287SAugustin Cavalier					":" kSystemAddonsDirectory;
6733f7f287SAugustin Cavalier
6833f7f287SAugustin Cavalier			default:
6933f7f287SAugustin Cavalier				return NULL;
7033f7f287SAugustin Cavalier		}
7133f7f287SAugustin Cavalier	}
7233f7f287SAugustin Cavalier
7338fa81bfSIngo Weinhold	// TODO: The *PATH variables should not include the standard system paths.
7438fa81bfSIngo Weinhold	// Instead those paths should always be used after the directories specified
7538fa81bfSIngo Weinhold	// via the variables.
76be22452fSAxel Dörfler	switch (type) {
77be22452fSAxel Dörfler		case B_APP_IMAGE:
78be22452fSAxel Dörfler			path = getenv("PATH");
79be22452fSAxel Dörfler			break;
80be22452fSAxel Dörfler		case B_LIBRARY_IMAGE:
81be22452fSAxel Dörfler			path = getenv("LIBRARY_PATH");
82be22452fSAxel Dörfler			break;
83be22452fSAxel Dörfler		case B_ADD_ON_IMAGE:
84be22452fSAxel Dörfler			path = getenv("ADDON_PATH");
85be22452fSAxel Dörfler			break;
86be22452fSAxel Dörfler
87be22452fSAxel Dörfler		default:
88be22452fSAxel Dörfler			return NULL;
89be22452fSAxel Dörfler	}
90be22452fSAxel Dörfler
91be22452fSAxel Dörfler	if (path != NULL)
92be22452fSAxel Dörfler		return path;
93be22452fSAxel Dörfler
9493c20889SAxel Dörfler	// The environment variables may not have been set yet - in that case,
9593c20889SAxel Dörfler	// we're returning some useful defaults.
9693c20889SAxel Dörfler	// Since the kernel does not set any variables, this is also needed
9793c20889SAxel Dörfler	// to start the root shell.
9893c20889SAxel Dörfler
99be22452fSAxel Dörfler	switch (type) {
100be22452fSAxel Dörfler		case B_APP_IMAGE:
1012e95cfdaSAugustin Cavalier			return kSystemNonpackagedBinDirectory
1023dfd9cb9SOliver Tappe				":" kGlobalBinDirectory
1033dfd9cb9SOliver Tappe				":" kSystemAppsDirectory
10438fa81bfSIngo Weinhold				":" kSystemPreferencesDirectory;
105be22452fSAxel Dörfler
106be22452fSAxel Dörfler		case B_LIBRARY_IMAGE:
1073dfd9cb9SOliver Tappe			return kAppLocalLibDirectory
10838fa81bfSIngo Weinhold				":" kSystemNonpackagedLibDirectory
1093dfd9cb9SOliver Tappe				":" kSystemLibDirectory;
110be22452fSAxel Dörfler
111be22452fSAxel Dörfler		case B_ADD_ON_IMAGE:
1123dfd9cb9SOliver Tappe			return kAppLocalAddonsDirectory
11338fa81bfSIngo Weinhold				":" kSystemNonpackagedAddonsDirectory
1143dfd9cb9SOliver Tappe				":" kSystemAddonsDirectory;
115be22452fSAxel Dörfler
116be22452fSAxel Dörfler		default:
117be22452fSAxel Dörfler			return NULL;
118be22452fSAxel Dörfler	}
119be22452fSAxel Dörfler}
120be22452fSAxel Dörfler
121be22452fSAxel Dörfler
1228d23c440SIngo Weinholdstatic bool
1238d23c440SIngo Weinholdreplace_executable_path_placeholder(const char*& dir, int& dirLength,
1248d23c440SIngo Weinhold	const char* placeholder, size_t placeholderLength,
1258d23c440SIngo Weinhold	const char* replacementSubPath, char*& buffer, size_t& bufferSize,
1268d23c440SIngo Weinhold	status_t& _error)
1278d23c440SIngo Weinhold{
1288d23c440SIngo Weinhold	if (dirLength < (int)placeholderLength
1298d23c440SIngo Weinhold		|| strncmp(dir, placeholder, placeholderLength) != 0) {
1308d23c440SIngo Weinhold		return false;
1318d23c440SIngo Weinhold	}
1328d23c440SIngo Weinhold
1338d23c440SIngo Weinhold	if (replacementSubPath == NULL) {
1348d23c440SIngo Weinhold		_error = B_ENTRY_NOT_FOUND;
1358d23c440SIngo Weinhold		return true;
1368d23c440SIngo Weinhold	}
1378d23c440SIngo Weinhold
1388d23c440SIngo Weinhold	char* lastSlash = strrchr(replacementSubPath, '/');
1398d23c440SIngo Weinhold
1408d23c440SIngo Weinhold	// Copy replacementSubPath without the last component (the application file
1418d23c440SIngo Weinhold	// name, respectively the requesting executable file name).
1428d23c440SIngo Weinhold	size_t toCopy;
1438d23c440SIngo Weinhold	if (lastSlash != NULL) {
1448d23c440SIngo Weinhold		toCopy = lastSlash - replacementSubPath;
1458d23c440SIngo Weinhold		strlcpy(buffer, replacementSubPath,
1468d23c440SIngo Weinhold			std::min((ssize_t)bufferSize, lastSlash + 1 - replacementSubPath));
1478d23c440SIngo Weinhold	} else {
1488d23c440SIngo Weinhold		replacementSubPath = ".";
1498d23c440SIngo Weinhold		toCopy = 1;
1508d23c440SIngo Weinhold		strlcpy(buffer, ".", bufferSize);
1518d23c440SIngo Weinhold	}
1528d23c440SIngo Weinhold
1538d23c440SIngo Weinhold	if (toCopy >= bufferSize) {
1548d23c440SIngo Weinhold		_error = B_NAME_TOO_LONG;
1558d23c440SIngo Weinhold		return true;
1568d23c440SIngo Weinhold	}
1578d23c440SIngo Weinhold
1588d23c440SIngo Weinhold	memcpy(buffer, replacementSubPath, toCopy);
1598d23c440SIngo Weinhold	buffer[toCopy] = '\0';
1608d23c440SIngo Weinhold
1618d23c440SIngo Weinhold	buffer += toCopy;
1628d23c440SIngo Weinhold	bufferSize -= toCopy;
1638d23c440SIngo Weinhold	dir += placeholderLength;
1648d23c440SIngo Weinhold	dirLength -= placeholderLength;
1658d23c440SIngo Weinhold
1668d23c440SIngo Weinhold	_error = B_OK;
1678d23c440SIngo Weinhold	return true;
1688d23c440SIngo Weinhold}
1698d23c440SIngo Weinhold
1708d23c440SIngo Weinhold
171be22452fSAxel Dörflerstatic int
17212a5e9a4SAxel Dörflertry_open_executable(const char *dir, int dirLength, const char *name,
1738d23c440SIngo Weinhold	const char *programPath, const char *requestingObjectPath,
1748d23c440SIngo Weinhold	const char *abiSpecificSubDir, char *path, size_t pathLength)
175be22452fSAxel Dörfler{
176fe7f3a2fSAxel Dörfler	size_t nameLength = strlen(name);
177fe7f3a2fSAxel Dörfler	struct stat stat;
178fe7f3a2fSAxel Dörfler	status_t status;
179be22452fSAxel Dörfler
180be22452fSAxel Dörfler	// construct the path
181fe7f3a2fSAxel Dörfler	if (dirLength > 0) {
182be22452fSAxel Dörfler		char *buffer = path;
18361b37794SIngo Weinhold		size_t subDirLen = 0;
184be22452fSAxel Dörfler
18512a5e9a4SAxel Dörfler		if (programPath == NULL)
18612a5e9a4SAxel Dörfler			programPath = gProgramArgs->program_path;
18712a5e9a4SAxel Dörfler
1888d23c440SIngo Weinhold		if (replace_executable_path_placeholder(dir, dirLength, "%A", 2,
1898d23c440SIngo Weinhold				programPath, buffer, pathLength, status)
1908d23c440SIngo Weinhold			|| replace_executable_path_placeholder(dir, dirLength, "$ORIGIN", 7,
1918d23c440SIngo Weinhold				requestingObjectPath, buffer, pathLength, status)) {
1928d23c440SIngo Weinhold			if (status != B_OK)
1938d23c440SIngo Weinhold				return status;
194729c6333SOliver Tappe		} else if (abiSpecificSubDir != NULL) {
19561b37794SIngo Weinhold			// We're looking for a library or an add-on and the executable has
196729c6333SOliver Tappe			// not been compiled with a compiler using the same ABI as the one
197729c6333SOliver Tappe			// the OS has been built with. Thus we only look in subdirs
198729c6333SOliver Tappe			// specific to that ABI.
199ea7e2601SJessica Hamilton			// However, only if it's a known library location
200ea7e2601SJessica Hamilton			for (int i = 0; i < 4; ++i) {
201ea7e2601SJessica Hamilton				char buffer[PATH_MAX];
202970910c2SJohn Scipione				status_t result = __find_directory(kLibraryDirectories[i], -1,
203970910c2SJohn Scipione					false, buffer, PATH_MAX);
204970910c2SJohn Scipione				if (result == B_OK && strncmp(dir, buffer, dirLength) == 0) {
205ea7e2601SJessica Hamilton					subDirLen = strlen(abiSpecificSubDir) + 1;
206ea7e2601SJessica Hamilton					break;
207ea7e2601SJessica Hamilton				}
208ea7e2601SJessica Hamilton			}
209be22452fSAxel Dörfler		}
210be22452fSAxel Dörfler
21161b37794SIngo Weinhold		if (dirLength + 1 + subDirLen + nameLength >= pathLength)
212be22452fSAxel Dörfler			return B_NAME_TOO_LONG;
213be22452fSAxel Dörfler
214fe7f3a2fSAxel Dörfler		memcpy(buffer, dir, dirLength);
215fe7f3a2fSAxel Dörfler		buffer[dirLength] = '/';
21661b37794SIngo Weinhold		if (subDirLen > 0) {
217729c6333SOliver Tappe			memcpy(buffer + dirLength + 1, abiSpecificSubDir, subDirLen - 1);
21861b37794SIngo Weinhold			buffer[dirLength + subDirLen] = '/';
21961b37794SIngo Weinhold		}
22061b37794SIngo Weinhold		strcpy(buffer + dirLength + 1 + subDirLen, name);
221be22452fSAxel Dörfler	} else {
222fe7f3a2fSAxel Dörfler		if (nameLength >= pathLength)
223be22452fSAxel Dörfler			return B_NAME_TOO_LONG;
224be22452fSAxel Dörfler
225fe7f3a2fSAxel Dörfler		strcpy(path + dirLength + 1, name);
226be22452fSAxel Dörfler	}
227be22452fSAxel Dörfler
2283728e62eSAxel Dörfler	TRACE(("runtime_loader: try_open_container(): %s\n", path));
229be22452fSAxel Dörfler
230fe7f3a2fSAxel Dörfler	// Test if the target is a symbolic link, and correct the path in this case
231fe7f3a2fSAxel Dörfler
232fe7f3a2fSAxel Dörfler	status = _kern_read_stat(-1, path, false, &stat, sizeof(struct stat));
233fe7f3a2fSAxel Dörfler	if (status < B_OK)
234fe7f3a2fSAxel Dörfler		return status;
235fe7f3a2fSAxel Dörfler
236fe7f3a2fSAxel Dörfler	if (S_ISLNK(stat.st_mode)) {
237fe7f3a2fSAxel Dörfler		char buffer[PATH_MAX];
238c57db7d0SIngo Weinhold		size_t length = PATH_MAX - 1;
239fe7f3a2fSAxel Dörfler		char *lastSlash;
240fe7f3a2fSAxel Dörfler
241fe7f3a2fSAxel Dörfler		// it's a link, indeed
242fe7f3a2fSAxel Dörfler		status = _kern_read_link(-1, path, buffer, &length);
243fe7f3a2fSAxel Dörfler		if (status < B_OK)
244fe7f3a2fSAxel Dörfler			return status;
245c57db7d0SIngo Weinhold		buffer[length] = '\0';
246fe7f3a2fSAxel Dörfler
247fe7f3a2fSAxel Dörfler		lastSlash = strrchr(path, '/');
248fe7f3a2fSAxel Dörfler		if (buffer[0] != '/' && lastSlash != NULL) {
249fe7f3a2fSAxel Dörfler			// relative path
250fe7f3a2fSAxel Dörfler			strlcpy(lastSlash + 1, buffer, lastSlash + 1 - path + pathLength);
251fe7f3a2fSAxel Dörfler		} else
252fe7f3a2fSAxel Dörfler			strlcpy(path, buffer, pathLength);
253fe7f3a2fSAxel Dörfler	}
254fe7f3a2fSAxel Dörfler
255be22452fSAxel Dörfler	return _kern_open(-1, path, O_RDONLY, 0);
256be22452fSAxel Dörfler}
257be22452fSAxel Dörfler
258be22452fSAxel Dörfler
259be22452fSAxel Dörflerstatic int
260be22452fSAxel Dörflersearch_executable_in_path_list(const char *name, const char *pathList,
2618d23c440SIngo Weinhold	int pathListLen, const char *programPath, const char *requestingObjectPath,
2628d23c440SIngo Weinhold	const char *abiSpecificSubDir, char *pathBuffer, size_t pathBufferLength)
263be22452fSAxel Dörfler{
264be22452fSAxel Dörfler	const char *pathListEnd = pathList + pathListLen;
265ce91cbc4SJérôme Duval	status_t status = B_ENTRY_NOT_FOUND;
266be22452fSAxel Dörfler
2673728e62eSAxel Dörfler	TRACE(("runtime_loader: search_container_in_path_list() %s in %.*s\n", name,
2683728e62eSAxel Dörfler		pathListLen, pathList));
269be22452fSAxel Dörfler
270be22452fSAxel Dörfler	while (pathListLen > 0) {
271be22452fSAxel Dörfler		const char *pathEnd = pathList;
272be22452fSAxel Dörfler		int fd;
273be22452fSAxel Dörfler
274be22452fSAxel Dörfler		// find the next ':' or run till the end of the string
275be22452fSAxel Dörfler		while (pathEnd < pathListEnd && *pathEnd != ':')
276be22452fSAxel Dörfler			pathEnd++;
277be22452fSAxel Dörfler
27812a5e9a4SAxel Dörfler		fd = try_open_executable(pathList, pathEnd - pathList, name,
2798d23c440SIngo Weinhold			programPath, requestingObjectPath, abiSpecificSubDir, pathBuffer,
2808d23c440SIngo Weinhold			pathBufferLength);
281ce91cbc4SJérôme Duval		if (fd >= 0) {
282ce91cbc4SJérôme Duval			// see if it's a dir
283ce91cbc4SJérôme Duval			struct stat stat;
284ce91cbc4SJérôme Duval			status = _kern_read_stat(fd, NULL, true, &stat, sizeof(struct stat));
285ce91cbc4SJérôme Duval			if (status == B_OK) {
286ce91cbc4SJérôme Duval				if (!S_ISDIR(stat.st_mode))
287ce91cbc4SJérôme Duval					return fd;
288ce91cbc4SJérôme Duval				status = B_IS_A_DIRECTORY;
289ce91cbc4SJérôme Duval			}
290ce91cbc4SJérôme Duval			_kern_close(fd);
291ce91cbc4SJérôme Duval		}
2920982317bSAxel Dörfler
293be22452fSAxel Dörfler		pathListLen = pathListEnd - pathEnd - 1;
294be22452fSAxel Dörfler		pathList = pathEnd + 1;
295be22452fSAxel Dörfler	}
296be22452fSAxel Dörfler
297ce91cbc4SJérôme Duval	return status;
298be22452fSAxel Dörfler}
299be22452fSAxel Dörfler
300be22452fSAxel Dörfler
301be22452fSAxel Dörflerint
30212a5e9a4SAxel Dörfleropen_executable(char *name, image_type type, const char *rpath,
3038d23c440SIngo Weinhold	const char *programPath, const char *requestingObjectPath,
3048d23c440SIngo Weinhold	const char *abiSpecificSubDir)
305be22452fSAxel Dörfler{
306be22452fSAxel Dörfler	char buffer[PATH_MAX];
307ce91cbc4SJérôme Duval	int fd = B_ENTRY_NOT_FOUND;
308be22452fSAxel Dörfler
309be22452fSAxel Dörfler	if (strchr(name, '/')) {
310be22452fSAxel Dörfler		// the name already contains a path, we don't have to search for it
3113728e62eSAxel Dörfler		fd = _kern_open(-1, name, O_RDONLY, 0);
3123986ce03SPhilippe Houdoin		if (fd >= 0 || type == B_APP_IMAGE)
3133986ce03SPhilippe Houdoin			return fd;
3143986ce03SPhilippe Houdoin
3153986ce03SPhilippe Houdoin		// can't search harder an absolute path add-on name!
3163986ce03SPhilippe Houdoin		if (type == B_ADD_ON_IMAGE && name[0] == '/')
3173728e62eSAxel Dörfler			return fd;
3183728e62eSAxel Dörfler
3193728e62eSAxel Dörfler		// Even though ELF specs don't say this, we give shared libraries
3203986ce03SPhilippe Houdoin		// and relative path based add-ons another chance and look
3213986ce03SPhilippe Houdoin		// them up in the usual search paths - at
3223728e62eSAxel Dörfler		// least that seems to be what BeOS does, and since it doesn't hurt...
3233986ce03SPhilippe Houdoin		if (type == B_LIBRARY_IMAGE) {
324dfc8551aSIngo Weinhold			// For library (but not add-on), strip any path from name.
325dfc8551aSIngo Weinhold			// Relative path of add-on is kept.
326dfc8551aSIngo Weinhold			const char* paths = strrchr(name, '/') + 1;
3273986ce03SPhilippe Houdoin			memmove(name, paths, strlen(paths) + 1);
3283986ce03SPhilippe Houdoin		}
329be22452fSAxel Dörfler	}
330be22452fSAxel Dörfler
331dfc8551aSIngo Weinhold	// try rpath (DT_RPATH)
332dfc8551aSIngo Weinhold	if (rpath != NULL) {
333dfc8551aSIngo Weinhold		// It consists of a colon-separated search path list. Optionally a
334dfc8551aSIngo Weinhold		// second search path list follows, separated from the first by a
335be22452fSAxel Dörfler		// semicolon.
336be22452fSAxel Dörfler		const char *semicolon = strchr(rpath, ';');
337be22452fSAxel Dörfler		const char *firstList = (semicolon ? rpath : NULL);
338be22452fSAxel Dörfler		const char *secondList = (semicolon ? semicolon + 1 : rpath);
339be22452fSAxel Dörfler			// If there is no ';', we set only secondList to simplify things.
340be22452fSAxel Dörfler		if (firstList) {
341be22452fSAxel Dörfler			fd = search_executable_in_path_list(name, firstList,
3428d23c440SIngo Weinhold				semicolon - firstList, programPath, requestingObjectPath, NULL,
3438d23c440SIngo Weinhold				buffer, sizeof(buffer));
344be22452fSAxel Dörfler		}
345be22452fSAxel Dörfler		if (fd < 0) {
346be22452fSAxel Dörfler			fd = search_executable_in_path_list(name, secondList,
3478d23c440SIngo Weinhold				strlen(secondList), programPath, requestingObjectPath, NULL,
3488d23c440SIngo Weinhold				buffer, sizeof(buffer));
349be22452fSAxel Dörfler		}
350be22452fSAxel Dörfler	}
351be22452fSAxel Dörfler
352dfc8551aSIngo Weinhold	// If not found yet, let's evaluate the system path variables to find the
353dfc8551aSIngo Weinhold	// shared object.
354dfc8551aSIngo Weinhold	if (fd < 0) {
355dfc8551aSIngo Weinhold		if (const char *paths = search_path_for_type(type)) {
356dfc8551aSIngo Weinhold			fd = search_executable_in_path_list(name, paths, strlen(paths),
3578d23c440SIngo Weinhold				programPath, NULL, abiSpecificSubDir, buffer, sizeof(buffer));
358dfc8551aSIngo Weinhold		}
359dfc8551aSIngo Weinhold	}
360dfc8551aSIngo Weinhold
361be22452fSAxel Dörfler	if (fd >= 0) {
362be22452fSAxel Dörfler		// we found it, copy path!
36312a5e9a4SAxel Dörfler		TRACE(("runtime_loader: open_executable(%s): found at %s\n", name, buffer));
364be22452fSAxel Dörfler		strlcpy(name, buffer, PATH_MAX);
365be22452fSAxel Dörfler	}
366be22452fSAxel Dörfler
367ce91cbc4SJérôme Duval	return fd;
368be22452fSAxel Dörfler}
369be22452fSAxel Dörfler
370be22452fSAxel Dörfler
3718b089927SOliver Tappe/*!
3728b089927SOliver Tappe	Applies haiku-specific fixes to a shebang line.
3738b089927SOliver Tappe*/
3748b089927SOliver Tappestatic void
3758b089927SOliver Tappefixup_shebang(char *invoker)
3768b089927SOliver Tappe{
3778b089927SOliver Tappe	char *current = invoker;
3788b089927SOliver Tappe	while (*current == ' ' || *current == '\t') {
3798b089927SOliver Tappe		++current;
3808b089927SOliver Tappe	}
3818b089927SOliver Tappe
3828b089927SOliver Tappe	char *commandStart = current;
3838b089927SOliver Tappe	while (*current != ' ' && *current != '\t' && *current != '\0') {
3848b089927SOliver Tappe		++current;
3858b089927SOliver Tappe	}
3868b089927SOliver Tappe
387938d81f6SAugustin Cavalier	// replace /usr/bin/ with /bin/
38880934b83SFrançois Revol	if (memcmp(commandStart, "/usr/bin/", strlen("/usr/bin/")) == 0)
3898b089927SOliver Tappe		memmove(commandStart, commandStart + 4, strlen(commandStart + 4) + 1);
3908b089927SOliver Tappe}
3918b089927SOliver Tappe
3928b089927SOliver Tappe
39374c0424aSAxel Dörfler/*!
39474c0424aSAxel Dörfler	Tests if there is an executable file at the provided path. It will
39574c0424aSAxel Dörfler	also test if the file has a valid ELF header or is a shell script.
39674c0424aSAxel Dörfler	Even if the runtime loader does not need to be able to deal with
39774c0424aSAxel Dörfler	both types, the caller will give scripts a proper treatment.
39874c0424aSAxel Dörfler*/
399be22452fSAxel Dörflerstatus_t
4008560d188SIngo Weinholdtest_executable(const char *name, char *invoker)
401be22452fSAxel Dörfler{
402be22452fSAxel Dörfler	char path[B_PATH_NAME_LENGTH];
403be22452fSAxel Dörfler	char buffer[B_FILE_NAME_LENGTH];
404be22452fSAxel Dörfler		// must be large enough to hold the ELF header
405be22452fSAxel Dörfler	status_t status;
406be22452fSAxel Dörfler	ssize_t length;
407be22452fSAxel Dörfler	int fd;
408be22452fSAxel Dörfler
409f0a53269SAxel Dörfler	if (name == NULL)
410f0a53269SAxel Dörfler		return B_BAD_VALUE;
411f0a53269SAxel Dörfler
412be22452fSAxel Dörfler	strlcpy(path, name, sizeof(path));
413be22452fSAxel Dörfler
4148d23c440SIngo Weinhold	fd = open_executable(path, B_APP_IMAGE, NULL, NULL, NULL, NULL);
415be22452fSAxel Dörfler	if (fd < B_OK)
416be22452fSAxel Dörfler		return fd;
417be22452fSAxel Dörfler
418be22452fSAxel Dörfler	// see if it's executable at all
419fb2500daSAxel Dörfler	status = _kern_access(-1, path, X_OK, false);
420be22452fSAxel Dörfler	if (status != B_OK)
421be22452fSAxel Dörfler		goto out;
422be22452fSAxel Dörfler
423be22452fSAxel Dörfler	// read and verify the ELF header
424be22452fSAxel Dörfler
425be22452fSAxel Dörfler	length = _kern_read(fd, 0, buffer, sizeof(buffer));
426be22452fSAxel Dörfler	if (length < 0) {
427be22452fSAxel Dörfler		status = length;
428be22452fSAxel Dörfler		goto out;
429be22452fSAxel Dörfler	}
430be22452fSAxel Dörfler
431be22452fSAxel Dörfler	status = elf_verify_header(buffer, length);
4322aaad308SJérôme Duval#ifdef _COMPAT_MODE
4332aaad308SJérôme Duval#ifdef __x86_64__
4342aaad308SJérôme Duval	if (status == B_NOT_AN_EXECUTABLE)
4352aaad308SJérôme Duval		status = elf32_verify_header(buffer, length);
4362aaad308SJérôme Duval#else
4372aaad308SJérôme Duval	if (status == B_NOT_AN_EXECUTABLE)
4382aaad308SJérôme Duval		status = elf64_verify_header(buffer, length);
4392aaad308SJérôme Duval#endif	// __x86_64__
4402aaad308SJérôme Duval#endif	// _COMPAT_MODE
441be22452fSAxel Dörfler	if (status == B_NOT_AN_EXECUTABLE) {
442be22452fSAxel Dörfler		if (!strncmp(buffer, "#!", 2)) {
443aa4b5749SAlexander von Gluck IV			// test for shell scripts
44443ed6aa0SAxel Dörfler			char *end;
4457ca40195SIngo Weinhold			buffer[min_c((size_t)length, sizeof(buffer) - 1)] = '\0';
44643ed6aa0SAxel Dörfler
44743ed6aa0SAxel Dörfler			end = strchr(buffer, '\n');
44843ed6aa0SAxel Dörfler			if (end == NULL) {
44943ed6aa0SAxel Dörfler				status = E2BIG;
45043ed6aa0SAxel Dörfler				goto out;
45143ed6aa0SAxel Dörfler			} else
45243ed6aa0SAxel Dörfler				end[0] = '\0';
45343ed6aa0SAxel Dörfler
4548b089927SOliver Tappe			if (invoker) {
45543ed6aa0SAxel Dörfler				strcpy(invoker, buffer + 2);
4568b089927SOliver Tappe				fixup_shebang(invoker);
4578b089927SOliver Tappe			}
45843ed6aa0SAxel Dörfler
459be22452fSAxel Dörfler			status = B_OK;
460aa4b5749SAlexander von Gluck IV		} else {
461aa4b5749SAlexander von Gluck IV			// Something odd like a PE?
462aa4b5749SAlexander von Gluck IV			status = pe_verify_header(buffer, length);
463aa4b5749SAlexander von Gluck IV
464aa4b5749SAlexander von Gluck IV			// It is a PE, throw B_UNKNOWN_EXECUTABLE
465aa4b5749SAlexander von Gluck IV			// likely win32 at this point
466aa4b5749SAlexander von Gluck IV			if (status == B_OK)
467aa4b5749SAlexander von Gluck IV				status = B_UNKNOWN_EXECUTABLE;
468be22452fSAxel Dörfler		}
4690f0234a5SAxel Dörfler	} else if (status == B_OK) {
470e3ac2588SAlex Smith		elf_ehdr *elfHeader = (elf_ehdr *)buffer;
4713560b757SIngo Weinhold		if (elfHeader->e_entry == 0) {
4720f0234a5SAxel Dörfler			// we don't like to open shared libraries
4730f0234a5SAxel Dörfler			status = B_NOT_AN_EXECUTABLE;
4740f0234a5SAxel Dörfler		} else if (invoker)
4750f0234a5SAxel Dörfler			invoker[0] = '\0';
4760f0234a5SAxel Dörfler	}
477be22452fSAxel Dörfler
478be22452fSAxel Dörflerout:
479be22452fSAxel Dörfler	_kern_close(fd);
480be22452fSAxel Dörfler	return status;
481be22452fSAxel Dörfler}
482be22452fSAxel Dörfler
484cf7e2ad8SIngo Weinholdstatic bool
485cf7e2ad8SIngo Weinholddetermine_x86_abi(int fd, const Elf32_Ehdr& elfHeader, bool& _isGcc2)
486cf7e2ad8SIngo Weinhold{
487cf7e2ad8SIngo Weinhold	// Unless we're a little-endian CPU, don't bother. We're not x86, so it
488cf7e2ad8SIngo Weinhold	// doesn't matter all that much whether we can determine the correct gcc
489cf7e2ad8SIngo Weinhold	// ABI. This saves the code below from having to deal with endianess
490cf7e2ad8SIngo Weinhold	// conversion.
491cf7e2ad8SIngo Weinhold#if B_HOST_IS_LENDIAN
492cf7e2ad8SIngo Weinhold
493cf7e2ad8SIngo Weinhold	// Since we don't want to load the complete image, we can't use the
494cf7e2ad8SIngo Weinhold	// functions that normally determine the Haiku version and ABI. Instead
495cf7e2ad8SIngo Weinhold	// we'll load the symbol and string tables and resolve the ABI symbol
496cf7e2ad8SIngo Weinhold	// manually.
497cf7e2ad8SIngo Weinhold
498cf7e2ad8SIngo Weinhold	// map the file into memory
499cf7e2ad8SIngo Weinhold	struct stat st;
500cf7e2ad8SIngo Weinhold	if (_kern_read_stat(fd, NULL, true, &st, sizeof(st)) != B_OK)
501cf7e2ad8SIngo Weinhold		return false;
502cf7e2ad8SIngo Weinhold
503cf7e2ad8SIngo Weinhold	void* fileBaseAddress;
504cf7e2ad8SIngo Weinhold	area_id area = _kern_map_file("mapped file", &fileBaseAddress,
505cf7e2ad8SIngo Weinhold		B_ANY_ADDRESS, st.st_size, B_READ_AREA, REGION_NO_PRIVATE_MAP, false,
506cf7e2ad8SIngo Weinhold		fd, 0);
507cf7e2ad8SIngo Weinhold	if (area < 0)
508cf7e2ad8SIngo Weinhold		return false;
509cf7e2ad8SIngo Weinhold
510cf7e2ad8SIngo Weinhold	struct AreaDeleter {
511cf7e2ad8SIngo Weinhold		AreaDeleter(area_id area)
512cf7e2ad8SIngo Weinhold			:
513cf7e2ad8SIngo Weinhold			fArea(area)
514cf7e2ad8SIngo Weinhold		{
515cf7e2ad8SIngo Weinhold		}
516cf7e2ad8SIngo Weinhold
517cf7e2ad8SIngo Weinhold		~AreaDeleter()
518cf7e2ad8SIngo Weinhold		{
519cf7e2ad8SIngo Weinhold			_kern_delete_area(fArea);
520cf7e2ad8SIngo Weinhold		}
521cf7e2ad8SIngo Weinhold
522cf7e2ad8SIngo Weinhold	private:
523cf7e2ad8SIngo Weinhold		area_id	fArea;
524cf7e2ad8SIngo Weinhold	} areaDeleter(area);
525cf7e2ad8SIngo Weinhold
526cf7e2ad8SIngo Weinhold	// get the section headers
527cf7e2ad8SIngo Weinhold	if (elfHeader.e_shoff == 0 || elfHeader.e_shentsize < sizeof(Elf32_Shdr))
528cf7e2ad8SIngo Weinhold		return false;
529cf7e2ad8SIngo Weinhold
530cf7e2ad8SIngo Weinhold	size_t sectionHeadersSize = elfHeader.e_shentsize * elfHeader.e_shnum;
531cf7e2ad8SIngo Weinhold	if (elfHeader.e_shoff + (off_t)sectionHeadersSize > st.st_size)
532cf7e2ad8SIngo Weinhold		return false;
533cf7e2ad8SIngo Weinhold
534cf7e2ad8SIngo Weinhold	void* sectionHeaders = (uint8*)fileBaseAddress + elfHeader.e_shoff;
535cf7e2ad8SIngo Weinhold
536cf7e2ad8SIngo Weinhold	// find the sections we need
537cf7e2ad8SIngo Weinhold	uint32* symbolHash = NULL;
538cf7e2ad8SIngo Weinhold	uint32 symbolHashSize = 0;
539cf7e2ad8SIngo Weinhold	uint32 symbolHashChainSize = 0;
540cf7e2ad8SIngo Weinhold	Elf32_Sym* symbolTable = NULL;
541cf7e2ad8SIngo Weinhold	uint32 symbolTableSize = 0;
542cf7e2ad8SIngo Weinhold	const char* stringTable = NULL;
543cf7e2ad8SIngo Weinhold	off_t stringTableSize = 0;
544cf7e2ad8SIngo Weinhold
545cf7e2ad8SIngo Weinhold	for (int32 i = 0; i < elfHeader.e_shnum; i++) {
546cf7e2ad8SIngo Weinhold		Elf32_Shdr* sectionHeader
547cf7e2ad8SIngo Weinhold			= (Elf32_Shdr*)((uint8*)sectionHeaders + i * elfHeader.e_shentsize);
548cf7e2ad8SIngo Weinhold		if ((off_t)sectionHeader->sh_offset + (off_t)sectionHeader->sh_size
549cf7e2ad8SIngo Weinhold				> st.st_size) {
550cf7e2ad8SIngo Weinhold			continue;
551cf7e2ad8SIngo Weinhold		}
552cf7e2ad8SIngo Weinhold
553cf7e2ad8SIngo Weinhold		void* sectionAddress = (uint8*)fileBaseAddress
554cf7e2ad8SIngo Weinhold			+ sectionHeader->sh_offset;
555cf7e2ad8SIngo Weinhold
556cf7e2ad8SIngo Weinhold		switch (sectionHeader->sh_type) {
557cf7e2ad8SIngo Weinhold			case SHT_HASH:
558cf7e2ad8SIngo Weinhold				symbolHash = (uint32*)sectionAddress;
559cf7e2ad8SIngo Weinhold				if (sectionHeader->sh_size < (off_t)sizeof(symbolHash[0]))
560cf7e2ad8SIngo Weinhold					return false;
561cf7e2ad8SIngo Weinhold				symbolHashSize = symbolHash[0];
562cf7e2ad8SIngo Weinhold				symbolHashChainSize
563cf7e2ad8SIngo Weinhold					= sectionHeader->sh_size / sizeof(symbolHash[0]);
564cf7e2ad8SIngo Weinhold				if (symbolHashChainSize < symbolHashSize + 2)
565cf7e2ad8SIngo Weinhold					return false;
566cf7e2ad8SIngo Weinhold				symbolHashChainSize -= symbolHashSize + 2;
567cf7e2ad8SIngo Weinhold				break;
568cf7e2ad8SIngo Weinhold			case SHT_DYNSYM:
569cf7e2ad8SIngo Weinhold				symbolTable = (Elf32_Sym*)sectionAddress;
570cf7e2ad8SIngo Weinhold				symbolTableSize = sectionHeader->sh_size;
571cf7e2ad8SIngo Weinhold				break;
572cf7e2ad8SIngo Weinhold			case SHT_STRTAB:
573cf7e2ad8SIngo Weinhold				// .shstrtab has the same type as .dynstr, but it isn't loaded
574cf7e2ad8SIngo Weinhold				// into memory.
575cf7e2ad8SIngo Weinhold				if (sectionHeader->sh_addr == 0)
576cf7e2ad8SIngo Weinhold					continue;
577cf7e2ad8SIngo Weinhold				stringTable = (const char*)sectionAddress;
578cf7e2ad8SIngo Weinhold				stringTableSize = (off_t)sectionHeader->sh_size;
579cf7e2ad8SIngo Weinhold				break;
580cf7e2ad8SIngo Weinhold			default:
581cf7e2ad8SIngo Weinhold				continue;
582cf7e2ad8SIngo Weinhold		}
583cf7e2ad8SIngo Weinhold	}
584cf7e2ad8SIngo Weinhold
585cf7e2ad8SIngo Weinhold	if (symbolHash == NULL || symbolTable == NULL || stringTable == NULL)
586cf7e2ad8SIngo Weinhold		return false;
587cf7e2ad8SIngo Weinhold	uint32 symbolCount
58872950e7cSJérôme Duval		= std::min(symbolTableSize / (uint32)sizeof(Elf32_Sym),
58972950e7cSJérôme Duval			symbolHashChainSize);
590cf7e2ad8SIngo Weinhold	if (symbolCount < symbolHashSize)
591cf7e2ad8SIngo Weinhold		return false;
592cf7e2ad8SIngo Weinhold
593cf7e2ad8SIngo Weinhold	// look up the ABI symbol
594cf7e2ad8SIngo Weinhold	const char* name = B_SHARED_OBJECT_HAIKU_ABI_VARIABLE_NAME;
595cf7e2ad8SIngo Weinhold	size_t nameLength = strlen(name);
596cf7e2ad8SIngo Weinhold	uint32 bucket = elf_hash(name) % symbolHashSize;
597cf7e2ad8SIngo Weinhold
598cf7e2ad8SIngo Weinhold	for (uint32 i = symbolHash[bucket + 2]; i < symbolCount && i != STN_UNDEF;
599cf7e2ad8SIngo Weinhold		i = symbolHash[2 + symbolHashSize + i]) {
600cf7e2ad8SIngo Weinhold		Elf32_Sym* symbol = symbolTable + i;
601cf7e2ad8SIngo Weinhold		if (symbol->st_shndx != SHN_UNDEF
602cf7e2ad8SIngo Weinhold			&& ((symbol->Bind() == STB_GLOBAL) || (symbol->Bind() == STB_WEAK))
603cf7e2ad8SIngo Weinhold			&& symbol->Type() == STT_OBJECT
604cf7e2ad8SIngo Weinhold			&& (off_t)symbol->st_name + (off_t)nameLength < stringTableSize
605cf7e2ad8SIngo Weinhold			&& strcmp(stringTable + symbol->st_name, name) == 0) {
606cf7e2ad8SIngo Weinhold			if (symbol->st_value > 0 && symbol->st_size >= sizeof(uint32)
607cf7e2ad8SIngo Weinhold				&& symbol->st_shndx < elfHeader.e_shnum) {
608cf7e2ad8SIngo Weinhold				Elf32_Shdr* sectionHeader = (Elf32_Shdr*)((uint8*)sectionHeaders
609cf7e2ad8SIngo Weinhold					+ symbol->st_shndx * elfHeader.e_shentsize);
610cf7e2ad8SIngo Weinhold				if (symbol->st_value >= sectionHeader->sh_addr
611cf7e2ad8SIngo Weinhold					&& symbol->st_value
612cf7e2ad8SIngo Weinhold						<= sectionHeader->sh_addr + sectionHeader->sh_size) {
613cf7e2ad8SIngo Weinhold					off_t fileOffset = symbol->st_value - sectionHeader->sh_addr
614cf7e2ad8SIngo Weinhold						+ sectionHeader->sh_offset;
61572950e7cSJérôme Duval					if (fileOffset + (off_t)sizeof(uint32) <= st.st_size) {
616cf7e2ad8SIngo Weinhold						uint32 abi
617cf7e2ad8SIngo Weinhold							= *(uint32*)((uint8*)fileBaseAddress + fileOffset);
618cf7e2ad8SIngo Weinhold						_isGcc2 = (abi & B_HAIKU_ABI_MAJOR)
619cf7e2ad8SIngo Weinhold							== B_HAIKU_ABI_GCC_2;
620cf7e2ad8SIngo Weinhold						return true;
621cf7e2ad8SIngo Weinhold					}
622cf7e2ad8SIngo Weinhold				}
623cf7e2ad8SIngo Weinhold			}
624cf7e2ad8SIngo Weinhold
625cf7e2ad8SIngo Weinhold			return false;
626cf7e2ad8SIngo Weinhold		}
627cf7e2ad8SIngo Weinhold	}
628cf7e2ad8SIngo Weinhold
629cf7e2ad8SIngo Weinhold	// ABI symbol not found. That means the object pre-dates its introduction
630cf7e2ad8SIngo Weinhold	// in Haiku. So this is most likely gcc 2. We don't fall back to reading
631cf7e2ad8SIngo Weinhold	// the comment sections to verify.
632cf7e2ad8SIngo Weinhold	_isGcc2 = true;
633cf7e2ad8SIngo Weinhold	return true;
634cf7e2ad8SIngo Weinhold#else	// not little endian
635cf7e2ad8SIngo Weinhold	return false;
636cf7e2ad8SIngo Weinhold#endif
637cf7e2ad8SIngo Weinhold}
638cf7e2ad8SIngo Weinhold
639cf7e2ad8SIngo Weinhold
640cf7e2ad8SIngo Weinholdstatic status_t
641cf7e2ad8SIngo Weinholdget_executable_architecture(int fd, const char** _architecture)
642cf7e2ad8SIngo Weinhold{
643cf7e2ad8SIngo Weinhold	// Read the ELF header. We read the 32 bit header. Generally the e_machine
644cf7e2ad8SIngo Weinhold	// field is the last one that interests us and the 64 bit header is still
645cf7e2ad8SIngo Weinhold	// identical at that point.
646cf7e2ad8SIngo Weinhold	Elf32_Ehdr elfHeader;
647cf7e2ad8SIngo Weinhold	ssize_t bytesRead = _kern_read(fd, 0, &elfHeader, sizeof(elfHeader));
648cf7e2ad8SIngo Weinhold	if (bytesRead < 0)
649cf7e2ad8SIngo Weinhold		return bytesRead;
650cf7e2ad8SIngo Weinhold	if ((size_t)bytesRead != sizeof(elfHeader))
651cf7e2ad8SIngo Weinhold		return B_NOT_AN_EXECUTABLE;
652cf7e2ad8SIngo Weinhold
653cf7e2ad8SIngo Weinhold	// check whether this is indeed an ELF file
6548efb6db7SAdrien Destugues	if (memcmp(elfHeader.e_ident, ELFMAG, 4) != 0)
655cf7e2ad8SIngo Weinhold		return B_NOT_AN_EXECUTABLE;
656cf7e2ad8SIngo Weinhold
657cf7e2ad8SIngo Weinhold	// check the architecture
658cf7e2ad8SIngo Weinhold	uint16 machine = elfHeader.e_machine;
659cf7e2ad8SIngo Weinhold	if ((elfHeader.e_ident[EI_DATA] == ELFDATA2LSB) != (B_HOST_IS_LENDIAN != 0))
660cf7e2ad8SIngo Weinhold		machine = (machine >> 8) | (machine << 8);
661cf7e2ad8SIngo Weinhold
662cf7e2ad8SIngo Weinhold	const char* architecture = NULL;
663cf7e2ad8SIngo Weinhold	switch (machine) {
664cf7e2ad8SIngo Weinhold		case EM_386:
665cf7e2ad8SIngo Weinhold		case EM_486:
666cf7e2ad8SIngo Weinhold		{
667cf7e2ad8SIngo Weinhold			bool isGcc2;
668cf7e2ad8SIngo Weinhold			if (determine_x86_abi(fd, elfHeader, isGcc2) && isGcc2)
669cf7e2ad8SIngo Weinhold				architecture = "x86_gcc2";
670cf7e2ad8SIngo Weinhold			else
671cf7e2ad8SIngo Weinhold				architecture = "x86";
672cf7e2ad8SIngo Weinhold			break;
673cf7e2ad8SIngo Weinhold		}
674cf7e2ad8SIngo Weinhold		case EM_68K:
675cf7e2ad8SIngo Weinhold			architecture = "m68k";
676cf7e2ad8SIngo Weinhold			break;
677cf7e2ad8SIngo Weinhold		case EM_PPC:
678cf7e2ad8SIngo Weinhold			architecture = "ppc";
679cf7e2ad8SIngo Weinhold			break;
680cf7e2ad8SIngo Weinhold		case EM_ARM:
681cf7e2ad8SIngo Weinhold			architecture = "arm";
682cf7e2ad8SIngo Weinhold			break;
683fb4cc984SAugustin Cavalier		case EM_ARM64:
684fb4cc984SAugustin Cavalier			architecture = "arm64";
685dd485ed4SAlexander von Gluck IV			break;
686cf7e2ad8SIngo Weinhold		case EM_X86_64:
687cf7e2ad8SIngo Weinhold			architecture = "x86_64";
688cf7e2ad8SIngo Weinhold			break;
689dd485ed4SAlexander von Gluck IV		case EM_RISCV:
690dd485ed4SAlexander von Gluck IV			architecture = "riscv";
691dd485ed4SAlexander von Gluck IV			break;
692cf7e2ad8SIngo Weinhold	}
693cf7e2ad8SIngo Weinhold
694cf7e2ad8SIngo Weinhold	if (architecture == NULL)
695cf7e2ad8SIngo Weinhold		return B_NOT_SUPPORTED;
696cf7e2ad8SIngo Weinhold
697cf7e2ad8SIngo Weinhold	*_architecture = architecture;
698cf7e2ad8SIngo Weinhold	return B_OK;
699cf7e2ad8SIngo Weinhold}
700cf7e2ad8SIngo Weinhold
701cf7e2ad8SIngo Weinhold
702cf7e2ad8SIngo Weinholdstatus_t
703cf7e2ad8SIngo Weinholdget_executable_architecture(const char* path, const char** _architecture)
704cf7e2ad8SIngo Weinhold{
705cf7e2ad8SIngo Weinhold	int fd = _kern_open(-1, path, O_RDONLY, 0);
706cf7e2ad8SIngo Weinhold	if (fd < 0)
707cf7e2ad8SIngo Weinhold		return fd;
708cf7e2ad8SIngo Weinhold
709cf7e2ad8SIngo Weinhold	status_t error = get_executable_architecture(fd, _architecture);
710cf7e2ad8SIngo Weinhold
711cf7e2ad8SIngo Weinhold	_kern_close(fd);
712cf7e2ad8SIngo Weinhold	return error;
713cf7e2ad8SIngo Weinhold}
714cf7e2ad8SIngo Weinhold
715cf7e2ad8SIngo Weinhold
71674c0424aSAxel Dörfler/*!
71774c0424aSAxel Dörfler	This is the main entry point of the runtime loader as
71874c0424aSAxel Dörfler	specified by its ld-script.
71974c0424aSAxel Dörfler*/
721e85e399fSPawel Dziepakruntime_loader(void* _args, void* commpage)
723be22452fSAxel Dörfler	void *entry = NULL;
72471d2531dSStephan Aßmus	int returnCode;
725be22452fSAxel Dörfler
72674c0424aSAxel Dörfler	gProgramArgs = (struct user_space_program_args *)_args;
727e85e399fSPawel Dziepak	__gCommPageAddress = commpage;
7292965c99fSIngo Weinhold	// Relocate the args and env arrays -- they are organized in a contiguous
7302965c99fSIngo Weinhold	// buffer which the kernel just copied into user space without adjusting the
7312965c99fSIngo Weinhold	// pointers.
7322965c99fSIngo Weinhold	{
7332965c99fSIngo Weinhold		int32 i;
7342965c99fSIngo Weinhold		addr_t relocationOffset = 0;
7352965c99fSIngo Weinhold
7362965c99fSIngo Weinhold		if (gProgramArgs->arg_count > 0)
7372965c99fSIngo Weinhold			relocationOffset = (addr_t)gProgramArgs->args[0];
7382965c99fSIngo Weinhold		else if (gProgramArgs->env_count > 0)
7392965c99fSIngo Weinhold			relocationOffset = (addr_t)gProgramArgs->env[0];
7402965c99fSIngo Weinhold
7412965c99fSIngo Weinhold		// That's basically: <new buffer address> - <old buffer address>.
7422965c99fSIngo Weinhold		// It looks a little complicated, since we don't have the latter one at
7432965c99fSIngo Weinhold		// hand and thus need to reconstruct it (<first string pointer> -
7442965c99fSIngo Weinhold		// <arguments + environment array sizes>).
7452965c99fSIngo Weinhold		relocationOffset = (addr_t)gProgramArgs->args - relocationOffset
7462965c99fSIngo Weinhold			+ (gProgramArgs->arg_count + gProgramArgs->env_count + 2)
7472965c99fSIngo Weinhold				* sizeof(char*);
7482965c99fSIngo Weinhold
7492965c99fSIngo Weinhold		for (i = 0; i < gProgramArgs->arg_count; i++)
7502965c99fSIngo Weinhold			gProgramArgs->args[i] += relocationOffset;
7512965c99fSIngo Weinhold
7522965c99fSIngo Weinhold		for (i = 0; i < gProgramArgs->env_count; i++)
7532965c99fSIngo Weinhold			gProgramArgs->env[i] += relocationOffset;
7542965c99fSIngo Weinhold	}
7552965c99fSIngo Weinhold
756cd6c2db4SAxel Dörfler#if DEBUG_RLD
757cd6c2db4SAxel Dörfler	close(0); open("/dev/console", 0); /* stdin   */
758cd6c2db4SAxel Dörfler	close(1); open("/dev/console", 0); /* stdout  */
759cd6c2db4SAxel Dörfler	close(2); open("/dev/console", 0); /* stderr  */
760cd6c2db4SAxel Dörfler#endif
7626f0994d4SAxel Dörfler	if (heap_init() < B_OK)
7636f0994d4SAxel Dörfler		return 1;
7646f0994d4SAxel Dörfler
765be22452fSAxel Dörfler	rldexport_init();
766be22452fSAxel Dörfler	rldelf_init();
768be22452fSAxel Dörfler	load_program(gProgramArgs->program_path, &entry);
770f351df08SAxel Dörfler	if (entry == NULL)
77152a38012Sejakowatz		return -1;
77324154245SAxel Dörfler	// call the program entry point (usually _start())
77474c0424aSAxel Dörfler	returnCode = ((int (*)(int, void *, void *))entry)(gProgramArgs->arg_count,
77574c0424aSAxel Dörfler		gProgramArgs->args, gProgramArgs->env);
77671d2531dSStephan Aßmus
77771d2531dSStephan Aßmus	terminate_program();
77871d2531dSStephan Aßmus
77971d2531dSStephan Aßmus	return returnCode;