From 40183c2eed7d3dd99922931c4d96682e1bb3184b Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Tue, 23 Apr 2024 20:26:45 -0700 Subject: [PATCH] rename the *_load_time examples, start GUIDE.txt, write the xlist base layer example --- GUIDE.txt | 21 +++++ {linux_load_time => linux_concrete}/build.sh | 0 .../linux_base.c | 0 .../linux_main.c | 0 .../linux_plugin.c | 0 {win32_load_time => win32_concrete}/build.bat | 0 .../win32_base.c | 0 .../win32_main.c | 0 .../win32_plugin.c | 0 xlist/base.c | 72 +++++++++++++++++ xlist/base.h | 79 +++++++++++++++++++ xlist/base.target.c | 3 + xlist/base.xlist.h | 1 + xlist/build.bat | 17 ++++ xlist/build.sh | 16 ++++ xlist/main.c | 38 +++++++++ xlist/plugin.c | 15 ++++ 17 files changed, 262 insertions(+) create mode 100644 GUIDE.txt rename {linux_load_time => linux_concrete}/build.sh (100%) rename {linux_load_time => linux_concrete}/linux_base.c (100%) rename {linux_load_time => linux_concrete}/linux_main.c (100%) rename {linux_load_time => linux_concrete}/linux_plugin.c (100%) rename {win32_load_time => win32_concrete}/build.bat (100%) rename {win32_load_time => win32_concrete}/win32_base.c (100%) rename {win32_load_time => win32_concrete}/win32_main.c (100%) rename {win32_load_time => win32_concrete}/win32_plugin.c (100%) create mode 100644 xlist/base.c create mode 100644 xlist/base.h create mode 100644 xlist/base.target.c create mode 100644 xlist/base.xlist.h create mode 100644 xlist/build.bat create mode 100644 xlist/build.sh create mode 100644 xlist/main.c create mode 100644 xlist/plugin.c diff --git a/GUIDE.txt b/GUIDE.txt new file mode 100644 index 0000000..1e54e60 --- /dev/null +++ b/GUIDE.txt @@ -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. + + diff --git a/linux_load_time/build.sh b/linux_concrete/build.sh similarity index 100% rename from linux_load_time/build.sh rename to linux_concrete/build.sh diff --git a/linux_load_time/linux_base.c b/linux_concrete/linux_base.c similarity index 100% rename from linux_load_time/linux_base.c rename to linux_concrete/linux_base.c diff --git a/linux_load_time/linux_main.c b/linux_concrete/linux_main.c similarity index 100% rename from linux_load_time/linux_main.c rename to linux_concrete/linux_main.c diff --git a/linux_load_time/linux_plugin.c b/linux_concrete/linux_plugin.c similarity index 100% rename from linux_load_time/linux_plugin.c rename to linux_concrete/linux_plugin.c diff --git a/win32_load_time/build.bat b/win32_concrete/build.bat similarity index 100% rename from win32_load_time/build.bat rename to win32_concrete/build.bat diff --git a/win32_load_time/win32_base.c b/win32_concrete/win32_base.c similarity index 100% rename from win32_load_time/win32_base.c rename to win32_concrete/win32_base.c diff --git a/win32_load_time/win32_main.c b/win32_concrete/win32_main.c similarity index 100% rename from win32_load_time/win32_main.c rename to win32_concrete/win32_main.c diff --git a/win32_load_time/win32_plugin.c b/win32_concrete/win32_plugin.c similarity index 100% rename from win32_load_time/win32_plugin.c rename to win32_concrete/win32_plugin.c diff --git a/xlist/base.c b/xlist/base.c new file mode 100644 index 0000000..2162ef8 --- /dev/null +++ b/xlist/base.c @@ -0,0 +1,72 @@ +// base symbols shared + +#if defined(BASE_IMPLEMENTOR) + +#include + +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 +#elif OS_LINUX +# include +#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 diff --git a/xlist/base.h b/xlist/base.h new file mode 100644 index 0000000..801e6d2 --- /dev/null +++ b/xlist/base.h @@ -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 diff --git a/xlist/base.target.c b/xlist/base.target.c new file mode 100644 index 0000000..8041384 --- /dev/null +++ b/xlist/base.target.c @@ -0,0 +1,3 @@ +#define BASE_IMPLEMENTOR 1 +#include "base.h" +#include "base.c" \ No newline at end of file diff --git a/xlist/base.xlist.h b/xlist/base.xlist.h new file mode 100644 index 0000000..7a4db73 --- /dev/null +++ b/xlist/base.xlist.h @@ -0,0 +1 @@ +X(base_func, void, (void)) diff --git a/xlist/build.bat b/xlist/build.bat new file mode 100644 index 0000000..f3c597b --- /dev/null +++ b/xlist/build.bat @@ -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 diff --git a/xlist/build.sh b/xlist/build.sh new file mode 100644 index 0000000..b4505ab --- /dev/null +++ b/xlist/build.sh @@ -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 diff --git a/xlist/main.c b/xlist/main.c new file mode 100644 index 0000000..b8a576a --- /dev/null +++ b/xlist/main.c @@ -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(); +} diff --git a/xlist/plugin.c b/xlist/plugin.c new file mode 100644 index 0000000..8fa0202 --- /dev/null +++ b/xlist/plugin.c @@ -0,0 +1,15 @@ +#include "base.h" + +#include "base.c" + +#include +EXPORT_SYMBOL void +plugin_func(void){ + printf("provided by plugin: {\n"); + printf(" "); + base_func(); + printf(" "); + base_func(); + printf("}\n"); +} +