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