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