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 <user_runtime.h> 23 24 25 struct EnvironmentFilter { 26 EnvironmentFilter() 27 : 28 fBuffer(NULL), 29 fEntries(NULL), 30 fBufferSize(0), 31 fEntryCount(0), 32 fAdditionalEnvCount(0), 33 fNextEntryIndex(0) 34 { 35 } 36 37 ~EnvironmentFilter() 38 { 39 free(fBuffer); 40 delete[] fEntries; 41 } 42 43 void Init(const char* path, const char* const* env, size_t envCount) 44 { 45 int fd = open(path, O_RDONLY); 46 if (fd < 0) 47 return; 48 FileDescriptorCloser fdCloser(fd); 49 50 static const char* const kEnvAttribute = "SYS:ENV"; 51 attr_info info; 52 if (fs_stat_attr(fd, kEnvAttribute, &info) < 0) 53 return; 54 55 _Init(fd, 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(int32 argCount, const char **args, const char **environ) 204 { 205 char invoker[B_FILE_NAME_LENGTH]; 206 char **newArgs = NULL; 207 int32 envCount = 0; 208 thread_id thread; 209 210 if (argCount < 1 || environ == NULL) 211 return B_BAD_VALUE; 212 213 // test validity of executable + support for scripts 214 { 215 status_t status = __test_executable(args[0], invoker); 216 if (status < B_OK) 217 return status; 218 219 if (invoker[0]) { 220 status = __parse_invoke_line(invoker, &newArgs, 221 (char * const **)&args, &argCount, args[0]); 222 if (status < B_OK) 223 return status; 224 } 225 } 226 227 // count environment variables 228 while (environ[envCount] != NULL) 229 envCount++; 230 231 char** flatArgs = NULL; 232 size_t flatArgsSize; 233 status_t status = __flatten_process_args(args, argCount, environ, 234 &envCount, args[0], &flatArgs, &flatArgsSize); 235 236 if (status == B_OK) { 237 thread = _kern_load_image(flatArgs, flatArgsSize, argCount, envCount, 238 B_NORMAL_PRIORITY, B_WAIT_TILL_LOADED, -1, 0); 239 240 free(flatArgs); 241 } else 242 thread = status; 243 244 free(newArgs); 245 return thread; 246 } 247 248 249 image_id 250 load_add_on(char const *name) 251 { 252 if (name == NULL) 253 return B_BAD_VALUE; 254 255 return __gRuntimeLoader->load_add_on(name, 0); 256 } 257 258 259 status_t 260 unload_add_on(image_id id) 261 { 262 return __gRuntimeLoader->unload_add_on(id); 263 } 264 265 266 status_t 267 get_image_symbol(image_id id, char const *symbolName, int32 symbolType, 268 void **_location) 269 { 270 return __gRuntimeLoader->get_image_symbol(id, symbolName, symbolType, 271 false, NULL, _location); 272 } 273 274 275 status_t 276 get_image_symbol_etc(image_id id, char const *symbolName, int32 symbolType, 277 bool recursive, image_id *_inImage, void **_location) 278 { 279 return __gRuntimeLoader->get_image_symbol(id, symbolName, symbolType, 280 recursive, _inImage, _location); 281 } 282 283 284 status_t 285 get_nth_image_symbol(image_id id, int32 num, char *nameBuffer, int32 *_nameLength, 286 int32 *_symbolType, void **_location) 287 { 288 return __gRuntimeLoader->get_nth_image_symbol(id, num, nameBuffer, _nameLength, _symbolType, _location); 289 } 290 291 292 status_t 293 _get_image_info(image_id id, image_info *info, size_t infoSize) 294 { 295 return _kern_get_image_info(id, info, infoSize); 296 } 297 298 299 status_t 300 _get_next_image_info(team_id team, int32 *cookie, image_info *info, size_t infoSize) 301 { 302 return _kern_get_next_image_info(team, cookie, info, infoSize); 303 } 304 305 306 void 307 clear_caches(void *address, size_t length, uint32 flags) 308 { 309 _kern_clear_caches(address, length, flags); 310 } 311 312 313 // #pragma mark - 314 315 316 static char * 317 next_argument(char **_start, bool separate) 318 { 319 char *line = *_start; 320 char quote = 0; 321 int32 i; 322 323 // eliminate leading spaces 324 while (line[0] == ' ') 325 line++; 326 327 if (line[0] == '"' || line[0] == '\'') { 328 quote = line[0]; 329 line++; 330 } 331 332 if (!line[0]) 333 return NULL; 334 335 for (i = 0;; i++) { 336 if (line[i] == '\\' && line[i + 1] != '\0') 337 continue; 338 339 if (line[i] == '\0') { 340 *_start = &line[i]; 341 return line; 342 } 343 if ((!quote && line[i] == ' ') || line[i] == quote) { 344 // argument separator! 345 if (separate) 346 line[i] = '\0'; 347 *_start = &line[i + 1]; 348 return line; 349 } 350 } 351 352 return NULL; 353 } 354 355 356 status_t 357 __parse_invoke_line(char *invoker, char ***_newArgs, 358 char * const **_oldArgs, int32 *_argCount, const char *arg0) 359 { 360 int32 i, count = 0; 361 char *arg = invoker; 362 char **newArgs; 363 364 // count arguments in the line 365 366 while (next_argument(&arg, false)) { 367 count++; 368 } 369 370 // this is a shell script and requires special treatment 371 newArgs = (char**)malloc((*_argCount + count + 1) * sizeof(void *)); 372 if (newArgs == NULL) 373 return B_NO_MEMORY; 374 375 // copy invoker and old arguments and to newArgs 376 377 for (i = 0; (arg = next_argument(&invoker, true)) != NULL; i++) { 378 newArgs[i] = arg; 379 } 380 for (i = 0; i < *_argCount; i++) { 381 if (i == 0) 382 newArgs[i + count] = (char*)arg0; 383 else 384 newArgs[i + count] = (char *)(*_oldArgs)[i]; 385 } 386 387 newArgs[i + count] = NULL; 388 389 *_newArgs = newArgs; 390 *_oldArgs = (char * const *)newArgs; 391 *_argCount += count; 392 393 return B_OK; 394 } 395 396 397 status_t 398 __get_next_image_dependency(image_id id, uint32 *cookie, const char **_name) 399 { 400 return __gRuntimeLoader->get_next_image_dependency(id, cookie, _name); 401 } 402 403 404 status_t 405 __test_executable(const char *path, char *invoker) 406 { 407 return __gRuntimeLoader->test_executable(path, invoker); 408 } 409 410 411 /*! Allocates a flat buffer and copies the argument and environment strings 412 into it. The buffer starts with a char* array which contains pointers to 413 the strings of the arguments and environment, followed by the strings. Both 414 arguments and environment arrays are NULL-terminated. 415 416 If executablePath is non-NULL, it should refer to the executable to be 417 executed. If the executable file specifies changes to environment variable 418 values, those will be performed. 419 */ 420 status_t 421 __flatten_process_args(const char* const* args, int32 argCount, 422 const char* const* env, int32* _envCount, const char* executablePath, 423 char*** _flatArgs, size_t* _flatSize) 424 { 425 if (args == NULL || _envCount == NULL || (env == NULL && *_envCount != 0)) 426 return B_BAD_VALUE; 427 428 int32 envCount = *_envCount; 429 430 // determine total needed size 431 int32 argSize = 0; 432 for (int32 i = 0; i < argCount; i++) { 433 if (args[i] == NULL) 434 return B_BAD_VALUE; 435 argSize += strlen(args[i]) + 1; 436 } 437 438 int32 envSize = 0; 439 for (int32 i = 0; i < envCount; i++) { 440 if (env[i] == NULL) 441 return B_BAD_VALUE; 442 envSize += strlen(env[i]) + 1; 443 } 444 445 EnvironmentFilter envFilter; 446 if (executablePath != NULL) 447 envFilter.Init(executablePath, env, envCount); 448 449 int32 totalSlotCount = argCount + envCount + 2 450 + envFilter.AdditionalSlotsNeeded(); 451 int32 size = totalSlotCount * sizeof(char*) + argSize + envSize 452 + envFilter.AdditionalSizeNeeded(); 453 if (size > MAX_PROCESS_ARGS_SIZE) 454 return B_TOO_MANY_ARGS; 455 456 // allocate space 457 char** flatArgs = (char**)malloc(size); 458 if (flatArgs == NULL) 459 return B_NO_MEMORY; 460 461 char** slot = flatArgs; 462 char* stringSpace = (char*)(flatArgs + totalSlotCount); 463 464 // copy arguments and environment 465 for (int32 i = 0; i < argCount; i++) { 466 int32 argSize = strlen(args[i]) + 1; 467 memcpy(stringSpace, args[i], argSize); 468 *slot++ = stringSpace; 469 stringSpace += argSize; 470 } 471 472 *slot++ = NULL; 473 474 for (int32 i = 0; i < envCount; i++) { 475 size_t envSize = envFilter.PrepareSlot(env[i], i, stringSpace); 476 *slot++ = stringSpace; 477 stringSpace += envSize; 478 } 479 480 envFilter.PrepareAdditionalSlots(slot, stringSpace); 481 482 *slot++ = NULL; 483 484 *_envCount = envCount + envFilter.AdditionalSlotsNeeded(); 485 *_flatArgs = flatArgs; 486 *_flatSize = stringSpace - (char*)flatArgs; 487 return B_OK; 488 } 489 490 491 extern "C" void _call_init_routines_(void); 492 void 493 _call_init_routines_(void) 494 { 495 // This is called by the original BeOS startup code. 496 // We don't need it, because our loader already does the job, right? 497 } 498 499