ВходНаше всё Теги codebook PARTS Поиск Опросы Закон Понедельник
21 сентября
/1036953
Топик полностью
fk0 (16.09.2020 04:46, просмотров: 59) в ответ на модули могут получать доступ к функциям основной логики через таблицу указателей размещенную по прибитому адресу! С этим не должно быть проблем! - автор: Aleksey_75
Не надо прибивать адреса. Верней надо, но ровно один адрес одной функции. Нужно лишь сделать самодельный недо-COM. Где эта одна функция в зависимости от аргументов реализует несколько функций, вроде получения версии, получения интерфейса по заданным параметрам. Предполагается что компонент (библиотека) может реализовать несколько интерфейсов. А интерфейс -- это структура из набора указателей на функции. Т.е. загружаешь библиотеку, зовёшь единственную функцию и говоришь ей 

"дай интерфейс такой-то (версии)". Она тебе указатель на эту структуру. И ты уже через структуру используешь все её функции.


В обратную сторону аналогично: чтоб получить интерфейс, например, нужно в свою очередь передать не только версию/номер/имя интерфейса, но и указатель на структуру с неким набором импортируемых функций, соответствующих интерфейсу. И обратно получишь структуру с импортированными функциями. И работай. Библиотека может реализовывать более одного интерфейса потому, например, что интерфейс первой версии, например, импортирует набор функций бутлоадера первой версии, а интерфейс второй версии хочет расширенный набор функций бутлоадера. И с двумя интерфейсами ты можешь работать с обоими бутлоадерами: с новым через второй интерфейс, и со старым через первый. И в обратную сторону, новый бутлоадер сможет работать со старой библотекой. Ну и ещё эта единственная функция должна реализовывать механизм перечисления интерфейсов, чтоб бутлоадер посмотрел, что библиотека может и выбрал что-нибудь. Можно, например, через callback: библиотека итерируется по своим интерфейсам и зовёт callback с описателем интерфейса, ноль возвращённый из callback'а прерывает процесс.


Сейчас попробую изобразить... пол ночи красноглазил, держи нетленку (https://coliru.stacked-crooked.com/a/71aedbcbdb9ee107 -- тут поиграйся с параметрами команной строки).

Завтра допишу детали реализации.



#include <stddef.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>

// Common architecture thesis:
//
// 1. whole process might consist of multiple libraries;
//
// 2. each library might implement one or more interfaces;
//
// 3. interface -- is a set of functions, which can be used
//    by other libraries;
//
// 4. each interfaces has some unique identifier, which determines it's
//    name and version;
//
// 5. interface of next version might inherit previously implemented
//    interface and extend it or might be completely different;
// 6. interface is singletone by the nature, there is no possibility
//    to have multiple instances of one interface;
//
// 7. initially each library provides exactly only one entry point
//    (function) via which functons can exchange with pointers to interfaces;
//
// 8. program can enumerate all interfaces available in the whole system;
//
// 9. program can select which one exactly interface from multiple
//    available ones it will use;
//
// 10. all library functions called by loader have no any relations to library
//     initialization -- these functions intendent only to linking/loading process,
//     initialization shoul be performed via separate user-defined functions.

///// Common definitions.

#define DECONST(v) ((void*)(intptr_t)(v))

struct LibLoader;

// Interface descriptor.
typedef struct
{
    // Interface name, which also defines interface version. Should be unique.
    const char *name;
        
    // Function which returns implementation of the interface described by this instance of IDescriptor.
    // Return value is the actual interface.
    // Function should not be called by libraries itself -- it is indendent for use by Loader.
    const void* (*_GetInterface)(struct LibLoader *);
}
IDescriptor;

// This is a type of callback function, which used by EnumerateInterface function (see below).
typedef const void* IEnumCallback(const IDescriptor *interface, void *thiz);

// Interface which should be implemented by each library, which is used
// to enumerate all interfaces provided by this library. Library might have
// multiple interfaces, main purpose for which is that the library might
// have disctinct incompatible interface versions. It's preferable to make
// this interface to be a base class for all other interfaces: this allows
// to enumerate all other interfaces by only having any other interface
// (which is usually acessible by other libraries during dependency resolving):
// without this interfaces can only be enumerated via loader.
typedef struct
{
    // Functions calls supplied callback function `cb' for each interface
    // which current library implements. Parameter `thiz' is passed to `cb' as is.
    // The return value might be NULL (all interfaces enumerated) or the result
    // of callback function -- some particular interface. Enumeration stops
    // immediately if callback function returns non-NULL value.
    const void* (*EnumerateInterfaces)(IEnumCallback *cb, void *thiz);
}
IEnumerator;

// Interface which represents the linker/loader.
typedef struct
{
    // Base class for this interface, allows loader interfaces enumeration
    // (for future extension).
    IEnumerator base;
    
    // This function allows enumerate all interfaces available in whole system
    // (ones which reside in different libraries). In most cases this function
    // isn't needed for user code.
    const void* (*EnumerateInterfaces)(IEnumCallback *cb, void *thiz);
    
    // In most times only this one function is required to be used from user code:
    // it is needed to resolve dependencies -- to get other interfaces on which
    // this current one depends (this function should be called from _GetInterface
    // method implementetion for each particular interface).
    const void* (*GetInterface)(struct LibLoader *, const char *name);
}
ILoader;

// Current version of the loader.
#define ILoaderVer "LibLoader1.0"

// This class represents loader class instance, which might be used by user code
// to perform other actions via ILoader interface.
typedef struct LibLoader
{
    const ILoader *iloader;
}
LibLoader;

// This function mostly indentent for using by loader itself, in rare cases
// (in multithreaded environment) it can be used by user code.
void GetLoaderInstance(struct LibLoader *);

// This is a type of "main" interface function, which every library should provide.
// This function is only should perform actions required to provide list of available
// interfaces to the loader. Library initialization functions MUST NOT be performed
// in this function. For any initialization tasks, especially, if it required to use
// other interfaces (provided by other libraries), TWO PHASE INITIALIZATION must be
// used. For this the interface can provide specially designated function, which can
// be called after all libraries loaded and linked.
typedef const IEnumerator* LibFuncType(void);

///////////////////////// This is header file for lib3 (which is recursive dependent on lib1

// Functions provided by the library (including base class implementing IEnumeration --
// which is always required for future extensions).
typedef struct
{
    IEnumerator base;
    
    int (*func3)(int);
}
ILib3;

// Interface name and version of Lib3.
#define Lib3Version "Lib3Ver1.0"

//////////////////////////// This is header file for lib2

typedef struct
{
    IEnumerator base;

    int (*func0)(int);
}
ILib2;

#define Lib2Version "Lib2Ver1.0"

//////////////////////////// This is headers of Lib1
//////////////////////////// This library implements two interfaces simulaneously.

// First interface implemented by library;
typedef struct
{
    IEnumerator base;
    
    void (*func1)(void);
    int (*func2)(int);
}
ILib1_1;

// Second interface implemented by this library, which EXTENDS first interface.
typedef struct
{
    ILib1_1 base;
    
    void (*func3)(int);
}
ILib1_2;

// Currently available Lib1 version (last).
#define Lib1Version "Lib1Ver2.0"

////////////////////////////////// This is implementation of Lib3

static const void* lib3_enumerator(IEnumCallback *cb, void *thiz);

// This variable contains reference to interface of Lib1 which will be
// resolved in runtime (see below).
static const ILib1_2 *lib1_impl;

// function(s) implemented by lib3
static int lib3_func3(int x)
{
    lib1_impl->func3(x + x + x);
    return 42;
}

// list of interfaces implemented by lib3
// currently lib3 implements only two interfaces: IEnumerator and ILib3.
static const ILib3 lib3_interfaces =
{
    { lib3_enumerator },
    lib3_func3
};

// Function returns first version (only one implemented) of ILib1 interface.
// If Lib1 use some other interfaces: in this function it must resolve all
// dependencies of current interface via calls to the loader and store
// pointers to foreign interfaces in own data segment.
// Note: all interfaces ARE SINGLETONES.
static const void* lib3_get_ver1(LibLoader *loader)
{
    if (lib1_impl == NULL)
    {
        fputs("lib3_get_ver1\n", stderr);
        
        // resolve dependencies
        const ILib1_2 *result = loader->iloader->GetInterface(loader, Lib1Version);
        if (!result)
            return NULL;
                
        lib1_impl = result;
    }    
    return &lib3_interfaces;
}

// IEnumerator.EnumerateInterfaces implementation for this library
static const void* lib3_enumerator(IEnumCallback *cb, void *thiz)
{
    static const IDescriptor desc = { Lib3Version, lib3_get_ver1 };
    return cb(&desc, thiz);
}

// Main function of the library -- only this function is initially externally visible.
const IEnumerator *library3_main(void)
{        
    return &lib3_interfaces.base;
}

///////////////////////////////////////// This is implementation of Lib2

static const void* lib2_enumerator(IEnumCallback *cb, void *thiz);

// function(s) implemented by library.
static int lib2_func0(int x)
{
    return x*x + 1;
}

// All interfaces implemented by library.
static const ILib2 lib2_interfaces =
{
    { lib2_enumerator },
    lib2_func0
};

// Function returns implemented (only one) library interface.
static const void* lib2_get_ver1(LibLoader *loader)
{
    // this library has no dependencies
    (void)loader;
    fputs("lib2_get_ver1\n", stderr);
    return &lib2_interfaces;
}

// IEnumerator.EnumerateInterfaces implementation for this library
static const void* lib2_enumerator(IEnumCallback *cb, void *thiz)
{
    static const IDescriptor desc = { Lib2Version, lib2_get_ver1 };
    return cb(&desc, thiz);
}

// Main function of the library -- only this function is initially externally visible.
const IEnumerator *library2_main(void)
{        
    return &lib2_interfaces.base;
}

///////////////////////////////////////// This is implementation of Lib1

static const void* lib1_enumerator(IEnumCallback *cb, void *thiz);

// These variable contain reference to interfaces of other libraries
// which will be resolved in runtime (see below).
static const ILib2 *lib2_impl;
static const ILib3 *lib3_impl;

// function(s) implemented by library (from all version of interface);.
static void lib1_func1(void)
{
}

// function(s) implemented by library (from all versions of interface).
static int lib1_func2(int x)
{
    return lib2_impl->func0(x) / 2 - 1;
}
   
  // function(s) implemented by library (from second version of interface)
static void lib1_func3(int x)
{
    (void)(lib2_impl->func0(x) * 2 + lib3_impl->func3(x));
}

// All interfaces implemented by library.
static const ILib1_2 lib1_interfaces =
{
    {
        {
            lib1_enumerator
        },
        lib1_func1,
        lib1_func2,
    },
    lib1_func3
};
 
// Function returns initial (first version) library interface. 
static const void* lib1_get_ver1(LibLoader *loader)
{
    if (lib2_impl == NULL)
    {
        fprintf(stderr, "lib1_get_ver1\n");
        
        // resolve dependencies
        const ILib2 *result = loader->iloader->GetInterface(loader, Lib2Version);
        if (!result)
            return NULL;
            
        lib2_impl = result;
    }
    return &lib1_interfaces.base;
}

// Function returns extended version of library interface. 
static const void* lib1_get_ver2(LibLoader *loader)
{
    if (lib2_impl == NULL)
    {
        fprintf(stderr, "lib1_get_ver2\n");
        
        // This function should resolve dependencies for two libraries:
        // Lib2 and Lib3, as both used in new function (lib1_func3).
        
        const ILib2 *result2 = loader->iloader->GetInterface(loader, Lib2Version);
        if (!result2)
            return NULL;
            
        const ILib3 *result3 = loader->iloader->GetInterface(loader, Lib3Version);
        if (!result3)
            return NULL;
            
        lib2_impl = result2;
        lib3_impl = result3;
    }
    return &lib1_interfaces;    
}

// IEnumerator.EnumerateInterfaces implementation for this library
static const void* lib1_enumerator(IEnumCallback *cb, void *thiz)
{
    static const IDescriptor list[] = 
    {
        { "Lib1Ver1.0", lib1_get_ver1 },
        { "Lib1Ver2.0", lib1_get_ver2 }
    };
    
    const IDescriptor *desc = list;
    unsigned n = sizeof(list)/sizeof(list[0]);
    while (n --> 0)
    {
        const void *result = cb(desc, thiz);
        if (result != NULL)
            return result;
            
        ++desc;
    } 
    
    return NULL;
}

// Main function of the library -- only this function is initially externally visible.
const IEnumerator *library1_main(void)
{        
    return &lib1_interfaces.base.base;
}

//////////////////////////// This is loader module. /////////////////////////////////////////////
//
// Due to the fact, that GetLoaderInstance() function should be available to main() function,
// the loader itself should be part of main program, at same time it is represented in system
// as separate module which implements "LoaderVer1.0" interface.

static const ILoader loader_vtable;

static const IEnumerator *loader_main(void);

// Loader must know all main functions of all libraries.
static LibFuncType *library_list[] =
{
    loader_main,
    library1_main,
    library2_main,
    library3_main
};

// data type used in loader internally
typedef struct LoaderChain
{
    const IDescriptor *desc;
    struct LoaderChain *next;
}
LoaderChain;

// data type used in loader internally
typedef struct
{
    LibLoader base;
    LoaderChain *chain;
}
RealLibLoader;

// ILoader.EnumerateInterfaces function implementation.
static const void* loader_enum(IEnumCallback *cb, void *thiz)
{
    unsigned n = sizeof(library_list)/sizeof(library_list[0]);
    LibFuncType **func = library_list;
    while (n --> 0) 
    {
        const IEnumerator *ie = (*func)();
        const void *result = ie->EnumerateInterfaces(cb, thiz);
        if (result != NULL)
            return result;
            
        ++func;
    }
    
    return NULL;
}

// function used internally by the loader
static const void* byname_cb(const IDescriptor *desc, void *thiz)
{
    return !strcmp(desc->name, thiz) ? desc : NULL; 
}

// ILoader.GetInterface function implementation.
static const void* loader_get(LibLoader *loader, const char *name)
{
    RealLibLoader *thiz = (void*)loader;
    
    const IDescriptor *desc = loader->iloader->EnumerateInterfaces(byname_cb, DECONST(name));
    if (desc == NULL)
    {
        fprintf(stderr, "fatal error: dependent interface '%s' not found!\n", name);     
        return NULL;
    }
    
    // Break recursion: in case of recursion function returns NULL in place of interface
    // expecting  that previously called lookup will return real pointer to interface. 
    // As a result, interface pointers obtained in functions imlementing _GetInterface()
    // should not be used directly. Instead two phase initialization required: at first
    // phase (call to _GetInterface()) variables holding pointers to interfaces should
    // only be initialized, but interface should not be used. And if interface usage required
    // during initialization, then specially designated function should be called later,
    // after all interface lookups finished, and only then perform actual initialization.

    LoaderChain *chain = thiz->chain;
    while (chain != NULL)
    {
        if (desc == chain->desc)
        {
            fprintf(stderr, "avoided recursion for %s\n", desc->name);
            return NULL;
        }
    
        chain = chain->next;
    }
    
    LoaderChain link = { desc, thiz->chain };
    thiz->chain = &link;
    
    const void *result = desc->_GetInterface(loader);   
    
    thiz->chain = link.next;
    
    return result;
}

// Function returns first version (only one implemented) of ILoaderInterface.
static const void* loader_get_ver1(LibLoader *loader)
{
    (void)loader;
    fputs("loader_get_ver1()\n", stderr);
    return &loader_vtable;
}

// Function implements IEnumerator.EnumerateInterface function for the loader itself.
static const void* loader_self(IEnumCallback *cb, void *thiz)
{
    static const IDescriptor desc = { ILoaderVer, loader_get_ver1 };
    return cb(&desc, thiz);
}

// ILoader instance.
static const ILoader loader_vtable =
{
    { loader_self },
    loader_enum,
    loader_get
};

// Main function of the loader -- it shouldn't be exposed, used internally.
const IEnumerator *loader_main(void)
{        
    return &loader_vtable.base;
}

// This function creates new loader instance for each thread.
void GetLoaderInstance(LibLoader *loader)
{
    RealLibLoader *thiz = (void*)loader;
    loader->iloader = &loader_vtable;
    thiz->chain = NULL;
}

/////////////////// Better to represent main program as separate module,
/////////////////// but in this example it only demonstrates linking process.

static const void* enum_cb(const IDescriptor *desc, void *thiz)
{
    (void)thiz;
    puts(desc->name);
    return NULL;
}

int main(int argc, char *argv[])
{
    LibLoader loader;
    GetLoaderInstance(&loader);
    
    // List all available modules.
    puts("Available interfaces:");
    loader.iloader->EnumerateInterfaces(enum_cb, NULL);
    puts("");
    fflush(stdout);
 
    if (argc <= 1)
        return puts("please specify interfaces which should be instantiated"), 1;
    
    // Load modules specified at command line.    
    unsigned n;
    for (n = 1; n < (unsigned)argc; n++)
    {
        printf("loading %s\n", argv[n]), fflush(stdout);
        loader.iloader->GetInterface(&loader, argv[n]);
    }
    puts("");
        
    return 0;   
}
[ZX]
dynamic linkingдинамическое связывание
Ответить