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