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