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