/** * @file PluginApi.h * @brief The contract PluginV1 and PluginV2 both implement, * and the Host loads by name. Every function here follows * cpp-style-guide.md section 11's rule for anything loaded at runtime: * plain C linkage, no classes or exceptions crossing the boundary. * * This header is deliberately identical for both plugin versions -- * in a real project this is the file that stays stable while * PluginV1/src/Plugin.cpp and PluginV2/src/Plugin.cpp (standing in for * "the same file, before and after your edit") diverge. */ #pragma once #ifdef __cplusplus extern "C" { #endif /** * @brief Scales its input, and prints which build it is. * * Changes behavior between V1 and V2 -- watch this one get * HOT_RELOAD_SYMBOL_PATCHED. * * @param input The value to scale. * @return V1: `input * 2`. V2: `input * 3`. */ int plugin_tick(int input); /** * @brief A feature that exists only in V1. * * Gone in V2 -- watch this one get HOT_RELOAD_SYMBOL_REMOVED, and the * Host's stub take over for any call still aimed at it afterwards. * * @param input The value to echo. * @return @p input, unchanged. */ int plugin_legacy_feature(int input); /** * @brief A feature that exists only in V2. * * Watch this one get HOT_RELOAD_SYMBOL_ADDED, then get called for the * first time right after. * * @param input The value to scale. * @return `input * 10`. */ int plugin_new_feature(int input); #if defined(__GNUC__) || defined(__clang__) /** * @brief An empty function, too small to hold the jump this library writes. * * Its patchable-function-entry padding is overridden down to zero, so * all that is left is the compiled empty body -- a handful of bytes, * fewer than the jump any of this library's backends writes on x86-64 * and ARM64 at any optimization level. Watch this one get * HOT_RELOAD_SYMBOL_SKIPPED_TOO_SMALL rather than corrupt whatever comes * after it. * * Two configurations cannot promise "too small": unoptimized 32-bit x86 * (its jump is only 5 bytes, which even an empty unoptimized function * reaches), and Windows, where a PE image carries no per-function size * and the library can only estimate one from the distance to the next * export. MSVC has no equivalent per-function override this demo could * apply, so this one only exists on GCC/Clang. * * ARM32 needs a different implementation underneath this same * declaration (see PluginV1/src/Plugin.cpp): an ordinary empty C++ * body there, even with patchable-function-entry overridden to zero, * still picks up an unoptimized build's full frame-pointer prologue * and epilogue -- confirmed directly, 8 bytes on this architecture at * `-O0`, tying this library's own ARM-state jump size exactly rather * than falling safely under it. A single hand-written `bx lr`, at file * scope rather than inside a C++ function body (sidestepping a * separate GCC quirk: an asm statement inside a naked function body * is followed by an unconditional extra NOP, confirmed directly), * reliably stays under every ARM32 jump size this library ever * writes, in both instruction sets, at every optimization level. */ __attribute__((patchable_function_entry(0))) void plugin_tiny_function(void); #endif /** * @brief The build's version number: 1 in V1, 2 in V2. * * An exported VARIABLE, not a function, sitting right next to * real functions in the same library and changing value between * V1 and V2 just like plugin_tick's behavior does -- watch this * one get HOT_RELOAD_SYMBOL_SKIPPED_NOT_CODE. This is the * mechanical proof behind hot_reload.h's "only executable code * changes" guarantee: nothing here tells the library "this one is * data, don't touch it" -- it discovers that itself from the * symbol table, the same way it discovers plugin_tick is code. */ extern int plugin_build_marker; #ifdef __cplusplus } #endif