2 * Copyright 2003-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 * Distributed under the terms of the MIT License.
7 #include <image_private.h>
17 #include <AutoDeleter.h>
19 #include <libroot_private.h>
20 #include <runtime_loader.h>
22 #include <user_runtime.h>
25 struct EnvironmentFilter
{
32 fAdditionalEnvCount(0),
43 void Init(const char* path
, const char* const* env
, size_t envCount
)
45 int fd
= open(path
, O_RDONLY
);
48 FileDescriptorCloser
fdCloser(fd
);
50 static const char* const kEnvAttribute
= "SYS:ENV";
52 if (fs_stat_attr(fd
, kEnvAttribute
, &info
) < 0)
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
;
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
);
89 void _Init(int fd
, const char* attribute
, size_t size
,
90 const char* const* env
, size_t envCount
)
96 char* buffer
= (char*)malloc(size
+ 1);
99 MemoryDeleter
bufferDeleter(buffer
);
101 ssize_t bytesRead
= fs_read_attr(fd
, attribute
, B_STRING_TYPE
, 0,
103 if (bytesRead
< 0 || (size_t)bytesRead
!= size
)
107 // deescape the buffer and count the entries
108 size_t entryCount
= 1;
110 for (const char* c
= buffer
; *c
!= '\0'; c
++) {
124 size
= out
- buffer
+ 1;
126 // create an entry array
127 fEntries
= new(std::nothrow
) Entry
[entryCount
];
128 if (fEntries
== NULL
)
131 bufferDeleter
.Detach();
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
,
143 if (fEntries
[fEntryCount
].index
< 0)
144 fAdditionalEnvCount
++;
147 out
+= strlen(out
) + 1;
151 std::sort(fEntries
, fEntries
+ fEntryCount
);
153 // Advance fNextEntryIndex to the first entry pointing to an existing
155 while (fNextEntryIndex
< fEntryCount
156 && fEntries
[fNextEntryIndex
].index
< 0) {
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
] == '=') {
174 size_t _FillSlot(const char* env
, char* buffer
)
176 size_t envSize
= strlen(env
) + 1;
177 memcpy(buffer
, env
, envSize
);
186 bool operator<(const Entry
& other
) const
188 return index
< other
.index
;
197 size_t fAdditionalEnvCount
;
198 size_t fNextEntryIndex
;
203 load_image(int32 argCount
, const char **args
, const char **environ
)
205 char invoker
[B_FILE_NAME_LENGTH
];
206 char **newArgs
= NULL
;
210 if (argCount
< 1 || environ
== NULL
)
213 // test validity of executable + support for scripts
215 status_t status
= __test_executable(args
[0], invoker
);
220 status
= __parse_invoke_line(invoker
, &newArgs
,
221 (char * const **)&args
, &argCount
, args
[0]);
227 // count environment variables
228 while (environ
[envCount
] != NULL
)
231 char** flatArgs
= NULL
;
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);
250 load_add_on(char const *name
)
255 return __gRuntimeLoader
->load_add_on(name
, 0);
260 unload_add_on(image_id id
)
262 return __gRuntimeLoader
->unload_add_on(id
);
267 get_image_symbol(image_id id
, char const *symbolName
, int32 symbolType
,
270 return __gRuntimeLoader
->get_image_symbol(id
, symbolName
, symbolType
,
271 false, NULL
, _location
);
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
);
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
);
293 _get_image_info(image_id id
, image_info
*info
, size_t infoSize
)
295 return _kern_get_image_info(id
, info
, infoSize
);
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
);
307 clear_caches(void *address
, size_t length
, uint32 flags
)
309 _kern_clear_caches(address
, length
, flags
);
317 next_argument(char **_start
, bool separate
)
319 char *line
= *_start
;
323 // eliminate leading spaces
324 while (line
[0] == ' ')
327 if (line
[0] == '"' || line
[0] == '\'') {
336 if (line
[i
] == '\\' && line
[i
+ 1] != '\0')
339 if (line
[i
] == '\0') {
343 if ((!quote
&& line
[i
] == ' ') || line
[i
] == quote
) {
344 // argument separator!
347 *_start
= &line
[i
+ 1];
357 __parse_invoke_line(char *invoker
, char ***_newArgs
,
358 char * const **_oldArgs
, int32
*_argCount
, const char *arg0
)
364 // count arguments in the line
366 while (next_argument(&arg
, false)) {
370 // this is a shell script and requires special treatment
371 newArgs
= (char**)malloc((*_argCount
+ count
+ 1) * sizeof(void *));
375 // copy invoker and old arguments and to newArgs
377 for (i
= 0; (arg
= next_argument(&invoker
, true)) != NULL
; i
++) {
380 for (i
= 0; i
< *_argCount
; i
++) {
382 newArgs
[i
+ count
] = (char*)arg0
;
384 newArgs
[i
+ count
] = (char *)(*_oldArgs
)[i
];
387 newArgs
[i
+ count
] = NULL
;
390 *_oldArgs
= (char * const *)newArgs
;
398 __get_next_image_dependency(image_id id
, uint32
*cookie
, const char **_name
)
400 return __gRuntimeLoader
->get_next_image_dependency(id
, cookie
, _name
);
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.
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))
428 int32 envCount
= *_envCount
;
430 // determine total needed size
432 for (int32 i
= 0; i
< argCount
; i
++) {
435 argSize
+= strlen(args
[i
]) + 1;
439 for (int32 i
= 0; i
< envCount
; i
++) {
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
;
457 char** flatArgs
= (char**)malloc(size
);
458 if (flatArgs
== NULL
)
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
;
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
);
484 *_envCount
= envCount
+ envFilter
.AdditionalSlotsNeeded();
485 *_flatArgs
= flatArgs
;
486 *_flatSize
= stringSpace
- (char*)flatArgs
;
491 extern "C" void _call_init_routines_(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?