headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / libroot / os / image.cpp
blobb59a6d4117be273ea8a8fb667197afd2846df333
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 */
6 #include <image.h>
7 #include <image_private.h>
9 #include <stdlib.h>
10 #include <string.h>
12 #include <algorithm>
13 #include <new>
15 #include <fs_attr.h>
17 #include <AutoDeleter.h>
19 #include <libroot_private.h>
20 #include <runtime_loader.h>
21 #include <syscalls.h>
22 #include <user_runtime.h>
25 struct EnvironmentFilter {
26 EnvironmentFilter()
28 fBuffer(NULL),
29 fEntries(NULL),
30 fBufferSize(0),
31 fEntryCount(0),
32 fAdditionalEnvCount(0),
33 fNextEntryIndex(0)
37 ~EnvironmentFilter()
39 free(fBuffer);
40 delete[] fEntries;
43 void Init(const char* path, const char* const* env, size_t envCount)
45 int fd = open(path, O_RDONLY);
46 if (fd < 0)
47 return;
48 FileDescriptorCloser fdCloser(fd);
50 static const char* const kEnvAttribute = "SYS:ENV";
51 attr_info info;
52 if (fs_stat_attr(fd, kEnvAttribute, &info) < 0)
53 return;
55 _Init(fd, kEnvAttribute, info.size, env, envCount);
58 size_t AdditionalSlotsNeeded() const
60 return fAdditionalEnvCount;
63 size_t AdditionalSizeNeeded() const
65 return fBufferSize + fAdditionalEnvCount * sizeof(char*);
68 size_t PrepareSlot(const char* env, int32 index, char* buffer)
70 if (fNextEntryIndex < fEntryCount
71 && fEntries[fNextEntryIndex].index == index) {
72 env = fEntries[fNextEntryIndex].replacement;
73 fNextEntryIndex++;
76 return _FillSlot(env, buffer);
79 void PrepareAdditionalSlots(char**& slot, char*& buffer)
81 for (size_t i = 0; i < fAdditionalEnvCount; i++) {
82 size_t envSize = _FillSlot(fEntries[i].replacement, buffer);
83 *slot++ = buffer;
84 buffer += envSize;
88 private:
89 void _Init(int fd, const char* attribute, size_t size,
90 const char* const* env, size_t envCount)
92 if (size == 0)
93 return;
95 // read the attribute
96 char* buffer = (char*)malloc(size + 1);
97 if (buffer == NULL)
98 return;
99 MemoryDeleter bufferDeleter(buffer);
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';
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;
123 *out++ = '\0';
124 size = out - buffer + 1;
126 // create an entry array
127 fEntries = new(std::nothrow) Entry[entryCount];
128 if (fEntries == NULL)
129 return;
131 bufferDeleter.Detach();
132 fBuffer = buffer;
133 fBufferSize = size;
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++;
147 out += strlen(out) + 1;
150 if (fEntryCount > 1)
151 std::sort(fEntries, fEntries + fEntryCount);
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++;
161 int32 _FindEnvEntry(const char* const* env, size_t envCount,
162 const char* variable, size_t variableLength)
164 for (size_t i = 0; i < envCount; i++) {
165 if (strncmp(env[i], variable, variableLength) == 0
166 && env[i][variableLength] == '=') {
167 return i;
171 return -1;
174 size_t _FillSlot(const char* env, char* buffer)
176 size_t envSize = strlen(env) + 1;
177 memcpy(buffer, env, envSize);
178 return envSize;
181 private:
182 struct Entry {
183 char* replacement;
184 int32 index;
186 bool operator<(const Entry& other) const
188 return index < other.index;
192 private:
193 char* fBuffer;
194 Entry* fEntries;
195 size_t fBufferSize;
196 size_t fEntryCount;
197 size_t fAdditionalEnvCount;
198 size_t fNextEntryIndex;
202 thread_id
203 load_image(int32 argCount, const char **args, const char **environ)
205 char invoker[B_FILE_NAME_LENGTH];
206 char **newArgs = NULL;
207 int32 envCount = 0;
208 thread_id thread;
210 if (argCount < 1 || environ == NULL)
211 return B_BAD_VALUE;
213 // test validity of executable + support for scripts
215 status_t status = __test_executable(args[0], invoker);
216 if (status < B_OK)
217 return status;
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;
227 // count environment variables
228 while (environ[envCount] != NULL)
229 envCount++;
231 char** flatArgs = NULL;
232 size_t flatArgsSize;
233 status_t status = __flatten_process_args(args, argCount, environ,
234 &envCount, args[0], &flatArgs, &flatArgsSize);
236 if (status == B_OK) {
237 thread = _kern_load_image(flatArgs, flatArgsSize, argCount, envCount,
238 B_NORMAL_PRIORITY, B_WAIT_TILL_LOADED, -1, 0);
240 free(flatArgs);
241 } else
242 thread = status;
244 free(newArgs);
245 return thread;
249 image_id
250 load_add_on(char const *name)
252 if (name == NULL)
253 return B_BAD_VALUE;
255 return __gRuntimeLoader->load_add_on(name, 0);
259 status_t
260 unload_add_on(image_id id)
262 return __gRuntimeLoader->unload_add_on(id);
266 status_t
267 get_image_symbol(image_id id, char const *symbolName, int32 symbolType,
268 void **_location)
270 return __gRuntimeLoader->get_image_symbol(id, symbolName, symbolType,
271 false, NULL, _location);
275 status_t
276 get_image_symbol_etc(image_id id, char const *symbolName, int32 symbolType,
277 bool recursive, image_id *_inImage, void **_location)
279 return __gRuntimeLoader->get_image_symbol(id, symbolName, symbolType,
280 recursive, _inImage, _location);
284 status_t
285 get_nth_image_symbol(image_id id, int32 num, char *nameBuffer, int32 *_nameLength,
286 int32 *_symbolType, void **_location)
288 return __gRuntimeLoader->get_nth_image_symbol(id, num, nameBuffer, _nameLength, _symbolType, _location);
292 status_t
293 _get_image_info(image_id id, image_info *info, size_t infoSize)
295 return _kern_get_image_info(id, info, infoSize);
299 status_t
300 _get_next_image_info(team_id team, int32 *cookie, image_info *info, size_t infoSize)
302 return _kern_get_next_image_info(team, cookie, info, infoSize);
306 void
307 clear_caches(void *address, size_t length, uint32 flags)
309 _kern_clear_caches(address, length, flags);
313 // #pragma mark -
316 static char *
317 next_argument(char **_start, bool separate)
319 char *line = *_start;
320 char quote = 0;
321 int32 i;
323 // eliminate leading spaces
324 while (line[0] == ' ')
325 line++;
327 if (line[0] == '"' || line[0] == '\'') {
328 quote = line[0];
329 line++;
332 if (!line[0])
333 return NULL;
335 for (i = 0;; i++) {
336 if (line[i] == '\\' && line[i + 1] != '\0')
337 continue;
339 if (line[i] == '\0') {
340 *_start = &line[i];
341 return line;
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;
352 return NULL;
356 status_t
357 __parse_invoke_line(char *invoker, char ***_newArgs,
358 char * const **_oldArgs, int32 *_argCount, const char *arg0)
360 int32 i, count = 0;
361 char *arg = invoker;
362 char **newArgs;
364 // count arguments in the line
366 while (next_argument(&arg, false)) {
367 count++;
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;
375 // copy invoker and old arguments and to newArgs
377 for (i = 0; (arg = next_argument(&invoker, true)) != NULL; i++) {
378 newArgs[i] = arg;
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];
387 newArgs[i + count] = NULL;
389 *_newArgs = newArgs;
390 *_oldArgs = (char * const *)newArgs;
391 *_argCount += count;
393 return B_OK;
397 status_t
398 __get_next_image_dependency(image_id id, uint32 *cookie, const char **_name)
400 return __gRuntimeLoader->get_next_image_dependency(id, cookie, _name);
404 status_t
405 __test_executable(const char *path, char *invoker)
407 return __gRuntimeLoader->test_executable(path, invoker);
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.
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.
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)
425 if (args == NULL || _envCount == NULL || (env == NULL && *_envCount != 0))
426 return B_BAD_VALUE;
428 int32 envCount = *_envCount;
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;
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;
445 EnvironmentFilter envFilter;
446 if (executablePath != NULL)
447 envFilter.Init(executablePath, env, envCount);
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;
456 // allocate space
457 char** flatArgs = (char**)malloc(size);
458 if (flatArgs == NULL)
459 return B_NO_MEMORY;
461 char** slot = flatArgs;
462 char* stringSpace = (char*)(flatArgs + totalSlotCount);
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;
472 *slot++ = NULL;
474 for (int32 i = 0; i < envCount; i++) {
475 size_t envSize = envFilter.PrepareSlot(env[i], i, stringSpace);
476 *slot++ = stringSpace;
477 stringSpace += envSize;
480 envFilter.PrepareAdditionalSlots(slot, stringSpace);
482 *slot++ = NULL;
484 *_envCount = envCount + envFilter.AdditionalSlotsNeeded();
485 *_flatArgs = flatArgs;
486 *_flatSize = stringSpace - (char*)flatArgs;
487 return B_OK;
491 extern "C" void _call_init_routines_(void);
492 void
493 _call_init_routines_(void)
495 // This is called by the original BeOS startup code.
496 // We don't need it, because our loader already does the job, right?