root/src/system/runtime_loader/add_ons.cpp
/*
 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */

#include "add_ons.h"

#include <util/kernel_cpp.h>

#include "runtime_loader_private.h"


typedef DoublyLinkedList<RuntimeLoaderAddOn> AddOnList;


static status_t register_defined_symbol_patcher(struct image_t* image,
        runtime_loader_symbol_patcher* _patcher, void* cookie);
static void unregister_defined_symbol_patcher(struct image_t* image,
        runtime_loader_symbol_patcher* _patcher, void* cookie);
static status_t register_undefined_symbol_patcher(struct image_t* image,
        runtime_loader_symbol_patcher* _patcher, void* cookie);
static void unregister_undefined_symbol_patcher(struct image_t* image,
        runtime_loader_symbol_patcher* _patcher, void* cookie);


static AddOnList sAddOns;

static runtime_loader_add_on_export sRuntimeLoaderAddOnExport = {
        register_defined_symbol_patcher,
        unregister_defined_symbol_patcher,
        register_undefined_symbol_patcher,
        unregister_undefined_symbol_patcher
};


// #pragma mark - add-on support functions


static status_t
register_defined_symbol_patcher(struct image_t* image,
        runtime_loader_symbol_patcher* _patcher, void* cookie)
{
        RuntimeLoaderSymbolPatcher* patcher
                = new(mynothrow) RuntimeLoaderSymbolPatcher(_patcher, cookie);
        if (patcher == NULL)
                return B_NO_MEMORY;

        patcher->next = image->defined_symbol_patchers;
        image->defined_symbol_patchers = patcher;

        return B_OK;
}


static void
unregister_defined_symbol_patcher(struct image_t* image,
        runtime_loader_symbol_patcher* _patcher, void* cookie)
{
        RuntimeLoaderSymbolPatcher** patcher = &image->defined_symbol_patchers;
        while (*patcher != NULL) {
                if ((*patcher)->patcher == _patcher && (*patcher)->cookie == cookie) {
                        RuntimeLoaderSymbolPatcher* toDelete = *patcher;
                        *patcher = (*patcher)->next;
                        delete toDelete;
                        return;
                }
                patcher = &(*patcher)->next;
        }
}


static status_t
register_undefined_symbol_patcher(struct image_t* image,
        runtime_loader_symbol_patcher* _patcher, void* cookie)
{
        RuntimeLoaderSymbolPatcher* patcher
                = new(mynothrow) RuntimeLoaderSymbolPatcher(_patcher, cookie);
        if (patcher == NULL)
                return B_NO_MEMORY;

        patcher->next = image->undefined_symbol_patchers;
        image->undefined_symbol_patchers = patcher;

        return B_OK;
}


static void
unregister_undefined_symbol_patcher(struct image_t* image,
        runtime_loader_symbol_patcher* _patcher, void* cookie)
{
        RuntimeLoaderSymbolPatcher** patcher = &image->undefined_symbol_patchers;
        while (*patcher != NULL) {
                if ((*patcher)->patcher == _patcher && (*patcher)->cookie == cookie) {
                        RuntimeLoaderSymbolPatcher* toDelete = *patcher;
                        *patcher = (*patcher)->next;
                        delete toDelete;
                        return;
                }
                patcher = &(*patcher)->next;
        }
}


// #pragma mark -


void
init_add_ons()
{
        // invoke static constructors
        new(&sAddOns) AddOnList;
}


status_t
add_add_on(image_t* image, runtime_loader_add_on* addOnStruct)
{
        RuntimeLoaderAddOn* addOn = new(mynothrow) RuntimeLoaderAddOn(image,
                addOnStruct);
        if (addOn == NULL)
                return B_NO_MEMORY;

        sAddOns.Add(addOn);
        addOnStruct->init(&gRuntimeLoader, &sRuntimeLoaderAddOnExport);

        return B_OK;
}


void
image_event(image_t* image, uint32 event)
{
        AddOnList::Iterator it = sAddOns.GetIterator();
        while (RuntimeLoaderAddOn* addOn = it.Next()) {
                void (*function)(image_t* image) = NULL;

                switch (event) {
                        case IMAGE_EVENT_LOADED:
                                function = addOn->addOn->image_loaded;
                                break;
                        case IMAGE_EVENT_RELOCATED:
                                function = addOn->addOn->image_relocated;
                                break;
                        case IMAGE_EVENT_INITIALIZED:
                                function = addOn->addOn->image_initialized;
                                break;
                        case IMAGE_EVENT_UNINITIALIZING:
                                function = addOn->addOn->image_uninitializing;
                                break;
                        case IMAGE_EVENT_UNLOADING:
                                function = addOn->addOn->image_unloading;
                                break;
                }

                if (function != NULL)
                        function(image);
        }
}