1 /* 2 * Copyright 2003-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <image.h> 7 #include <image_private.h> 8 9 #include <stdlib.h> 10 #include <string.h> 11 12 #include <algorithm> 13 #include <new> 14 15 #include <fs_attr.h> 16 17 #include <AutoDeleter.h> 18 19 #include <libroot_private.h> 20 #include <runtime_loader.h> 21 #include <syscalls.h> 22 #include <syscall_load_image.h> 23 #include <user_runtime.h> 24 25 26 struct EnvironmentFilter { 27 EnvironmentFilter() 28 : 29 fBuffer(NULL), 30 fEntries(NULL), 31 fBufferSize(0), 32 fEntryCount(0), 33 fAdditionalEnvCount(0), 34 fNextEntryIndex(0) 35 { 36 } 37 38 ~EnvironmentFilter() 39 { 40 free(fBuffer); 41 delete[] fEntries; 42 } 43 44 void Init(const char* path, const char* const* env, size_t envCount) 45 { 46 int fd = open(path, O_RDONLY); 47 if (fd < 0) 48 return; 49 FileDescriptorCloser fdCloser(fd); 50 51 static const char* const kEnvAttribute = "SYS:ENV"; 52 attr_info info; 53 if (fs_stat_attr(fd, kEnvAttribute, &info) < 0) 54 return; 55 56 _Init(fd, kEnvAttribute, info.size, env, envCount); 57 } 58 59 size_t AdditionalSlotsNeeded() const 60 { 61 return fAdditionalEnvCount; 62 } 63 64 size_t AdditionalSizeNeeded() const 65 { 66 return fBufferSize + fAdditionalEnvCount * sizeof(char*); 67 } 68 69 size_t PrepareSlot(const char* env, int32 index, char* buffer) 70 { 71 if (fNextEntryIndex < fEntryCount 72 && fEntries[fNextEntryIndex].index == index) { 73 env = fEntries[fNextEntryIndex].replacement; 74 fNextEntryIndex++; 75 } 76 77 return _FillSlot(env, buffer); 78 } 79 80 void PrepareAdditionalSlots(char**& slot, char*& buffer) 81 { 82 for (size_t i = 0; i < fAdditionalEnvCount; i++) { 83 size_t envSize = _FillSlot(fEntries[i].replacement, buffer); 84 *slot++ = buffer; 85 buffer += envSize; 86 } 87 } 88 89 private: 90 void _Init(int fd, const char* attribute, size_t size, 91 const char* const* env, size_t envCount) 92 { 93 if (size == 0) 94 return; 95 96 // read the attribute 97 char* buffer = (char*)malloc(size + 1); 98 if (buffer == NULL) 99 return; 100 MemoryDeleter bufferDeleter(buffer); 101 102 ssize_t bytesRead = fs_read_attr(fd, attribute, B_STRING_TYPE, 0, 103 buffer, size); 104 if (bytesRead < 0 || (size_t)bytesRead != size) 105 return; 106 buffer[size] = '\0'; 107 108 // deescape the buffer and count the entries 109 size_t entryCount = 1; 110 char* out = buffer; 111 for (const char* c = buffer; *c != '\0'; c++) { 112 if (*c == '\\') { 113 c++; 114 if (*c == '\0') 115 break; 116 if (*c == '0') { 117 *out++ = '\0'; 118 entryCount++; 119 } else 120 *out++ = *c; 121 } else 122 *out++ = *c; 123 } 124 *out++ = '\0'; 125 size = out - buffer + 1; 126 127 // create an entry array 128 fEntries = new(std::nothrow) Entry[entryCount]; 129 if (fEntries == NULL) 130 return; 131 132 bufferDeleter.Detach(); 133 fBuffer = buffer; 134 fBufferSize = size; 135 136 // init the entries 137 out = buffer; 138 for (size_t i = 0; i < entryCount; i++) { 139 const char* separator = strchr(out, '='); 140 if (separator != NULL && separator != out) { 141 fEntries[fEntryCount].replacement = out; 142 fEntries[fEntryCount].index = _FindEnvEntry(env, envCount, out, 143 separator - out); 144 if (fEntries[fEntryCount].index < 0) 145 fAdditionalEnvCount++; 146 fEntryCount++; 147 } 148 out += strlen(out) + 1; 149 } 150 151 if (fEntryCount > 1) 152 std::sort(fEntries, fEntries + fEntryCount); 153 154 // Advance fNextEntryIndex to the first entry pointing to an existing 155 // env variable. 156 while (fNextEntryIndex < fEntryCount 157 && fEntries[fNextEntryIndex].index < 0) { 158 fNextEntryIndex++; 159 } 160 } 161 162 int32 _FindEnvEntry(const char* const* env, size_t envCount, 163 const char* variable, size_t variableLength) 164 { 165 for (size_t i = 0; i < envCount; i++) { 166 if (strncmp(env[i], variable, variableLength) == 0 167 && env[i][variableLength] == '=') { 168 return i; 169 } 170 } 171 172 return -1; 173 } 174 175 size_t _FillSlot(const char* env, char* buffer) 176 { 177 size_t envSize = strlen(env) + 1; 178 memcpy(buffer, env, envSize); 179 return envSize; 180 } 181 182 private: 183 struct Entry { 184 char* replacement; 185 int32 index; 186 187 bool operator<(const Entry& other) const 188 { 189 return index < other.index; 190 } 191 }; 192 193 private: 194 char* fBuffer; 195 Entry* fEntries; 196 size_t fBufferSize; 197 size_t fEntryCount; 198 size_t fAdditionalEnvCount; 199 size_t fNextEntryIndex; 200 }; 201 202 203 thread_id 204 __load_image_at_path(const char* path, int32 argCount, const char **args, 205 const char **environ) 206 { 207 char invoker[B_FILE_NAME_LENGTH]; 208 char **newArgs = NULL; 209 int32 envCount = 0; 210 thread_id thread; 211 212 if (argCount < 1 || environ == NULL) 213 return B_BAD_VALUE; 214 215 // test validity of executable + support for scripts 216 { 217 status_t status = __test_executable(path, invoker); 218 if (status < B_OK) 219 return status; 220 221 if (invoker[0]) { 222 status = __parse_invoke_line(invoker, &newArgs, 223 (char * const **)&args, &argCount, path); 224 if (status < B_OK) 225 return status; 226 } 227 } 228 229 // count environment variables 230 while (environ[envCount] != NULL) 231 envCount++; 232 233 char** flatArgs = NULL; 234 size_t flatArgsSize; 235 status_t status = __flatten_process_args(args, argCount, environ, 236 &envCount, path, &flatArgs, &flatArgsSize); 237 238 if (status == B_OK) { 239 thread = _kern_load_image(flatArgs, flatArgsSize, argCount, envCount, 240 B_NORMAL_PRIORITY, B_WAIT_TILL_LOADED, -1, 0); 241 242 free(flatArgs); 243 } else 244 thread = status; 245 246 free(newArgs); 247 return thread; 248 } 249 250 251 thread_id 252 load_image(int32 argCount, const char **args, const char **environ) 253 { 254 return __load_image_at_path(args[0], argCount, args, environ); 255 } 256 257 258 image_id 259 load_add_on(char const *name) 260 { 261 if (name == NULL) 262 return B_BAD_VALUE; 263 264 return __gRuntimeLoader->load_add_on(name, 0); 265 } 266 267 268 status_t 269 unload_add_on(image_id id) 270 { 271 return __gRuntimeLoader->unload_add_on(id); 272 } 273 274 275 status_t 276 get_image_symbol(image_id id, char const *symbolName, int32 symbolType, 277 void **_location) 278 { 279 return __gRuntimeLoader->get_image_symbol(id, symbolName, symbolType, 280 false, NULL, _location); 281 } 282 283 284 status_t 285 get_image_symbol_etc(image_id id, char const *symbolName, int32 symbolType, 286 bool recursive, image_id *_inImage, void **_location) 287 { 288 return __gRuntimeLoader->get_image_symbol(id, symbolName, symbolType, 289 recursive, _inImage, _location); 290 } 291 292 293 status_t 294 get_nth_image_symbol(image_id id, int32 num, char *nameBuffer, int32 *_nameLength, 295 int32 *_symbolType, void **_location) 296 { 297 return __gRuntimeLoader->get_nth_image_symbol(id, num, nameBuffer, _nameLength, _symbolType, _location); 298 } 299 300 301 status_t 302 _get_image_info(image_id id, image_info *info, size_t infoSize) 303 { 304 return _kern_get_image_info(id, info, infoSize); 305 } 306 307 308 status_t 309 _get_next_image_info(team_id team, int32 *cookie, image_info *info, size_t infoSize) 310 { 311 return _kern_get_next_image_info(team, cookie, info, infoSize); 312 } 313 314 315 void 316 clear_caches(void *address, size_t length, uint32 flags) 317 { 318 _kern_clear_caches(address, length, flags); 319 } 320 321 322 // #pragma mark - 323 324 325 status_t 326 __look_up_in_path(const char *file, char *buffer) 327 { 328 // get the PATH 329 const char* paths = getenv("PATH"); 330 if (paths == NULL) 331 return B_ENTRY_NOT_FOUND; 332 333 int fileNameLen = strlen(file); 334 335 // iterate through the paths 336 const char* pathEnd = paths - 1; 337 while (pathEnd != NULL) { 338 paths = pathEnd + 1; 339 pathEnd = strchr(paths, ':'); 340 int pathLen = (pathEnd ? pathEnd - paths : strlen(paths)); 341 342 // We skip empty paths and those that would become too long. 343 // The latter is not really correct, but practically irrelevant. 344 if (pathLen == 0 345 || pathLen + 1 + fileNameLen >= B_PATH_NAME_LENGTH) { 346 continue; 347 } 348 349 // concatinate the program path 350 char path[B_PATH_NAME_LENGTH]; 351 memcpy(path, paths, pathLen); 352 path[pathLen] = '\0'; 353 354 if (path[pathLen - 1] != '/') 355 strcat(path, "/"); 356 strcat(path, file); 357 358 // check whether it is a file 359 struct stat st; 360 if (stat(path, &st) != 0 || !S_ISREG(st.st_mode)) 361 continue; 362 363 // if executable, we've found what we are looking for 364 if (access(path, X_OK) == 0) { 365 strlcpy(buffer, path, B_PATH_NAME_LENGTH); 366 return B_OK; 367 } 368 } 369 370 return B_ENTRY_NOT_FOUND; 371 } 372 373 374 static char * 375 next_argument(char **_start, bool separate) 376 { 377 char *line = *_start; 378 char quote = 0; 379 int32 i; 380 381 // eliminate leading spaces 382 while (line[0] == ' ') 383 line++; 384 385 if (line[0] == '"' || line[0] == '\'') { 386 quote = line[0]; 387 line++; 388 } 389 390 if (!line[0]) 391 return NULL; 392 393 for (i = 0;; i++) { 394 if (line[i] == '\\' && line[i + 1] != '\0') 395 continue; 396 397 if (line[i] == '\0') { 398 *_start = &line[i]; 399 return line; 400 } 401 if ((!quote && line[i] == ' ') || line[i] == quote) { 402 // argument separator! 403 if (separate) 404 line[i] = '\0'; 405 *_start = &line[i + 1]; 406 return line; 407 } 408 } 409 410 return NULL; 411 } 412 413 414 status_t 415 __parse_invoke_line(char *invoker, char ***_newArgs, 416 char * const **_oldArgs, int32 *_argCount, const char *arg0) 417 { 418 int32 i, count = 0; 419 char *arg = invoker; 420 char **newArgs; 421 422 // count arguments in the line 423 424 while (next_argument(&arg, false)) { 425 count++; 426 } 427 428 // this is a shell script and requires special treatment 429 newArgs = (char**)malloc((*_argCount + count + 1) * sizeof(void *)); 430 if (newArgs == NULL) 431 return B_NO_MEMORY; 432 433 // copy invoker and old arguments and to newArgs 434 435 for (i = 0; (arg = next_argument(&invoker, true)) != NULL; i++) { 436 newArgs[i] = arg; 437 } 438 for (i = 0; i < *_argCount; i++) { 439 if (i == 0) 440 newArgs[i + count] = (char*)arg0; 441 else 442 newArgs[i + count] = (char *)(*_oldArgs)[i]; 443 } 444 445 newArgs[i + count] = NULL; 446 447 *_newArgs = newArgs; 448 *_oldArgs = (char * const *)newArgs; 449 *_argCount += count; 450 451 return B_OK; 452 } 453 454 455 status_t 456 __get_next_image_dependency(image_id id, uint32 *cookie, const char **_name) 457 { 458 return __gRuntimeLoader->get_next_image_dependency(id, cookie, _name); 459 } 460 461 462 status_t 463 __test_executable(const char *path, char *invoker) 464 { 465 return __gRuntimeLoader->test_executable(path, invoker); 466 } 467 468 469 /*! Allocates a flat buffer and copies the argument and environment strings 470 into it. The buffer starts with a char* array which contains pointers to 471 the strings of the arguments and environment, followed by the strings. Both 472 arguments and environment arrays are NULL-terminated. 473 474 If executablePath is non-NULL, it should refer to the executable to be 475 executed. If the executable file specifies changes to environment variable 476 values, those will be performed. 477 */ 478 status_t 479 __flatten_process_args(const char* const* args, int32 argCount, 480 const char* const* env, int32* _envCount, const char* executablePath, 481 char*** _flatArgs, size_t* _flatSize) 482 { 483 if (args == NULL || _envCount == NULL || (env == NULL && *_envCount != 0)) 484 return B_BAD_VALUE; 485 486 int32 envCount = *_envCount; 487 488 // determine total needed size 489 int32 argSize = 0; 490 for (int32 i = 0; i < argCount; i++) { 491 if (args[i] == NULL) 492 return B_BAD_VALUE; 493 argSize += strlen(args[i]) + 1; 494 } 495 496 int32 envSize = 0; 497 for (int32 i = 0; i < envCount; i++) { 498 if (env[i] == NULL) 499 return B_BAD_VALUE; 500 envSize += strlen(env[i]) + 1; 501 } 502 503 EnvironmentFilter envFilter; 504 if (executablePath != NULL) 505 envFilter.Init(executablePath, env, envCount); 506 507 int32 totalSlotCount = argCount + envCount + 2 508 + envFilter.AdditionalSlotsNeeded(); 509 int32 size = totalSlotCount * sizeof(char*) + argSize + envSize 510 + envFilter.AdditionalSizeNeeded(); 511 if (size > MAX_PROCESS_ARGS_SIZE) 512 return B_TOO_MANY_ARGS; 513 514 // allocate space 515 char** flatArgs = (char**)malloc(size); 516 if (flatArgs == NULL) 517 return B_NO_MEMORY; 518 519 char** slot = flatArgs; 520 char* stringSpace = (char*)(flatArgs + totalSlotCount); 521 522 // copy arguments and environment 523 for (int32 i = 0; i < argCount; i++) { 524 int32 argSize = strlen(args[i]) + 1; 525 memcpy(stringSpace, args[i], argSize); 526 *slot++ = stringSpace; 527 stringSpace += argSize; 528 } 529 530 *slot++ = NULL; 531 532 for (int32 i = 0; i < envCount; i++) { 533 size_t envSize = envFilter.PrepareSlot(env[i], i, stringSpace); 534 *slot++ = stringSpace; 535 stringSpace += envSize; 536 } 537 538 envFilter.PrepareAdditionalSlots(slot, stringSpace); 539 540 *slot++ = NULL; 541 542 *_envCount = envCount + envFilter.AdditionalSlotsNeeded(); 543 *_flatArgs = flatArgs; 544 *_flatSize = stringSpace - (char*)flatArgs; 545 return B_OK; 546 } 547 548 549 extern "C" void _call_init_routines_(void); 550 void 551 _call_init_routines_(void) 552 { 553 // This is called by the original BeOS startup code. 554 // We don't need it, because our loader already does the job, right? 555 } 556 557