root/src/system/runtime_loader/elf.cpp
/*
 * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Copyright 2003-2011, Axel Dörfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 *
 * Copyright 2002, Manuel J. Petit. All rights reserved.
 * Copyright 2001, Travis Geiselbrecht. All rights reserved.
 * Distributed under the terms of the NewOS License.
 */

#include "runtime_loader_private.h"

#include <ctype.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <OS.h>

#include <commpage_defs.h>
#include <syscalls.h>
#include <util/kernel_cpp.h>

#include <locks.h>

#include "add_ons.h"
#include "commpage.h"
#include "elf_load_image.h"
#include "elf_symbol_lookup.h"
#include "elf_tls.h"
#include "elf_versioning.h"
#include "errors.h"
#include "images.h"


// TODO: implement better locking strategy
// TODO: implement lazy binding

// a handle returned by load_library() (dlopen())
#define RLD_GLOBAL_SCOPE        ((void*)-2l)

static const char* const kLockName = "runtime loader";


typedef void (*init_term_function)(image_id);
typedef void (*initfini_array_function)();

bool gProgramLoaded = false;
image_t* gProgramImage;

static image_t** sPreloadedAddons = NULL;
static uint32 sPreloadedAddonCount = 0;

static recursive_lock sLock = RECURSIVE_LOCK_INITIALIZER(kLockName);


static const char *
find_dt_string(image_t *image, int32 d_tag)
{
        int i;
        elf_dyn *d = (elf_dyn *)image->dynamic_ptr;

        for (i = 0; d[i].d_tag != DT_NULL; i++) {
                if (d[i].d_tag == d_tag)
                        return STRING(image, d[i].d_un.d_val);
        }

        return NULL;
}


static const char *
find_dt_rpath(image_t *image)
{
        return find_dt_string(image, DT_RPATH);
}


static const char *
find_dt_runpath(image_t *image)
{
        return find_dt_string(image, DT_RUNPATH);
}


image_id
preload_image(char const* path, image_t **image)
{
        if (path == NULL)
                return B_BAD_VALUE;

        KTRACE("rld: preload_image(\"%s\")", path);

        status_t status = load_image(path, B_LIBRARY_IMAGE, NULL, NULL, NULL, image);
        if (status < B_OK) {
                KTRACE("rld: preload_image(\"%s\") failed to load container: %s", path,
                        strerror(status));
                return status;
        }

        if ((*image)->find_undefined_symbol == NULL)
                (*image)->find_undefined_symbol = find_undefined_symbol_global;

        KTRACE("rld: preload_image(\"%s\") done: id: %" B_PRId32, path, (*image)->id);

        return (*image)->id;
}


static void
preload_images(image_t **image, int32 *_count = NULL)
{
        const char* imagePaths = getenv("LD_PRELOAD");
        if (imagePaths == NULL) {
                if (_count != NULL)
                        *_count = 0;
                return;
        }

        int32 count = 0;

        while (*imagePaths != '\0') {
                // find begin of image path
                while (*imagePaths != '\0' && isspace(*imagePaths))
                        imagePaths++;

                if (*imagePaths == '\0')
                        break;

                // find end of image path
                const char* imagePath = imagePaths;
                while (*imagePaths != '\0' && !isspace(*imagePaths))
                        imagePaths++;

                // extract the path
                char path[B_PATH_NAME_LENGTH];
                size_t pathLen = imagePaths - imagePath;
                if (pathLen > sizeof(path) - 1)
                        continue;

                if (image == NULL) {
                        count++;
                        continue;
                }
                memcpy(path, imagePath, pathLen);
                path[pathLen] = '\0';

                // load the image
                preload_image(path, &image[count++]);
        }

        KTRACE("rld: preload_images count: %d", count);

        if (_count != NULL)
                *_count = count;
}


static status_t
load_immediate_dependencies(image_t *image, bool preload)
{
        elf_dyn *d = (elf_dyn *)image->dynamic_ptr;
        bool reportErrors = report_errors();
        status_t status = B_OK;
        uint32 i, j;
        const char *rpath = NULL, *runpath;
        if (!d || (image->flags & RFLAG_DEPENDENCIES_LOADED))
                return B_OK;

        image->flags |= RFLAG_DEPENDENCIES_LOADED;

        int32 preloadedCount = 0;
        if (preload) {
                preload_images(NULL, &preloadedCount);
                image->num_needed += preloadedCount;
        }
        if (image->num_needed == 0)
                return B_OK;

        KTRACE("rld: load_dependencies(\"%s\", id: %" B_PRId32 ")", image->name,
                image->id);

        image->needed = (image_t**)malloc(image->num_needed * sizeof(image_t *));
        if (image->needed == NULL) {
                FATAL("%s: Failed to allocate needed struct\n", image->path);
                KTRACE("rld: load_dependencies(\"%s\", id: %" B_PRId32
                        ") failed: no memory", image->name, image->id);
                return B_NO_MEMORY;
        }

        memset(image->needed, 0, image->num_needed * sizeof(image_t *));
        if (preload)
                preload_images(image->needed);
        runpath = find_dt_runpath(image);
        if (runpath == NULL)
                rpath = find_dt_rpath(image);

        for (i = 0, j = preloadedCount; d[i].d_tag != DT_NULL; i++) {
                switch (d[i].d_tag) {
                        case DT_NEEDED:
                        {
                                int32 neededOffset = d[i].d_un.d_val;
                                const char *name = STRING(image, neededOffset);

                                status_t loadStatus = load_image(name, B_LIBRARY_IMAGE,
                                        rpath, runpath, image->path, &image->needed[j]);
                                if (loadStatus < B_OK) {
                                        status = loadStatus;
                                        // correct error code in case the file could not been found
                                        if (status == B_ENTRY_NOT_FOUND) {
                                                status = B_MISSING_LIBRARY;

                                                if (reportErrors)
                                                        gErrorMessage.AddString("missing library", name);
                                        }

                                        // Collect all missing libraries in case we report back
                                        if (!reportErrors) {
                                                KTRACE("rld: load_dependencies(\"%s\", id: %" B_PRId32
                                                        ") failed: %s", image->name, image->id,
                                                        strerror(status));
                                                return status;
                                        }
                                }

                                j += 1;
                                break;
                        }

                        default:
                                // ignore any other tag
                                continue;
                }
        }

        if (status < B_OK) {
                KTRACE("rld: load_dependencies(\"%s\", id: %" B_PRId32 ") "
                        "failed: %s", image->name, image->id,
                        strerror(status));
                return status;
        }

        if (j != image->num_needed) {
                FATAL("Internal error at load_dependencies()");
                KTRACE("rld: load_dependencies(\"%s\", id: %" B_PRId32 ") "
                        "failed: internal error", image->name, image->id);
                return B_ERROR;
        }

        KTRACE("rld: load_dependencies(\"%s\", id: %" B_PRId32 ") done",
                image->name, image->id);

        return B_OK;
}


static status_t
load_dependencies(image_t* image, bool preload = false)
{
        // load dependencies (breadth-first)
        for (image_t* otherImage = image; otherImage != NULL;
                        otherImage = otherImage->next) {
                status_t status = load_immediate_dependencies(otherImage, preload);
                if (status != B_OK)
                        return status;
                preload = false;
        }

        // Check the needed versions for the given image and all newly loaded
        // dependencies.
        for (image_t* otherImage = image; otherImage != NULL;
                        otherImage = otherImage->next) {
                status_t status = check_needed_image_versions(otherImage);
                if (status != B_OK)
                        return status;
        }

        return B_OK;
}


static status_t
relocate_image(image_t *rootImage, image_t *image)
{
        SymbolLookupCache cache(image);

        status_t status = arch_relocate_image(rootImage, image, &cache);
        if (status < B_OK) {
                FATAL("%s: Troubles relocating: %s\n", image->path, strerror(status));
                return status;
        }

        _kern_image_relocated(image->id);
        image_event(image, IMAGE_EVENT_RELOCATED);
        return B_OK;
}


static status_t
relocate_dependencies(image_t *image)
{
        // get the images that still have to be relocated
        image_t **list;
        ssize_t count = get_sorted_image_list(image, &list, RFLAG_RELOCATED);
        if (count < B_OK)
                return count;

        // relocate
        for (ssize_t i = 0; i < count; i++) {
                status_t status = relocate_image(image, list[i]);
                if (status < B_OK) {
                        free(list);
                        return status;
                }
        }

        free(list);
        return B_OK;
}


static void
init_dependencies(image_t *image, bool initHead)
{
        image_t **initList = NULL;
        ssize_t count, i;

        if (initHead && image->preinit_array) {
                uint count_preinit = image->preinit_array_len / sizeof(addr_t);
                for (uint j = 0; j < count_preinit; j++)
                        ((initfini_array_function)image->preinit_array[j])();
        }

        count = get_sorted_image_list(image, &initList, RFLAG_INITIALIZED);
        if (count <= 0) {
                free(initList);
                return;
        }

        if (!initHead) {
                // this removes the "calling" image
                image->flags &= ~RFLAG_INITIALIZED;
                initList[--count] = NULL;
        }

        TRACE(("%ld: init dependencies\n", find_thread(NULL)));
        for (i = 0; i < count; i++) {
                image = initList[i];

                TRACE(("%ld:  init: %s\n", find_thread(NULL), image->name));

                init_term_function before;
                if (find_symbol(image,
                                SymbolLookupInfo(B_INIT_BEFORE_FUNCTION_NAME, B_SYMBOL_TYPE_TEXT),
                                (void**)&before) == B_OK) {
                        before(image->id);
                }

                if (image->init_routine != 0)
                        ((init_term_function)image->init_routine)(image->id);

                if (image->init_array) {
                        uint count_init = image->init_array_len / sizeof(addr_t);
                        for (uint j = 0; j < count_init; j++)
                                ((initfini_array_function)image->init_array[j])();
                }

                init_term_function after;
                if (find_symbol(image,
                                SymbolLookupInfo(B_INIT_AFTER_FUNCTION_NAME, B_SYMBOL_TYPE_TEXT),
                                (void**)&after) == B_OK) {
                        after(image->id);
                }

                image_event(image, IMAGE_EVENT_INITIALIZED);
        }
        TRACE(("%ld: init done.\n", find_thread(NULL)));

        free(initList);
}


static void
call_term_functions(image_t* image)
{
        init_term_function before;
        if (find_symbol(image,
                        SymbolLookupInfo(B_TERM_BEFORE_FUNCTION_NAME, B_SYMBOL_TYPE_TEXT),
                        (void**)&before) == B_OK) {
                before(image->id);
        }

        if (image->term_array) {
                uint count_term = image->term_array_len / sizeof(addr_t);
                for (uint i = count_term; i-- > 0;)
                        ((initfini_array_function)image->term_array[i])();
        }

        if (image->term_routine)
                ((init_term_function)image->term_routine)(image->id);

        init_term_function after;
        if (find_symbol(image,
                        SymbolLookupInfo(B_TERM_AFTER_FUNCTION_NAME, B_SYMBOL_TYPE_TEXT),
                        (void**)&after) == B_OK) {
                after(image->id);
        }
}


static void
inject_runtime_loader_api(image_t* rootImage)
{
        // We patch any exported __gRuntimeLoader symbols to point to our private
        // API.
        image_t* image;
        void* _export;
        if (find_symbol_breadth_first(rootImage,
                        SymbolLookupInfo("__gRuntimeLoader", B_SYMBOL_TYPE_DATA), &image,
                        &_export) == B_OK) {
                *(void**)_export = &gRuntimeLoader;
        }
}


static status_t
add_preloaded_addon(image_t* image)
{
        // We realloc() everytime -- not particularly efficient, but good enough for
        // small number of preloaded addons.
        image_t** newArray = (image_t**)realloc(sPreloadedAddons,
                sizeof(image_t*) * (sPreloadedAddonCount + 1));
        if (newArray == NULL)
                return B_NO_MEMORY;

        sPreloadedAddons = newArray;
        newArray[sPreloadedAddonCount++] = image;

        return B_OK;
}


image_id
preload_addon(char const* path)
{
        if (path == NULL)
                return B_BAD_VALUE;

        KTRACE("rld: preload_addon(\"%s\")", path);

        image_t *image = NULL;
        status_t status = load_image(path, B_LIBRARY_IMAGE, NULL, NULL, NULL, &image);
        if (status < B_OK) {
                KTRACE("rld: preload_addon(\"%s\") failed to load container: %s", path,
                        strerror(status));
                return status;
        }

        if (image->find_undefined_symbol == NULL)
                image->find_undefined_symbol = find_undefined_symbol_global;

        status = load_dependencies(image);
        if (status < B_OK)
                goto err;

        set_image_flags_recursively(image, RTLD_GLOBAL);

        status = relocate_dependencies(image);
        if (status < B_OK)
                goto err;

        status = add_preloaded_addon(image);
        if (status < B_OK)
                goto err;

        inject_runtime_loader_api(image);

        remap_images();
        init_dependencies(image, true);

        // if the image contains an add-on, register it
        runtime_loader_add_on* addOnStruct;
        if (find_symbol(image,
                        SymbolLookupInfo("__gRuntimeLoaderAddOn", B_SYMBOL_TYPE_DATA),
                        (void**)&addOnStruct) == B_OK) {
                add_add_on(image, addOnStruct);
        }

        KTRACE("rld: preload_addon(\"%s\") done: id: %" B_PRId32, path, image->id);

        return image->id;

err:
        KTRACE("rld: preload_addon(\"%s\") failed: %s", path, strerror(status));

        dequeue_loaded_image(image);
        delete_image(image);
        return status;
}


static void
preload_addons()
{
        const char* imagePaths = getenv("LD_PRELOAD_ADDONS");
        if (imagePaths == NULL)
                return;

        while (*imagePaths != '\0') {
                // find begin of image path
                while (*imagePaths != '\0' && isspace(*imagePaths))
                        imagePaths++;

                if (*imagePaths == '\0')
                        break;

                // find end of image path
                const char* imagePath = imagePaths;
                while (*imagePaths != '\0' && !isspace(*imagePaths))
                        imagePaths++;

                // extract the path
                char path[B_PATH_NAME_LENGTH];
                size_t pathLen = imagePaths - imagePath;
                if (pathLen > sizeof(path) - 1)
                        continue;
                memcpy(path, imagePath, pathLen);
                path[pathLen] = '\0';

                // load the image
                preload_addon(path);
        }
}


//      #pragma mark - libroot.so exported functions


image_id
load_program(char const *path, void **_entry)
{
        status_t status;
        image_t *image;

        KTRACE("rld: load_program(\"%s\")", path);

        RecursiveLocker _(sLock);
                // for now, just do stupid simple global locking

        preload_addons();

        TRACE(("rld: load %s\n", path));

        status = load_image(path, B_APP_IMAGE, NULL, NULL, NULL, &gProgramImage);
        if (status < B_OK)
                goto err;

        if (gProgramImage->find_undefined_symbol == NULL)
                gProgramImage->find_undefined_symbol = find_undefined_symbol_global;

        status = load_dependencies(gProgramImage, true);
        if (status < B_OK)
                goto err;

        // Set RTLD_GLOBAL on all libraries including the program.
        // This results in the desired symbol resolution for dlopen()ed libraries.
        set_image_flags_recursively(gProgramImage, RTLD_GLOBAL);

        status = relocate_dependencies(gProgramImage);
        if (status < B_OK)
                goto err;

        inject_runtime_loader_api(gProgramImage);

        remap_images();
        init_dependencies(gProgramImage, true);

        // Since the images are initialized now, we no longer should use our
        // getenv(), but use the one from libroot.so
        find_symbol_breadth_first(gProgramImage,
                SymbolLookupInfo("getenv", B_SYMBOL_TYPE_TEXT), &image,
                (void**)&gGetEnv);

        if (gProgramImage->entry_point == 0) {
                status = B_NOT_AN_EXECUTABLE;
                goto err;
        }

        *_entry = (void *)(gProgramImage->entry_point);

        gProgramLoaded = true;

        KTRACE("rld: load_program(\"%s\") done: entry: %p, id: %" B_PRId32 , path,
                *_entry, gProgramImage->id);

        return gProgramImage->id;

err:
        KTRACE("rld: load_program(\"%s\") failed: %s", path, strerror(status));

        delete_image(gProgramImage);

        if (report_errors()) {
                // send error message
                gErrorMessage.AddInt32("error", status);
                gErrorMessage.SetDeliveryInfo(gProgramArgs->error_token,
                        -1, 0, find_thread(NULL));

                _kern_write_port_etc(gProgramArgs->error_port, 'KMSG',
                        gErrorMessage.Buffer(), gErrorMessage.ContentSize(), 0, 0);
        }
        _kern_loading_app_failed(status);

        return status;
}


image_id
load_library(char const *path, uint32 flags, bool addOn, void* caller,
        void** _handle)
{
        image_t *image = NULL;
        image_type type = (addOn ? B_ADD_ON_IMAGE : B_LIBRARY_IMAGE);
        status_t status;
        const char* rpath = NULL, *runpath = NULL;
        const char* requestingObjectPath = NULL;

        if (path == NULL && addOn)
                return B_BAD_VALUE;

        KTRACE("rld: load_library(\"%s\", %#" B_PRIx32 ", %d)", path, flags, addOn);

        RecursiveLocker _(sLock);
                // for now, just do stupid simple global locking

        // have we already loaded this library?
        // Checking it at this stage saves loading its dependencies again
        if (!addOn) {
                // a NULL path is fine -- it means the global scope shall be opened
                if (path == NULL) {
                        *_handle = RLD_GLOBAL_SCOPE;
                        return 0;
                }

                image = find_loaded_image_by_name(path, APP_OR_LIBRARY_TYPE);
                if (image != NULL && (flags & RTLD_GLOBAL) != 0)
                        set_image_flags_recursively(image, RTLD_GLOBAL);

                if (image) {
                        atomic_add(&image->ref_count, 1);
                        KTRACE("rld: load_library(\"%s\"): already loaded: %" B_PRId32,
                                path, image->id);
                        *_handle = image;
                        return image->id;
                } else if ((flags & RTLD_NOLOAD) != 0) {
                        return B_NAME_NOT_FOUND;
                }

                // First of all, find the caller image.
                image_t* callerImage = find_loaded_image_by_address((addr_t)caller);
                if (callerImage != NULL) {
                        runpath = find_dt_runpath(callerImage);
                        if (runpath == NULL)
                                rpath = find_dt_rpath(callerImage);
                        requestingObjectPath = callerImage->path;
                }
        }

        status = load_image(path, type, rpath, runpath, requestingObjectPath, &image);
        if (status < B_OK) {
                KTRACE("rld: load_library(\"%s\") failed to load container: %s", path,
                        strerror(status));
                return status;
        }

        if (image->find_undefined_symbol == NULL) {
                if (addOn)
                        image->find_undefined_symbol = find_undefined_symbol_add_on;
                else if (flags & RTLD_GROUP)
                        image->find_undefined_symbol = find_undefined_symbol_dependencies_only;
                else
                        image->find_undefined_symbol = find_undefined_symbol_global;
        }

        status = load_dependencies(image);
        if (status < B_OK)
                goto err;

        // If specified, set the RTLD_GLOBAL flag recursively on this image and all
        // dependencies. If not specified, we temporarily set
        // RFLAG_USE_FOR_RESOLVING so that the dependencies will correctly be used
        // for undefined symbol resolution.
        if ((flags & RTLD_GLOBAL) != 0)
                set_image_flags_recursively(image, RTLD_GLOBAL);
        else
                set_image_flags_recursively(image, RFLAG_USE_FOR_RESOLVING);

        status = relocate_dependencies(image);
        if (status < B_OK)
                goto err;

        if ((flags & RTLD_GLOBAL) == 0)
                clear_image_flags_recursively(image, RFLAG_USE_FOR_RESOLVING);

        remap_images();
        init_dependencies(image, true);

        KTRACE("rld: load_library(\"%s\") done: id: %" B_PRId32, path, image->id);

        *_handle = image;
        return image->id;

err:
        KTRACE("rld: load_library(\"%s\") failed: %s", path, strerror(status));

        unload_library(image, -1, addOn);
        return status;
}


status_t
unload_library(void* handle, image_id imageID, bool addOn)
{
        image_t *image;
        image_type type = addOn ? B_ADD_ON_IMAGE : B_LIBRARY_IMAGE;

        if (handle == NULL && imageID < 0)
                return B_BAD_IMAGE_ID;

        if (handle == RLD_GLOBAL_SCOPE)
                return B_OK;

        RecursiveLocker _(sLock);
                // for now, just do stupid simple global locking

        if (gInvalidImageIDs) {
                // After fork, we lazily rebuild the image IDs of all loaded images
                update_image_ids();
        }

        // we only check images that have been already initialized

        if (handle != NULL) {
                image = (image_t*)handle;
                put_image(image);
        } else {
                image = find_loaded_image_by_id(imageID, true);
                if (image == NULL)
                        return B_BAD_IMAGE_ID;

                // unload image
                if (type != image->type)
                        return B_BAD_VALUE;
                put_image(image);
        }

        while ((image = get_disposable_images().head) != NULL) {
                dequeue_disposable_image(image);

                if ((image->flags & RFLAG_INITIALIZED) != 0) {
                        // Call the exit hooks that live in this image.
                        // Note: With the Itanium ABI this shouldn't really be done this
                        // way anymore, since global destructors are registered via
                        // __cxa_atexit() (the ones that are registered dynamically) and the
                        // termination routine should call __cxa_finalize() for the image.
                        // The reason why we still do it is that hooks registered with
                        // atexit() aren't associated with the image. We could find out
                        // there which image the hooks lives in and register it
                        // respectively, but since that would be done always, that's
                        // probably more expensive than calling
                        // call_atexit_hooks_for_range() only here, which happens only when
                        // libraries are unloaded dynamically.
                        if (gRuntimeLoader.call_atexit_hooks_for_range != NULL) {
                                for (uint32 i = 0; i < image->num_regions; i++) {
                                        gRuntimeLoader.call_atexit_hooks_for_range(
                                                image->regions[i].vmstart, image->regions[i].vmsize);
                                }
                        }

                        image_event(image, IMAGE_EVENT_UNINITIALIZING);

                        call_term_functions(image);
                }

                TLSBlockTemplates::Get().Unregister(image->dso_tls_id);

                unmap_image(image);

                image_event(image, IMAGE_EVENT_UNLOADING);

                delete_image(image);
        }

        return B_OK;
}


status_t
get_nth_symbol(image_id imageID, int32 num, char *nameBuffer,
        int32 *_nameLength, int32 *_type, void **_location)
{
        int32 count = 0, j;
        uint32 i;
        image_t *image;

        RecursiveLocker _(sLock);

        // get the image from those who have been already initialized
        image = find_loaded_image_by_id(imageID, false);
        if (image == NULL)
                return B_BAD_IMAGE_ID;

        // iterate through all the hash buckets until we've found the one
        for (i = 0; i < HASHTABSIZE(image); i++) {
                for (j = HASHBUCKETS(image)[i]; j != STN_UNDEF; j = HASHCHAINS(image)[j]) {
                        elf_sym *symbol = &image->syms[j];

                        if (count == num) {
                                const char* symbolName = SYMNAME(image, symbol);
                                strlcpy(nameBuffer, symbolName, *_nameLength);
                                *_nameLength = strlen(symbolName);

                                void* location = (void*)(symbol->st_value
                                        + image->regions[0].delta);
                                int32 type;
                                if (symbol->Type() == STT_FUNC)
                                        type = B_SYMBOL_TYPE_TEXT;
                                else if (symbol->Type() == STT_OBJECT)
                                        type = B_SYMBOL_TYPE_DATA;
                                else
                                        type = B_SYMBOL_TYPE_ANY;
                                        // TODO: check with the return types of that BeOS function

                                patch_defined_symbol(image, symbolName, &location, &type);

                                if (_type != NULL)
                                        *_type = type;
                                if (_location != NULL)
                                        *_location = location;
                                goto out;
                        }
                        count++;
                }
        }
out:
        if (num != count)
                return B_BAD_INDEX;

        return B_OK;
}


status_t
get_nearest_symbol_at_address(void* address, image_id* _imageID,
        char** _imagePath, char** _imageName, char** _symbolName, int32* _type,
        void** _location, bool* _exactMatch)
{
        RecursiveLocker _(sLock);

        image_t* image = find_loaded_image_by_address((addr_t)address);
        if (image == NULL) {
                addr_t commpageBegin = (addr_t)__gCommPageAddress;
                addr_t commpageEnd = (addr_t)commpageBegin + COMMPAGE_SIZE;

                // The caller may be looking for a commpage symbol.
                if ((addr_t)address >= commpageBegin && (addr_t)address < commpageEnd) {
                        return get_nearest_commpage_symbol_at_address_locked(address, _imageID, _imagePath,
                                _imageName, _symbolName, _type, _location, _exactMatch);
                }

                return B_BAD_VALUE;
        }

        if (_imageID != NULL)
                *_imageID = image->id;
        if (_imagePath != NULL)
                *_imagePath = image->path;
        if (_imageName != NULL)
                *_imageName = image->name;

        // If the caller does not want the actual symbol name, only the image,
        // we can just return immediately.
        if (_symbolName == NULL && _type == NULL && _location == NULL)
                return B_OK;

        bool exactMatch = false;
        elf_sym* foundSymbol = NULL;
        addr_t foundLocation = (addr_t)NULL;

        for (uint32 i = 0; i < HASHTABSIZE(image) && !exactMatch; i++) {
                for (int32 j = HASHBUCKETS(image)[i]; j != STN_UNDEF;
                                j = HASHCHAINS(image)[j]) {
                        elf_sym *symbol = &image->syms[j];
                        addr_t location = symbol->st_value + image->regions[0].delta;

                        if (location <= (addr_t)address && location >= foundLocation) {
                                foundSymbol = symbol;
                                foundLocation = location;

                                // jump out if we have an exact match
                                if (location + symbol->st_size > (addr_t)address) {
                                        exactMatch = true;
                                        break;
                                }
                        }
                }
        }

        if (_exactMatch != NULL)
                *_exactMatch = exactMatch;

        if (foundSymbol != NULL) {
                *_symbolName = SYMNAME(image, foundSymbol);

                if (_type != NULL) {
                        if (foundSymbol->Type() == STT_FUNC)
                                *_type = B_SYMBOL_TYPE_TEXT;
                        else if (foundSymbol->Type() == STT_OBJECT)
                                *_type = B_SYMBOL_TYPE_DATA;
                        else
                                *_type = B_SYMBOL_TYPE_ANY;
                        // TODO: check with the return types of that BeOS function
                }

                if (_location != NULL)
                        *_location = (void*)foundLocation;
        } else {
                *_symbolName = NULL;
                if (_location != NULL)
                        *_location = NULL;
        }

        return B_OK;
}


status_t
get_symbol(image_id imageID, char const *symbolName, int32 symbolType,
        bool recursive, image_id *_inImage, void **_location)
{
        status_t status = B_OK;
        image_t *image;

        if (imageID < B_OK)
                return B_BAD_IMAGE_ID;
        if (symbolName == NULL)
                return B_BAD_VALUE;

        // Previously, these functions were called in __haiku_init_before
        // and __haiku_init_after. Now we call them inside runtime_loader,
        // so we prevent applications from fetching them.
        if (strcmp(symbolName, B_INIT_BEFORE_FUNCTION_NAME) == 0
                || strcmp(symbolName, B_INIT_AFTER_FUNCTION_NAME) == 0
                || strcmp(symbolName, B_TERM_BEFORE_FUNCTION_NAME) == 0
                || strcmp(symbolName, B_TERM_AFTER_FUNCTION_NAME) == 0)
                return B_BAD_VALUE;

        RecursiveLocker _(sLock);
                // for now, just do stupid simple global locking

        // get the image from those who have been already initialized
        image = find_loaded_image_by_id(imageID, false);
        if (image != NULL) {
                if (recursive) {
                        // breadth-first search in the given image and its dependencies
                        status = find_symbol_breadth_first(image,
                                SymbolLookupInfo(symbolName, symbolType, NULL,
                                        LOOKUP_FLAG_DEFAULT_VERSION),
                                &image, _location);
                } else {
                        status = find_symbol(image,
                                SymbolLookupInfo(symbolName, symbolType, NULL,
                                        LOOKUP_FLAG_DEFAULT_VERSION),
                                _location);
                }

                if (status == B_OK && _inImage != NULL)
                        *_inImage = image->id;
        } else
                status = B_BAD_IMAGE_ID;

        return status;
}


status_t
get_library_symbol(void* handle, void* caller, const char* symbolName,
        void **_location)
{
        status_t status = B_ENTRY_NOT_FOUND;

        if (symbolName == NULL)
                return B_BAD_VALUE;

        RecursiveLocker _(sLock);
                // for now, just do stupid simple global locking

        if (handle == RTLD_DEFAULT || handle == RLD_GLOBAL_SCOPE) {
                // look in the default scope
                image_t* image;
                elf_sym* symbol = find_undefined_symbol_global(gProgramImage,
                        gProgramImage,
                        SymbolLookupInfo(symbolName, B_SYMBOL_TYPE_ANY, NULL,
                                LOOKUP_FLAG_DEFAULT_VERSION),
                        &image);
                if (symbol != NULL) {
                        *_location = (void*)(symbol->st_value + image->regions[0].delta);
                        int32 symbolType = symbol->Type() == STT_FUNC
                                ? B_SYMBOL_TYPE_TEXT : B_SYMBOL_TYPE_DATA;
                        patch_defined_symbol(image, symbolName, _location, &symbolType);
                        status = B_OK;
                }
        } else if (handle == RTLD_NEXT) {
                // Look in the default scope, but also in the dependencies of the
                // calling image. Return the next after the caller symbol.

                // First of all, find the caller image.
                image_t* callerImage = find_loaded_image_by_address((addr_t)caller);
                if (callerImage != NULL) {
                        // found the caller -- now search the global scope until we find
                        // the next symbol
                        bool hitCallerImage = false;
                        set_image_flags_recursively(callerImage, RFLAG_USE_FOR_RESOLVING);

                        elf_sym* candidateSymbol = NULL;
                        image_t* candidateImage = NULL;

                        image_t* image = get_loaded_images().head;
                        for (; image != NULL; image = image->next) {
                                // skip the caller image
                                if (image == callerImage) {
                                        hitCallerImage = true;
                                        continue;
                                }

                                // skip all images up to the caller image; also skip add-on
                                // images and those not marked above for resolution
                                if (!hitCallerImage || image->type == B_ADD_ON_IMAGE
                                        || (image->flags
                                                & (RTLD_GLOBAL | RFLAG_USE_FOR_RESOLVING)) == 0) {
                                        continue;
                                }

                                elf_sym *symbol = find_symbol(image,
                                        SymbolLookupInfo(symbolName, B_SYMBOL_TYPE_TEXT, NULL,
                                                LOOKUP_FLAG_DEFAULT_VERSION));
                                if (symbol == NULL)
                                        continue;

                                // found a symbol
                                bool isWeak = symbol->Bind() == STB_WEAK;
                                if (candidateImage == NULL || !isWeak) {
                                        candidateSymbol = symbol;
                                        candidateImage = image;

                                        if (!isWeak)
                                                break;
                                }

                                // symbol is weak, so we need to continue
                        }

                        if (candidateSymbol != NULL) {
                                // found the symbol
                                *_location = (void*)(candidateSymbol->st_value
                                        + candidateImage->regions[0].delta);
                                int32 symbolType = B_SYMBOL_TYPE_TEXT;
                                patch_defined_symbol(candidateImage, symbolName, _location,
                                        &symbolType);
                                status = B_OK;
                        }

                        clear_image_flags_recursively(callerImage, RFLAG_USE_FOR_RESOLVING);
                }
        } else {
                // breadth-first search in the given image and its dependencies
                image_t* inImage;
                status = find_symbol_breadth_first((image_t*)handle,
                        SymbolLookupInfo(symbolName, B_SYMBOL_TYPE_ANY, NULL,
                                LOOKUP_FLAG_DEFAULT_VERSION),
                        &inImage, _location);
        }

        return status;
}


status_t
get_next_image_dependency(image_id id, uint32 *cookie, const char **_name)
{
        uint32 i, j, searchIndex = *cookie;
        elf_dyn *dynamicSection;
        image_t *image;

        if (_name == NULL)
                return B_BAD_VALUE;

        RecursiveLocker _(sLock);

        image = find_loaded_image_by_id(id, false);
        if (image == NULL)
                return B_BAD_IMAGE_ID;

        dynamicSection = (elf_dyn *)image->dynamic_ptr;
        if (dynamicSection == NULL || image->num_needed <= searchIndex)
                return B_ENTRY_NOT_FOUND;

        for (i = 0, j = 0; dynamicSection[i].d_tag != DT_NULL; i++) {
                if (dynamicSection[i].d_tag != DT_NEEDED)
                        continue;

                if (j++ == searchIndex) {
                        int32 neededOffset = dynamicSection[i].d_un.d_val;

                        *_name = STRING(image, neededOffset);
                        *cookie = searchIndex + 1;
                        return B_OK;
                }
        }

        return B_ENTRY_NOT_FOUND;
}


//      #pragma mark - runtime_loader private exports


/*! Read and verify the ELF header */
status_t
elf_verify_header(void *header, size_t length)
{
        int32 programSize, sectionSize;

        if (length < sizeof(elf_ehdr))
                return B_NOT_AN_EXECUTABLE;

        return parse_elf_header((elf_ehdr *)header, &programSize, &sectionSize);
}


#ifdef _COMPAT_MODE
#ifdef __x86_64__
status_t
elf32_verify_header(void *header, size_t length)
{
        int32 programSize, sectionSize;

        if (length < sizeof(Elf32_Ehdr))
                return B_NOT_AN_EXECUTABLE;

        return parse_elf32_header((Elf32_Ehdr *)header, &programSize, &sectionSize);
}
#else
status_t
elf64_verify_header(void *header, size_t length)
{
        int32 programSize, sectionSize;

        if (length < sizeof(Elf64_Ehdr))
                return B_NOT_AN_EXECUTABLE;

        return parse_elf64_header((Elf64_Ehdr *)header, &programSize, &sectionSize);
}
#endif  // __x86_64__
#endif  // _COMPAT_MODE


void
terminate_program(void)
{
        image_t **termList;
        ssize_t count, i;

        count = get_sorted_image_list(NULL, &termList, RFLAG_TERMINATED);
        if (count < B_OK)
                return;

        if (gInvalidImageIDs) {
                // After fork, we lazily rebuild the image IDs of all loaded images
                update_image_ids();
        }

        TRACE(("%ld: terminate dependencies\n", find_thread(NULL)));
        for (i = count; i-- > 0;) {
                image_t *image = termList[i];

                TRACE(("%ld:  term: %s\n", find_thread(NULL), image->name));

                image_event(image, IMAGE_EVENT_UNINITIALIZING);

                call_term_functions(image);

                image_event(image, IMAGE_EVENT_UNLOADING);
        }
        TRACE(("%ld:  term done.\n", find_thread(NULL)));

        free(termList);
}


void
rldelf_init(void)
{
        init_add_ons();

        // create the debug area
        {
                size_t size = TO_PAGE_SIZE(sizeof(runtime_loader_debug_area));

                runtime_loader_debug_area *area;
                area_id areaID = _kern_create_area(RUNTIME_LOADER_DEBUG_AREA_NAME,
                        (void **)&area, B_RANDOMIZED_ANY_ADDRESS, size, B_NO_LOCK,
                        B_READ_AREA | B_WRITE_AREA);
                if (areaID < B_OK) {
                        FATAL("Failed to create debug area.\n");
                        _kern_loading_app_failed(areaID);
                }

                area->loaded_images = &get_loaded_images();
        }

        // initialize error message if needed
        if (report_errors()) {
                void *buffer = malloc(1024);
                if (buffer == NULL)
                        return;

                gErrorMessage.SetTo(buffer, 1024, 'Rler');
        }
}


status_t
elf_reinit_after_fork(void)
{
        recursive_lock_init(&sLock, kLockName);

        // We also need to update the IDs of our images. We are the child and
        // and have cloned images with different IDs. Since in most cases (fork()
        // + exec*()) this would just increase the fork() overhead with no one
        // caring, we do that lazily, when first doing something different.
        gInvalidImageIDs = true;

        return B_OK;
}