rename the *_load_time examples, start GUIDE.txt, write the xlist base layer example
parent
a3befc6dcd
commit
40183c2eed
|
@ -0,0 +1,21 @@
|
|||
The main goal of this investigation is to organize shared data and code across
|
||||
multiple binary files. This is especially important for something like a base
|
||||
layer that will be used in a program that supports hot-reloading or plugins.
|
||||
|
||||
Each isolated example in this repository explores a way to set up the base
|
||||
layer, plugin, and main program.
|
||||
|
||||
The examples:
|
||||
|
||||
*_concrete - Concrete examples for each operating system I have investigated
|
||||
showing how to setup and use various types of dynamic linking. In
|
||||
these the "base" layer goes through load-time linking, the
|
||||
"plugin" layer goes through run-time linking, and the "main" layer
|
||||
acts as the executable that must bind it all together.
|
||||
|
||||
xlist - My prefered solution to the problem posed in the investigation which
|
||||
relies only on run-time linking. It uses an xlist to manage the
|
||||
maintenance burden of run-time linking, and a basic outline of the
|
||||
abstracted form of each layer.
|
||||
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// base symbols shared
|
||||
|
||||
#if defined(BASE_IMPLEMENTOR)
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int x = 0;
|
||||
|
||||
void
|
||||
base_func(void){
|
||||
printf("x = %d\n", x);
|
||||
x += 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// dynamic function linkage protocol
|
||||
|
||||
#if defined(BASE_IMPLEMENTOR)
|
||||
|
||||
EXPORT_SYMBOL BASE_Funcs*
|
||||
base_export_functions(void){
|
||||
static BASE_Funcs funcs = {0};
|
||||
if (funcs.base_func == 0){
|
||||
#define X(N,R,P) funcs.N = N;
|
||||
#include "base.xlist.h"
|
||||
#undef X
|
||||
}
|
||||
return(&funcs);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(BASE_IMPLEMENTOR)
|
||||
|
||||
#if OS_WINDOWS
|
||||
# include <Windows.h>
|
||||
#elif OS_LINUX
|
||||
# include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
BEFORE_MAIN(base_dynamic_user_init){
|
||||
BASE_Funcs *funcs = 0;
|
||||
|
||||
#if OS_WINDOWS
|
||||
HMODULE module = LoadLibraryA("base.dll");
|
||||
if (module != 0){
|
||||
BASE_ExportFuncs *base_export_functions = (BASE_ExportFuncs*)GetProcAddress(module, "base_export_functions");
|
||||
if (base_export_functions != 0){
|
||||
funcs = base_export_functions();
|
||||
}
|
||||
}
|
||||
#elif OS_LINUX
|
||||
void *module = dlopen("./base.so", RTLD_NOW);
|
||||
if (module != 0){
|
||||
BASE_ExportFuncs *base_export_functions = (BASE_ExportFuncs*)dlsym(module, "base_export_functions");
|
||||
if (base_export_functions != 0){
|
||||
funcs = base_export_functions();
|
||||
}
|
||||
}
|
||||
#else
|
||||
# error base_import_functions not completed for this OS
|
||||
#endif
|
||||
|
||||
#define X(N,R,P) N = funcs->N;
|
||||
#include "base.xlist.h"
|
||||
#undef X
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,79 @@
|
|||
// baby context cracking
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define OS_WINDOWS 1
|
||||
#elif defined(__gnu_linux__)
|
||||
# define OS_LINUX 1
|
||||
#else
|
||||
# error this configuration of compiler & OS is not supported
|
||||
#endif
|
||||
|
||||
#if !defined(OS_WINDOWS)
|
||||
# define OS_WINDOWS 0
|
||||
#endif
|
||||
#if !defined(OS_LINUX)
|
||||
# define OS_LINUX 0
|
||||
#endif
|
||||
|
||||
|
||||
// linkage keyword abstraction
|
||||
|
||||
#if OS_WINDOWS
|
||||
# define EXPORT_SYMBOL __declspec(dllexport)
|
||||
#elif OS_LINUX
|
||||
# define EXPORT_SYMBOL __attribute__ ((visibility ("default")))
|
||||
#else
|
||||
# error keyword abstractions missing for this OS
|
||||
#endif
|
||||
|
||||
|
||||
#if OS_WINDOWS
|
||||
|
||||
# pragma section(".CRT$XCU", read)
|
||||
|
||||
# define BEFORE_MAIN(n) extern void n(void); \
|
||||
__declspec(allocate(".CRT$XCU")) void (*n##__)(void) = n; \
|
||||
__pragma(comment(linker, "/include:" #n)) extern void n(void)
|
||||
|
||||
#elif OS_LINUX
|
||||
|
||||
# define BEFORE_MAIN(n) \
|
||||
__attribute__((constructor)) static void n(void)
|
||||
|
||||
#else
|
||||
# error BEFORE_MAIN missing for this OS
|
||||
#endif
|
||||
|
||||
// base layer types
|
||||
|
||||
typedef void BASE_Library;
|
||||
|
||||
|
||||
// base symbols shared
|
||||
|
||||
#if defined(BASE_IMPLEMENTOR)
|
||||
|
||||
void base_func(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// dynamic function linkage protocol
|
||||
|
||||
typedef struct BASE_Funcs{
|
||||
#define X(N,R,P) R (*N) P;
|
||||
#include "base.xlist.h"
|
||||
#undef X
|
||||
} BASE_Funcs;
|
||||
|
||||
typedef BASE_Funcs* BASE_ExportFuncs(void);
|
||||
|
||||
#if defined(BASE_IMPLEMENTOR)
|
||||
EXPORT_SYMBOL BASE_Funcs* base_export_functions(void);
|
||||
#endif
|
||||
|
||||
#if !defined(BASE_IMPLEMENTOR)
|
||||
#define X(N,R,P) R (*N) P = 0;
|
||||
#include "base.xlist.h"
|
||||
#undef X
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
#define BASE_IMPLEMENTOR 1
|
||||
#include "base.h"
|
||||
#include "base.c"
|
|
@ -0,0 +1 @@
|
|||
X(base_func, void, (void))
|
|
@ -0,0 +1,17 @@
|
|||
@echo off
|
||||
|
||||
REM: Path setup
|
||||
|
||||
SET src=%cd%
|
||||
cd ..
|
||||
if not exist "build\" mkdir build
|
||||
cd build
|
||||
|
||||
REM: Build the base layer
|
||||
cl /nologo /Zi /LD %src%\base.target.c /Febase.dll
|
||||
|
||||
REM: Build the main layer
|
||||
cl /nologo /Zi %src%\main.c
|
||||
|
||||
REM: Build the plugin layer
|
||||
cl /nologo /Zi /LD %src%\plugin.c /Feplugin.dll
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Path setup
|
||||
src=$PWD
|
||||
cd ..
|
||||
mkdir -p build
|
||||
cd build
|
||||
|
||||
# Build the base layer as base.so
|
||||
gcc -fvisibility=hidden -fPIC -shared $src/base.target.c -o base.so
|
||||
|
||||
# Build the main layer
|
||||
gcc -fvisibility=hidden $src/main.c -o main
|
||||
|
||||
# Build the plugin layer
|
||||
gcc -fvisibility=hidden -fPIC -shared $src/plugin.c -o plugin.so
|
|
@ -0,0 +1,38 @@
|
|||
#include "base.h"
|
||||
|
||||
#include "base.c"
|
||||
|
||||
|
||||
// setup plugin_func as a function pointer so that it can be linked at run time
|
||||
typedef void PLUGIN_Func(void);
|
||||
PLUGIN_Func *plugin_func = 0;
|
||||
|
||||
|
||||
int main(){
|
||||
// base layer initialization is automatic and runs before main - base functions begin working right away
|
||||
base_func();
|
||||
|
||||
|
||||
// to call a function with run-time linking, we must manually load and link it
|
||||
#if OS_WINDOWS
|
||||
HMODULE module = LoadLibraryA("plugin.dll");
|
||||
if (module != 0){
|
||||
plugin_func = (PLUGIN_Func*)GetProcAddress(module, "plugin_func");
|
||||
}
|
||||
#elif OS_LINUX
|
||||
void *module = dlopen("./plugin.so", RTLD_NOW);
|
||||
if (module != 0){
|
||||
plugin_func = (PLUGIN_Func*)dlsym(module, "plugin_func");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// calls to plugin_func only work after the "plugin" loaded successfully
|
||||
if (plugin_func != 0){
|
||||
plugin_func();
|
||||
}
|
||||
|
||||
|
||||
// demonstration: the data structures contained in the 'base' are not duplicated in each binary
|
||||
base_func();
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#include "base.h"
|
||||
|
||||
#include "base.c"
|
||||
|
||||
#include <stdio.h>
|
||||
EXPORT_SYMBOL void
|
||||
plugin_func(void){
|
||||
printf("provided by plugin: {\n");
|
||||
printf(" ");
|
||||
base_func();
|
||||
printf(" ");
|
||||
base_func();
|
||||
printf("}\n");
|
||||
}
|
||||
|
Loading…
Reference in New Issue