Apparently, both load_image() and exec*() support running scripts. We do that
now as well.
Also, both functions will now test if the executable exists and is valid; that
way, load_image()/exec*() can catch many errors without having to create a new
team (or erase the current one - an exec*("my invalid app") might now return
with an error).
The runtime linker now exports a function to test executables that is aware
of the search paths, and will also check user permissions upfront.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@13113 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,13 +1,18 @@
|
|||||||
/*
|
/*
|
||||||
** Copyright 2002, Manuel J. Petit. All rights reserved.
|
* Copyright 2003-2005, Axel Dörfler, [email protected].
|
||||||
** Distributed under the terms of the NewOS License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*
|
||||||
|
* Copyright 2002, Manuel J. Petit. All rights reserved.
|
||||||
|
* Distributed under the terms of the NewOS License.
|
||||||
|
*/
|
||||||
#ifndef KERNEL_USER_RUNTIME_H_
|
#ifndef KERNEL_USER_RUNTIME_H_
|
||||||
#define KERNEL_USER_RUNTIME_H_
|
#define KERNEL_USER_RUNTIME_H_
|
||||||
|
|
||||||
|
|
||||||
#include <image.h>
|
#include <image.h>
|
||||||
#include <OS.h>
|
#include <OS.h>
|
||||||
|
|
||||||
|
|
||||||
#define MAGIC_APP_NAME "_APP_"
|
#define MAGIC_APP_NAME "_APP_"
|
||||||
|
|
||||||
struct rld_export {
|
struct rld_export {
|
||||||
@@ -18,6 +23,8 @@ struct rld_export {
|
|||||||
int32 symbolType, void **_location);
|
int32 symbolType, void **_location);
|
||||||
status_t (*get_nth_image_symbol)(image_id imageID, int32 num, char *symbolName,
|
status_t (*get_nth_image_symbol)(image_id imageID, int32 num, char *symbolName,
|
||||||
int32 *nameLength, int32 *symbolType, void **_location);
|
int32 *nameLength, int32 *symbolType, void **_location);
|
||||||
|
status_t (*test_executable)(const char *path, uid_t user, gid_t group,
|
||||||
|
char *starter);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct uspace_program_args {
|
struct uspace_program_args {
|
||||||
@@ -34,6 +41,4 @@ struct uspace_program_args {
|
|||||||
struct rld_export *rld_export;
|
struct rld_export *rld_export;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void (libinit_f)(unsigned, struct uspace_program_args const *);
|
|
||||||
|
|
||||||
#endif /* KERNEL_USER_RUNTIME_H_ */
|
#endif /* KERNEL_USER_RUNTIME_H_ */
|
||||||
|
|||||||
@@ -6,9 +6,13 @@
|
|||||||
#define LIBROOT_PRIVATE_H
|
#define LIBROOT_PRIVATE_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
|
||||||
struct uspace_program_args;
|
struct uspace_program_args;
|
||||||
struct real_time_data;
|
struct real_time_data;
|
||||||
|
|
||||||
|
status_t __test_executable(const char *path, char *starter);
|
||||||
void __init_image(const struct uspace_program_args *args);
|
void __init_image(const struct uspace_program_args *args);
|
||||||
void __init_dlfcn(const struct uspace_program_args *args);
|
void __init_dlfcn(const struct uspace_program_args *args);
|
||||||
void __init_env(const struct uspace_program_args *args);
|
void __init_env(const struct uspace_program_args *args);
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
#include <OS.h>
|
#include <OS.h>
|
||||||
#include <image.h>
|
#include <image.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
static struct rld_export const *sRuntimeLinker;
|
static struct rld_export const *sRuntimeLinker;
|
||||||
|
|
||||||
@@ -18,17 +20,48 @@ static struct rld_export const *sRuntimeLinker;
|
|||||||
thread_id
|
thread_id
|
||||||
load_image(int32 argCount, const char **args, const char **environ)
|
load_image(int32 argCount, const char **args, const char **environ)
|
||||||
{
|
{
|
||||||
|
char starter[B_FILE_NAME_LENGTH];
|
||||||
|
const char **newArgs = NULL;
|
||||||
int32 envCount = 0;
|
int32 envCount = 0;
|
||||||
|
thread_id thread;
|
||||||
|
|
||||||
if (argCount < 1 || environ == NULL)
|
if (argCount < 1 || environ == NULL)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
// test validity of executable + support for scripts
|
||||||
|
{
|
||||||
|
status_t status = __test_executable(args[0], starter);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
if (starter[0]) {
|
||||||
|
int32 i;
|
||||||
|
|
||||||
|
// this is a shell script and requires special treatment
|
||||||
|
newArgs = malloc((argCount + 2) * sizeof(void *));
|
||||||
|
if (newArgs == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
// copy args and have "starter" as new app
|
||||||
|
newArgs[0] = starter;
|
||||||
|
for (i = 0; i < argCount; i++)
|
||||||
|
newArgs[i + 1] = args[i];
|
||||||
|
newArgs[i + 1] = NULL;
|
||||||
|
|
||||||
|
args = newArgs;
|
||||||
|
argCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// count environment variables
|
// count environment variables
|
||||||
while (environ[envCount] != NULL)
|
while (environ[envCount] != NULL)
|
||||||
envCount++;
|
envCount++;
|
||||||
|
|
||||||
return _kern_load_image(argCount, args, envCount, environ,
|
thread = _kern_load_image(argCount, args, envCount, environ,
|
||||||
B_NORMAL_PRIORITY, B_WAIT_TILL_LOADED);
|
B_NORMAL_PRIORITY, B_WAIT_TILL_LOADED);
|
||||||
|
|
||||||
|
free(newArgs);
|
||||||
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -85,6 +118,13 @@ clear_caches(void *address, size_t length, uint32 flags)
|
|||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
__test_executable(const char *path, char *starter)
|
||||||
|
{
|
||||||
|
return sRuntimeLinker->test_executable(path, geteuid(), getegid(), starter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
__init_image(const struct uspace_program_args *args)
|
__init_image(const struct uspace_program_args *args)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
/*
|
/*
|
||||||
** Copyright 2004, Axel Dörfler, [email protected]. All rights reserved.
|
* Copyright 2004-2005, Axel Dörfler, [email protected]. All rights reserved.
|
||||||
** Distributed under the terms of the Haiku License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <syscalls.h>
|
#include <syscalls.h>
|
||||||
|
#include <libroot_private.h>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <alloca.h>
|
#include <alloca.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@@ -49,31 +51,64 @@ copy_arguments(va_list list, const char **args, const char *arg)
|
|||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
execve(const char *path, char * const argv[], char * const environment[])
|
execve(const char *path, char * const args[], char * const environment[])
|
||||||
{
|
{
|
||||||
int32 argc = 0, envCount = 0;
|
int32 argCount = 0, envCount = 0;
|
||||||
|
char starter[B_FILE_NAME_LENGTH];
|
||||||
|
char **newArgs = NULL;
|
||||||
|
|
||||||
// count argument/environment list entries here, we don't want
|
// count argument/environment list entries here, we don't want
|
||||||
// to do this in the kernel
|
// to do this in the kernel
|
||||||
while (argv[argc] != NULL)
|
while (args[argCount] != NULL)
|
||||||
argc++;
|
argCount++;
|
||||||
while (environment[envCount] != NULL)
|
while (environment[envCount] != NULL)
|
||||||
envCount++;
|
envCount++;
|
||||||
|
|
||||||
if (argc == 0) {
|
if (argCount == 0) {
|
||||||
// we need some more info on what to do...
|
// we need some more info on what to do...
|
||||||
errno = B_BAD_VALUE;
|
errno = B_BAD_VALUE;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test validity of executable + support for scripts
|
||||||
|
{
|
||||||
|
status_t status = __test_executable(args[0], starter);
|
||||||
|
if (status < B_OK) {
|
||||||
|
errno = status;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (starter[0]) {
|
||||||
|
int32 i;
|
||||||
|
|
||||||
|
// this is a shell script and requires special treatment
|
||||||
|
newArgs = malloc((argCount + 2) * sizeof(void *));
|
||||||
|
if (newArgs == NULL) {
|
||||||
|
errno = B_NO_MEMORY;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy args and have "starter" as new app
|
||||||
|
newArgs[0] = starter;
|
||||||
|
for (i = 0; i < argCount; i++)
|
||||||
|
newArgs[i + 1] = args[i];
|
||||||
|
newArgs[i + 1] = NULL;
|
||||||
|
|
||||||
|
path = starter;
|
||||||
|
args = newArgs;
|
||||||
|
argCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// "argv[0]" and "path" should be identical here, but they don't have
|
// "argv[0]" and "path" should be identical here, but they don't have
|
||||||
// to. Instead of worrying and needing to copy the array, we just
|
// to. Instead of worrying and needing to copy the array, we just
|
||||||
// don't care and pass everything to the kernel - it will have to
|
// don't care and pass everything to the kernel - it will have to
|
||||||
// do the right thing :)
|
// do the right thing :)
|
||||||
|
|
||||||
errno = _kern_exec(path, argc, argv, envCount, environment);
|
errno = _kern_exec(path, argCount, args, envCount, environment);
|
||||||
// if this call returns, something definitely went wrong
|
// if this call returns, something definitely went wrong
|
||||||
|
|
||||||
|
free(newArgs);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,268 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <syscalls.h>
|
#include <syscalls.h>
|
||||||
#include <user_runtime.h>
|
#include <user_runtime.h>
|
||||||
#include "rld_priv.h"
|
#include "rld_priv.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct uspace_program_args *gProgramArgs;
|
||||||
|
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
search_path_for_type(image_type type)
|
||||||
|
{
|
||||||
|
const char *path = NULL;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case B_APP_IMAGE:
|
||||||
|
path = getenv("PATH");
|
||||||
|
break;
|
||||||
|
case B_LIBRARY_IMAGE:
|
||||||
|
path = getenv("LIBRARY_PATH");
|
||||||
|
break;
|
||||||
|
case B_ADD_ON_IMAGE:
|
||||||
|
path = getenv("ADDON_PATH");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDo: for now, if the variable was not set, return default paths
|
||||||
|
if (path != NULL)
|
||||||
|
return path;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case B_APP_IMAGE:
|
||||||
|
return "/boot/home/config/bin:"
|
||||||
|
"/bin:"
|
||||||
|
"/boot/apps:"
|
||||||
|
"/boot/preferences:"
|
||||||
|
"/boot/beos/apps:"
|
||||||
|
"/boot/beos/preferences:"
|
||||||
|
"/boot/develop/tools/gnupro/bin";
|
||||||
|
|
||||||
|
case B_LIBRARY_IMAGE:
|
||||||
|
return "%A/lib:/boot/home/config/lib:/boot/beos/system/lib";
|
||||||
|
|
||||||
|
case B_ADD_ON_IMAGE:
|
||||||
|
return "%A/add-ons"
|
||||||
|
":/boot/home/config/add-ons"
|
||||||
|
":/boot/beos/system/add-ons";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
try_open_executable(const char *dir, int dirLen, const char *name, char *path,
|
||||||
|
int pathLen)
|
||||||
|
{
|
||||||
|
int nameLen = strlen(name);
|
||||||
|
|
||||||
|
// construct the path
|
||||||
|
if (dirLen > 0) {
|
||||||
|
char *buffer = path;
|
||||||
|
|
||||||
|
if (dirLen >= 2 && strncmp(dir, "%A", 2) == 0) {
|
||||||
|
// Replace %A with current app folder path (of course,
|
||||||
|
// this must be the first part of the path)
|
||||||
|
// ToDo: Maybe using first image info is better suited than
|
||||||
|
// gProgamArgs->program_path here?
|
||||||
|
char *lastSlash = strrchr(gProgramArgs->program_path, '/');
|
||||||
|
int bytesCopied;
|
||||||
|
|
||||||
|
// copy what's left (when the application name is removed)
|
||||||
|
if (lastSlash != NULL) {
|
||||||
|
strlcpy(buffer, gProgramArgs->program_path,
|
||||||
|
min(pathLen, lastSlash + 1 - gProgramArgs->program_path));
|
||||||
|
} else
|
||||||
|
strlcpy(buffer, ".", pathLen);
|
||||||
|
|
||||||
|
bytesCopied = strlen(buffer);
|
||||||
|
buffer += bytesCopied;
|
||||||
|
pathLen -= bytesCopied;
|
||||||
|
dir += 2;
|
||||||
|
dirLen -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirLen + 1 + nameLen >= pathLen)
|
||||||
|
return B_NAME_TOO_LONG;
|
||||||
|
|
||||||
|
memcpy(buffer, dir, dirLen);
|
||||||
|
buffer[dirLen] = '/';
|
||||||
|
strcpy(buffer + dirLen + 1, name);
|
||||||
|
} else {
|
||||||
|
if (nameLen >= pathLen)
|
||||||
|
return B_NAME_TOO_LONG;
|
||||||
|
|
||||||
|
strcpy(path + dirLen + 1, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TRACE(("rld.so: try_open_container(): %s\n", path));
|
||||||
|
|
||||||
|
return _kern_open(-1, path, O_RDONLY, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
search_executable_in_path_list(const char *name, const char *pathList,
|
||||||
|
int pathListLen, char *pathBuffer, int pathBufferLen)
|
||||||
|
{
|
||||||
|
const char *pathListEnd = pathList + pathListLen;
|
||||||
|
|
||||||
|
//TRACE(("rld.so: search_container_in_path_list() %s in %.*s\n", name,
|
||||||
|
// pathListLen, pathList));
|
||||||
|
|
||||||
|
while (pathListLen > 0) {
|
||||||
|
const char *pathEnd = pathList;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
// find the next ':' or run till the end of the string
|
||||||
|
while (pathEnd < pathListEnd && *pathEnd != ':')
|
||||||
|
pathEnd++;
|
||||||
|
|
||||||
|
fd = try_open_executable(pathList, pathEnd - pathList, name, pathBuffer,
|
||||||
|
pathBufferLen);
|
||||||
|
if (fd >= 0)
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
pathListLen = pathListEnd - pathEnd - 1;
|
||||||
|
pathList = pathEnd + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
open_executable(char *name, image_type type, const char *rpath)
|
||||||
|
{
|
||||||
|
const char *paths;
|
||||||
|
char buffer[PATH_MAX];
|
||||||
|
int fd = -1;
|
||||||
|
|
||||||
|
if (strchr(name, '/')) {
|
||||||
|
// the name already contains a path, we don't have to search for it
|
||||||
|
return _kern_open(-1, name, O_RDONLY, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// first try rpath (DT_RPATH)
|
||||||
|
if (rpath) {
|
||||||
|
// It consists of a colon-separated search path list. Optionally it
|
||||||
|
// follows a second search path list, separated from the first by a
|
||||||
|
// semicolon.
|
||||||
|
const char *semicolon = strchr(rpath, ';');
|
||||||
|
const char *firstList = (semicolon ? rpath : NULL);
|
||||||
|
const char *secondList = (semicolon ? semicolon + 1 : rpath);
|
||||||
|
// If there is no ';', we set only secondList to simplify things.
|
||||||
|
if (firstList) {
|
||||||
|
fd = search_executable_in_path_list(name, firstList,
|
||||||
|
semicolon - firstList, buffer, sizeof(buffer));
|
||||||
|
}
|
||||||
|
if (fd < 0) {
|
||||||
|
fd = search_executable_in_path_list(name, secondList,
|
||||||
|
strlen(secondList), buffer, sizeof(buffer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// let's evaluate the system path variables to find the container
|
||||||
|
if (fd < 0) {
|
||||||
|
paths = search_path_for_type(type);
|
||||||
|
if (paths) {
|
||||||
|
fd = search_executable_in_path_list(name, paths, strlen(paths),
|
||||||
|
buffer, sizeof(buffer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd >= 0) {
|
||||||
|
// we found it, copy path!
|
||||||
|
//TRACE(("rld.so: open_container(%s): found at %s\n", name, buffer));
|
||||||
|
strlcpy(name, buffer, PATH_MAX);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Tests if there is an executable file at the provided path. It will
|
||||||
|
* also test if the file has a valid ELF header or is a shell script.
|
||||||
|
* Even if the runtime loader does not need to be able to deal with
|
||||||
|
* both types, the caller will give scripts a proper treatment.
|
||||||
|
*/
|
||||||
|
|
||||||
|
status_t
|
||||||
|
test_executable(const char *name, uid_t user, gid_t group, char *starter)
|
||||||
|
{
|
||||||
|
char path[B_PATH_NAME_LENGTH];
|
||||||
|
char buffer[B_FILE_NAME_LENGTH];
|
||||||
|
// must be large enough to hold the ELF header
|
||||||
|
struct stat stat;
|
||||||
|
status_t status;
|
||||||
|
ssize_t length;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
strlcpy(path, name, sizeof(path));
|
||||||
|
|
||||||
|
fd = open_executable(path, B_APP_IMAGE, NULL);
|
||||||
|
if (fd < B_OK)
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
// see if it's executable at all
|
||||||
|
|
||||||
|
status = _kern_read_stat(fd, NULL, true, &stat, sizeof(struct stat));
|
||||||
|
if (status != B_OK)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
// shift mode bits, to check directly against accessMode
|
||||||
|
if (user == stat.st_uid)
|
||||||
|
stat.st_mode >>= 6;
|
||||||
|
else if (group == stat.st_gid)
|
||||||
|
stat.st_mode >>= 3;
|
||||||
|
|
||||||
|
if (~(stat.st_mode & S_IRWXO) & X_OK) {
|
||||||
|
status = B_NOT_ALLOWED;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read and verify the ELF header
|
||||||
|
|
||||||
|
length = _kern_read(fd, 0, buffer, sizeof(buffer));
|
||||||
|
if (length < 0) {
|
||||||
|
status = length;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = elf_verify_header(buffer, length);
|
||||||
|
if (status == B_NOT_AN_EXECUTABLE) {
|
||||||
|
// test for shell scripts
|
||||||
|
if (!strncmp(buffer, "#!", 2)) {
|
||||||
|
status = B_OK;
|
||||||
|
if (starter) {
|
||||||
|
char *end;
|
||||||
|
buffer[length - 1] = '\0';
|
||||||
|
|
||||||
|
if ((end = strchr(buffer, '\n')) != NULL)
|
||||||
|
end[0] = '\0';
|
||||||
|
strcpy(starter, buffer + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (status == B_OK && starter)
|
||||||
|
starter[0] = '\0';
|
||||||
|
|
||||||
|
out:
|
||||||
|
_kern_close(fd);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** This is the main entry point of the runtime loader as
|
/** This is the main entry point of the runtime loader as
|
||||||
* specified by its ld-script.
|
* specified by its ld-script.
|
||||||
@@ -20,8 +277,9 @@
|
|||||||
int
|
int
|
||||||
runtime_loader(void *_args)
|
runtime_loader(void *_args)
|
||||||
{
|
{
|
||||||
struct uspace_program_args *args = (struct uspace_program_args *)_args;
|
void *entry = NULL;
|
||||||
void *entry = 0;
|
|
||||||
|
gProgramArgs = (struct uspace_program_args *)_args;
|
||||||
|
|
||||||
#if DEBUG_RLD
|
#if DEBUG_RLD
|
||||||
close(0); open("/dev/console", 0); /* stdin */
|
close(0); open("/dev/console", 0); /* stdin */
|
||||||
@@ -30,14 +288,15 @@ runtime_loader(void *_args)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
rldheap_init();
|
rldheap_init();
|
||||||
rldexport_init(args);
|
rldexport_init();
|
||||||
rldelf_init(args);
|
rldelf_init();
|
||||||
|
|
||||||
load_program(args->program_path, &entry);
|
load_program(gProgramArgs->program_path, &entry);
|
||||||
|
|
||||||
if (entry == NULL)
|
if (entry == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
// call the program entry point (usually _start())
|
// call the program entry point (usually _start())
|
||||||
return ((int (*)(int, void *, void *, void *))entry)(args->argc, args->argv, args->envp, args);
|
return ((int (*)(int, void *, void *, const void *))entry)(gProgramArgs->argc,
|
||||||
|
gProgramArgs->argv, gProgramArgs->envp, gProgramArgs);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
|
|
||||||
int runtime_loader(void *arg);
|
int runtime_loader(void *arg);
|
||||||
|
int open_executable(char *name, image_type type, const char *rpath);
|
||||||
|
status_t test_executable(const char *path, uid_t user, gid_t group, char *starter);
|
||||||
|
|
||||||
status_t unload_program(image_id imageID);
|
status_t unload_program(image_id imageID);
|
||||||
image_id load_program(char const *path, void **entry);
|
image_id load_program(char const *path, void **entry);
|
||||||
@@ -23,12 +25,15 @@ status_t get_nth_symbol(image_id imageID, int32 num, char *nameBuffer, int32 *_n
|
|||||||
status_t get_symbol(image_id imageID, char const *symbolName, int32 symbolType,
|
status_t get_symbol(image_id imageID, char const *symbolName, int32 symbolType,
|
||||||
void **_location);
|
void **_location);
|
||||||
|
|
||||||
void rldelf_init(struct uspace_program_args const *uspa);
|
status_t elf_verify_header(void *header, int32 length);
|
||||||
void rldexport_init(struct uspace_program_args *uspa);
|
void rldelf_init(void);
|
||||||
|
void rldexport_init(void);
|
||||||
|
|
||||||
// RLD heap
|
// RLD heap
|
||||||
void rldheap_init(void);
|
void rldheap_init(void);
|
||||||
void *rldalloc(size_t);
|
void *rldalloc(size_t);
|
||||||
void rldfree(void *p);
|
void rldfree(void *p);
|
||||||
|
|
||||||
|
extern struct uspace_program_args *gProgramArgs;
|
||||||
|
|
||||||
#endif /* RUN_TIME_LINKER_H */
|
#endif /* RUN_TIME_LINKER_H */
|
||||||
|
|||||||
@@ -1,13 +1,37 @@
|
|||||||
/*
|
/*
|
||||||
** Copyright 2001, Travis Geiselbrecht. All rights reserved.
|
* Copyright 2005, Axel Dörfler, [email protected].
|
||||||
** Distributed under the terms of the NewOS License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*
|
||||||
|
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
|
||||||
|
* Distributed under the terms of the NewOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <syscalls.h>
|
#include <syscalls.h>
|
||||||
|
|
||||||
|
#include "rld_priv.h"
|
||||||
|
|
||||||
|
|
||||||
|
char *
|
||||||
|
getenv(const char *name)
|
||||||
|
{
|
||||||
|
// ToDo: this should use the real environ pointer once available!
|
||||||
|
// (or else, any updates to the search paths while the app is running are ignored)
|
||||||
|
char **environ = gProgramArgs->envp;
|
||||||
|
int32 length = strlen(name);
|
||||||
|
int32 i;
|
||||||
|
|
||||||
|
for (i = 0; environ[i] != NULL; i++) {
|
||||||
|
if (!strncmp(name, environ[i], length) && environ[i][length] == '=')
|
||||||
|
return environ[i] + length + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
printf(const char *fmt, ...)
|
printf(const char *fmt, ...)
|
||||||
|
|||||||
@@ -71,9 +71,7 @@ enum {
|
|||||||
#define APP_OR_LIBRARY_TYPE (IMAGE_TYPE_TO_MASK(B_APP_IMAGE) \
|
#define APP_OR_LIBRARY_TYPE (IMAGE_TYPE_TO_MASK(B_APP_IMAGE) \
|
||||||
| IMAGE_TYPE_TO_MASK(B_LIBRARY_IMAGE))
|
| IMAGE_TYPE_TO_MASK(B_LIBRARY_IMAGE))
|
||||||
|
|
||||||
|
typedef struct elf_region_t {
|
||||||
typedef
|
|
||||||
struct elf_region_t {
|
|
||||||
area_id id;
|
area_id id;
|
||||||
addr_t start;
|
addr_t start;
|
||||||
addr_t size;
|
addr_t size;
|
||||||
@@ -85,7 +83,6 @@ struct elf_region_t {
|
|||||||
uint32 flags;
|
uint32 flags;
|
||||||
} elf_region_t;
|
} elf_region_t;
|
||||||
|
|
||||||
|
|
||||||
typedef struct image_t {
|
typedef struct image_t {
|
||||||
// image identification
|
// image identification
|
||||||
char path[B_OS_NAME_LENGTH];
|
char path[B_OS_NAME_LENGTH];
|
||||||
@@ -122,13 +119,12 @@ typedef struct image_t {
|
|||||||
elf_region_t regions[1];
|
elf_region_t regions[1];
|
||||||
} image_t;
|
} image_t;
|
||||||
|
|
||||||
|
typedef struct image_queue_t {
|
||||||
typedef
|
|
||||||
struct image_queue_t {
|
|
||||||
image_t *head;
|
image_t *head;
|
||||||
image_t *tail;
|
image_t *tail;
|
||||||
} image_queue_t;
|
} image_queue_t;
|
||||||
|
|
||||||
|
typedef void (libinit_f)(unsigned, struct uspace_program_args const *);
|
||||||
|
|
||||||
static image_queue_t gLoadedImages = {0, 0};
|
static image_queue_t gLoadedImages = {0, 0};
|
||||||
static image_queue_t gLoadingImages = {0, 0};
|
static image_queue_t gLoadingImages = {0, 0};
|
||||||
@@ -140,8 +136,6 @@ static sem_id rld_sem;
|
|||||||
static thread_id rld_sem_owner;
|
static thread_id rld_sem_owner;
|
||||||
static int32 rld_sem_count;
|
static int32 rld_sem_count;
|
||||||
|
|
||||||
static struct uspace_program_args const *gProgramArgs;
|
|
||||||
|
|
||||||
|
|
||||||
#define STRING(image, offset) ((char *)(&(image)->strtab[(offset)]))
|
#define STRING(image, offset) ((char *)(&(image)->strtab[(offset)]))
|
||||||
#define SYMNAME(image, sym) STRING(image, (sym)->st_name)
|
#define SYMNAME(image, sym) STRING(image, (sym)->st_name)
|
||||||
@@ -205,24 +199,6 @@ rld_lock()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
char *
|
|
||||||
getenv(const char *name)
|
|
||||||
{
|
|
||||||
// ToDo: this should use the real environ pointer once available!
|
|
||||||
// (or else, any updates to the search paths while the app is running are ignored)
|
|
||||||
char **environ = gProgramArgs->envp;
|
|
||||||
int32 length = strlen(name);
|
|
||||||
int32 i;
|
|
||||||
|
|
||||||
for (i = 0; environ[i] != NULL; i++) {
|
|
||||||
if (!strncmp(name, environ[i], length) && environ[i][length] == '=')
|
|
||||||
return environ[i] + length + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
enqueue_image(image_queue_t *queue, image_t *image)
|
enqueue_image(image_queue_t *queue, image_t *image)
|
||||||
{
|
{
|
||||||
@@ -928,194 +904,6 @@ relocate_image(image_t *image)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static const char *
|
|
||||||
search_path_for_type(image_type type)
|
|
||||||
{
|
|
||||||
const char *path = NULL;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case B_APP_IMAGE:
|
|
||||||
path = getenv("PATH");
|
|
||||||
break;
|
|
||||||
case B_LIBRARY_IMAGE:
|
|
||||||
path = getenv("LIBRARY_PATH");
|
|
||||||
break;
|
|
||||||
case B_ADD_ON_IMAGE:
|
|
||||||
path = getenv("ADDON_PATH");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return NULL;
|
|
||||||
#if 0
|
|
||||||
case B_APP_IMAGE:
|
|
||||||
return getenv("PATH");
|
|
||||||
case B_LIBRARY_IMAGE:
|
|
||||||
return getenv("LIBRARY_PATH");
|
|
||||||
case B_ADD_ON_IMAGE:
|
|
||||||
return getenv("ADDON_PATH");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToDo: for now, if the variable was not set, return default paths
|
|
||||||
if (path != NULL)
|
|
||||||
return path;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case B_APP_IMAGE:
|
|
||||||
return "/boot/home/config/bin:"
|
|
||||||
"/bin:"
|
|
||||||
"/boot/apps:"
|
|
||||||
"/boot/preferences:"
|
|
||||||
"/boot/beos/apps:"
|
|
||||||
"/boot/beos/preferences:"
|
|
||||||
"/boot/develop/tools/gnupro/bin";
|
|
||||||
|
|
||||||
case B_LIBRARY_IMAGE:
|
|
||||||
return "%A/lib:/boot/home/config/lib:/boot/beos/system/lib";
|
|
||||||
|
|
||||||
case B_ADD_ON_IMAGE:
|
|
||||||
return "%A/add-ons"
|
|
||||||
":/boot/home/config/add-ons"
|
|
||||||
":/boot/beos/system/add-ons";
|
|
||||||
|
|
||||||
default:
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
try_open_container(const char *dir, int dirLen, const char *name, char *path,
|
|
||||||
int pathLen)
|
|
||||||
{
|
|
||||||
int nameLen = strlen(name);
|
|
||||||
|
|
||||||
// construct the path
|
|
||||||
if (dirLen > 0) {
|
|
||||||
char *buffer = path;
|
|
||||||
|
|
||||||
if (dirLen >= 2 && strncmp(dir, "%A", 2) == 0) {
|
|
||||||
// Replace %A with current app folder path (of course,
|
|
||||||
// this must be the first part of the path)
|
|
||||||
// ToDo: Maybe using first image info is better suited than
|
|
||||||
// gProgamArgs->program_path here?
|
|
||||||
char *lastSlash = strrchr(gProgramArgs->program_path, '/');
|
|
||||||
int bytesCopied;
|
|
||||||
|
|
||||||
// copy what's left (when the application name is removed)
|
|
||||||
if (lastSlash != NULL) {
|
|
||||||
strlcpy(buffer, gProgramArgs->program_path,
|
|
||||||
min(pathLen, lastSlash + 1 - gProgramArgs->program_path));
|
|
||||||
} else
|
|
||||||
strlcpy(buffer, ".", pathLen);
|
|
||||||
|
|
||||||
bytesCopied = strlen(buffer);
|
|
||||||
buffer += bytesCopied;
|
|
||||||
pathLen -= bytesCopied;
|
|
||||||
dir += 2;
|
|
||||||
dirLen -= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dirLen + 1 + nameLen >= pathLen)
|
|
||||||
return B_NAME_TOO_LONG;
|
|
||||||
|
|
||||||
memcpy(buffer, dir, dirLen);
|
|
||||||
buffer[dirLen] = '/';
|
|
||||||
strcpy(buffer + dirLen + 1, name);
|
|
||||||
} else {
|
|
||||||
if (nameLen >= pathLen)
|
|
||||||
return B_NAME_TOO_LONG;
|
|
||||||
|
|
||||||
strcpy(path + dirLen + 1, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
TRACE(("rld.so: try_open_container(): %s\n", path));
|
|
||||||
|
|
||||||
return _kern_open(-1, path, O_RDONLY, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
search_container_in_path_list(const char *name, const char *pathList,
|
|
||||||
int pathListLen, char *pathBuffer, int pathBufferLen)
|
|
||||||
{
|
|
||||||
const char *pathListEnd = pathList + pathListLen;
|
|
||||||
|
|
||||||
TRACE(("rld.so: search_container_in_path_list() %s in %.*s\n", name,
|
|
||||||
pathListLen, pathList));
|
|
||||||
|
|
||||||
while (pathListLen > 0) {
|
|
||||||
const char *pathEnd = pathList;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
// find the next ':' or run till the end of the string
|
|
||||||
while (pathEnd < pathListEnd && *pathEnd != ':')
|
|
||||||
pathEnd++;
|
|
||||||
|
|
||||||
fd = try_open_container(pathList, pathEnd - pathList, name, pathBuffer,
|
|
||||||
pathBufferLen);
|
|
||||||
if (fd >= 0)
|
|
||||||
return fd;
|
|
||||||
|
|
||||||
pathListLen = pathListEnd - pathEnd - 1;
|
|
||||||
pathList = pathEnd + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return B_ENTRY_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
open_container(char *name, image_type type, const char *rpath)
|
|
||||||
{
|
|
||||||
const char *paths;
|
|
||||||
char buffer[PATH_MAX];
|
|
||||||
int fd = -1;
|
|
||||||
|
|
||||||
if (strchr(name, '/')) {
|
|
||||||
// the name already contains a path, we don't have to search for it
|
|
||||||
return _kern_open(-1, name, O_RDONLY, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// first try rpath (DT_RPATH)
|
|
||||||
if (rpath) {
|
|
||||||
// It consists of a colon-separated search path list. Optionally it
|
|
||||||
// follows a second search path list, separated from the first by a
|
|
||||||
// semicolon.
|
|
||||||
const char *semicolon = strchr(rpath, ';');
|
|
||||||
const char *firstList = (semicolon ? rpath : NULL);
|
|
||||||
const char *secondList = (semicolon ? semicolon + 1 : rpath);
|
|
||||||
// If there is no ';', we set only secondList to simplify things.
|
|
||||||
if (firstList) {
|
|
||||||
fd = search_container_in_path_list(name, firstList,
|
|
||||||
semicolon - firstList, buffer, sizeof(buffer));
|
|
||||||
}
|
|
||||||
if (fd < 0) {
|
|
||||||
fd = search_container_in_path_list(name, secondList,
|
|
||||||
strlen(secondList), buffer, sizeof(buffer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// let's evaluate the system path variables to find the container
|
|
||||||
if (fd < 0) {
|
|
||||||
paths = search_path_for_type(type);
|
|
||||||
if (paths) {
|
|
||||||
fd = search_container_in_path_list(name, paths, strlen(paths),
|
|
||||||
buffer, sizeof(buffer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fd >= 0) {
|
|
||||||
// we found it, copy path!
|
|
||||||
TRACE(("rld.so: open_container(%s): found at %s\n", name, buffer));
|
|
||||||
strlcpy(name, buffer, PATH_MAX);
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
return B_ENTRY_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static image_t *
|
static image_t *
|
||||||
load_container(char const *name, image_type type, const char *rpath)
|
load_container(char const *name, image_type type, const char *rpath)
|
||||||
{
|
{
|
||||||
@@ -1146,7 +934,7 @@ load_container(char const *name, image_type type, const char *rpath)
|
|||||||
strlcpy(path, name, sizeof(path));
|
strlcpy(path, name, sizeof(path));
|
||||||
|
|
||||||
// Try to load explicit image path first
|
// Try to load explicit image path first
|
||||||
fd = open_container(path, type, rpath);
|
fd = open_executable(path, type, rpath);
|
||||||
FATAL((fd < 0), fd, "cannot open file %s\n", path);
|
FATAL((fd < 0), fd, "cannot open file %s\n", path);
|
||||||
|
|
||||||
len = _kern_read(fd, 0, &eheader, sizeof(eheader));
|
len = _kern_read(fd, 0, &eheader, sizeof(eheader));
|
||||||
@@ -1546,15 +1334,24 @@ get_symbol(image_id imageID, char const *symbolName, int32 symbolType, void **_l
|
|||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
/*
|
|
||||||
* init routine, just get hold of the user-space program args
|
/** Read and verify the ELF header */
|
||||||
*/
|
|
||||||
|
status_t
|
||||||
|
elf_verify_header(void *header, int32 length)
|
||||||
|
{
|
||||||
|
int32 programSize, sectionSize;
|
||||||
|
|
||||||
|
if (length < (int32)sizeof(struct Elf32_Ehdr))
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
return parse_elf_header((struct Elf32_Ehdr *)header, &programSize, §ionSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
rldelf_init(struct uspace_program_args const *_args)
|
rldelf_init(void)
|
||||||
{
|
{
|
||||||
gProgramArgs = _args;
|
|
||||||
|
|
||||||
rld_sem = create_sem(1, "rld_lock");
|
rld_sem = create_sem(1, "rld_lock");
|
||||||
rld_sem_owner = -1;
|
rld_sem_owner = -1;
|
||||||
rld_sem_count = 0;
|
rld_sem_count = 0;
|
||||||
|
|||||||
@@ -42,16 +42,24 @@ export_get_nth_image_symbol(image_id id, int32 num, char *nameBuffer, int32 *_na
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
export_test_executable(const char *path, uid_t user, gid_t group, char *starter)
|
||||||
|
{
|
||||||
|
return test_executable(path, user, group, starter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
rldexport_init(struct uspace_program_args *args)
|
rldexport_init(void)
|
||||||
{
|
{
|
||||||
static struct rld_export exports = {
|
static struct rld_export exports = {
|
||||||
// dynamic loading support API
|
// dynamic loading support API
|
||||||
export_load_add_on,
|
export_load_add_on,
|
||||||
export_unload_add_on,
|
export_unload_add_on,
|
||||||
export_get_image_symbol,
|
export_get_image_symbol,
|
||||||
export_get_nth_image_symbol
|
export_get_nth_image_symbol,
|
||||||
|
export_test_executable
|
||||||
};
|
};
|
||||||
|
|
||||||
args->rld_export = &exports;
|
gProgramArgs->rld_export = &exports;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user