83 lines
4.5 KiB
CMake
83 lines
4.5 KiB
CMake
# HotReload.cmake
|
|
#
|
|
# One function: hot_reload_enable_patchable_functions(<target>).
|
|
# Call it on any SHARED library target whose exported functions you
|
|
# intend to hot-reload with source/HotReload. It applies whichever
|
|
# compiler flag guarantees enough safely-overwritable space at each
|
|
# function's entry point for this library's jump-patch backends
|
|
# (source/HotReload/src/Architecture.cpp) to write into -- without it,
|
|
# HotReloadContext::reload() falls back to the compiled function's
|
|
# symbol size (ELF only -- see SymbolTable.cpp) and silently skips
|
|
# (HOT_RELOAD_SYMBOL_SKIPPED_TOO_SMALL) anything too small, rather than
|
|
# corrupting whatever comes after it in memory, but a target built
|
|
# with this flag applied should never actually hit that fallback.
|
|
#
|
|
# 32 bytes covers every backend with margin: ARM64's worst case is a
|
|
# 4-byte BTI landing pad (only present if the target enables ARM
|
|
# branch protection) plus a 16-byte jump sequence = 20 bytes; x86-64's
|
|
# is a 4-byte ENDBR64 landing pad (on by default on some toolchains --
|
|
# see Architecture.cpp's file comment) plus its 14-byte jump = 18;
|
|
# plain x86 needs at most 4 + 5 = 9. 32 leaves headroom over the
|
|
# largest of those without meaningfully affecting code size -- this is
|
|
# NOP padding, skipped in a few cycles by the branch predictor on
|
|
# every call until a function is actually reloaded.
|
|
function(hot_reload_enable_patchable_functions TARGET_NAME)
|
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
# M (the "before entry" count) is deliberately 0: all 32 NOPs
|
|
# land AT and after the function's address, which is exactly
|
|
# where source/HotReload writes into. Confirmed against
|
|
# `objdump` output while building this library -- see the
|
|
# design note this function's file comment links back to.
|
|
target_compile_options(${TARGET_NAME} PRIVATE -fpatchable-function-entry=32,0)
|
|
elseif(MSVC)
|
|
# MSVC's own equivalent: reserves a 2-byte pad at every
|
|
# function's entry plus additional padding immediately before
|
|
# it, specifically so an external tool can overwrite the entry
|
|
# with a short jump. See
|
|
# https://learn.microsoft.com/en-us/cpp/build/reference/hotpatch
|
|
# This does not take a byte count the way -fpatchable-function-
|
|
# entry does; the PE export table also gives this library no
|
|
# per-symbol size the way ELF's symbol table does (see
|
|
# SymbolTable.cpp's file comment), so on MSVC this flag is not
|
|
# just recommended but the primary safety mechanism -- there is
|
|
# no runtime fallback check backing it up here the way there is
|
|
# on GCC/Clang/ELF.
|
|
target_compile_options(${TARGET_NAME} PRIVATE /hotpatch)
|
|
else()
|
|
message(WARNING
|
|
"hot_reload_enable_patchable_functions(${TARGET_NAME}): "
|
|
"unrecognized compiler '${CMAKE_CXX_COMPILER_ID}', no patchable-entry "
|
|
"flag applied. Hot-reloading ${TARGET_NAME} will still work for any "
|
|
"function whose compiled body happens to be large enough on its own; "
|
|
"see HOT_RELOAD_SYMBOL_SKIPPED_TOO_SMALL in hot_reload.h.")
|
|
endif()
|
|
endfunction()
|
|
|
|
# Restricts TARGET's export surface to only what's explicitly marked
|
|
# with HOT_RELOAD_EXPORT (hot_reload.h) -- the opposite of both
|
|
# GCC/Clang's default (export every symbol with external linkage) and
|
|
# WINDOWS_EXPORT_ALL_SYMBOLS. Narrower, not quieter: see the top-level
|
|
# README's "Why do std::string/std::vector symbols show up as
|
|
# PATCHED?" section for the one specific kind of noise this
|
|
# deliberately does NOT remove, and why removing it would need
|
|
# excluding all weak-linkage symbols by default -- which would just as
|
|
# happily hide a hot-reloadable template of your OWN.
|
|
#
|
|
# What this DOES remove: your own internal helpers you never meant to
|
|
# expose, and (on GCC/Clang, if the target statically links its C++
|
|
# runtime) incidental runtime-library internals -- both real,
|
|
# observed cases from building this library and its examples.
|
|
function(hot_reload_restrict_exports TARGET_NAME)
|
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
target_compile_options(${TARGET_NAME} PRIVATE -fvisibility=hidden -fvisibility-inlines-hidden)
|
|
elseif(MSVC)
|
|
# MSVC already exports nothing by default -- WINDOWS_EXPORT_ALL_SYMBOLS
|
|
# is what opts OUT of that; simply not setting it (and not adding
|
|
# individual __declspec(dllexport)/HOT_RELOAD_EXPORT other than where
|
|
# you want them) is this function's Windows equivalent. Nothing to
|
|
# apply here, but calling this on every platform your target builds
|
|
# for, unconditionally, is still the point -- see hot_reload_enable_
|
|
# patchable_functions() immediately above for the same pattern.
|
|
endif()
|
|
endfunction()
|