55 lines
3.0 KiB
CMake
55 lines
3.0 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()
|