Fix documentation

This commit is contained in:
alejo committed 2026-09-25 03:20:35 -03:00
1 parent a317c07124
commit ab21b3f30d
27 files changed
+2137 -1043

No files matched your search

+60 -24
View File
@@ -1,3 +1,7 @@
/**
* @file Architecture.cpp
* @brief Per-architecture jump-patch backends for x86, x86-64, and ARM64.
*/
#include "Architecture.hpp"
#include <atomic>
@@ -51,7 +55,8 @@
* executing through the exact bytes being patched -- doing that in
* general on a variable-length ISA needs a signal-handler-based retry
* protocol (a much bigger mechanism, well beyond a dev-loop tool's
* requirements). See the README for this precondition stated plainly.
* requirements). See the "Limitations" section of the top-level README
* for this precondition stated plainly.
*/
namespace hot_reload::detail
@@ -60,20 +65,30 @@ namespace hot_reload::detail
{
#if defined(__x86_64__) || defined(_M_X64)
/// Size in bytes of the CET landing pad (`endbr64`) a function may start with.
constexpr std::size_t kLandingPadBytes = 4;
/// Encoding of `endbr64`, the x86-64 CET indirect-branch landing pad.
constexpr unsigned char kEndbr64[kLandingPadBytes] = {0xF3, 0x0F, 0x1E, 0xFA};
// jmp qword ptr [rip+0]; <8-byte absolute address> -- reaches
// any 64-bit address regardless of how far apart the old and
// new modules were loaded, unlike a 5-byte relative jump
// (E9 rel32) which only reaches +/-2GB. Verified byte-for-byte
// against `as`/objdump output while building this library.
/// Size in bytes of the jump written over a function's entry:
/// `jmp qword ptr [rip+0]` followed by an 8-byte absolute address. That reaches any
/// 64-bit address regardless of how far apart the old and new modules were loaded,
/// unlike a 5-byte relative jump (`E9 rel32`) which only reaches +/-2GB. Verified
/// byte-for-byte against `as`/objdump output while building this library.
constexpr std::size_t kJumpBytes = 14;
/// Whether the function at @p entry starts with an `endbr64` landing pad.
/// @param entry The function's entry address (at least kLandingPadBytes readable bytes).
/// @return True if the first bytes are `endbr64`.
bool hasLandingPad(const unsigned char* entry)
{
return std::memcmp(entry, kEndbr64, kLandingPadBytes) == 0;
}
/// Writes the x86-64 absolute jump at @p site, payload first and the opcode last.
/// @param site Where the jump goes (just past the landing pad, if there is one).
/// @param target Address the jump transfers control to.
void writeJumpAt(unsigned char* site, const void* target)
{
std::uint64_t targetValue = 0;
@@ -99,20 +114,29 @@ namespace hot_reload::detail
#elif defined(__i386__) || defined(_M_IX86)
/// Size in bytes of the CET landing pad (`endbr32`) a function may start with.
constexpr std::size_t kLandingPadBytes = 4;
/// Encoding of `endbr32`, the 32-bit x86 CET indirect-branch landing pad.
constexpr unsigned char kEndbr32[kLandingPadBytes] = {0xF3, 0x0F, 0x1E, 0xFB};
// e9 rel32 -- a plain relative near jump. On 32-bit x86 the
// entire address space fits in the signed 32-bit displacement
// (mod 2^32 wraparound gives the right answer regardless of
// "distance"), so unlike x86-64 there is no need for an
// absolute/indirect form here.
/// Size in bytes of the jump written over a function's entry: `e9 rel32`, a plain
/// relative near jump. On 32-bit x86 the entire address space fits in the signed 32-bit
/// displacement (mod 2^32 wraparound gives the right answer regardless of "distance"),
/// so unlike x86-64 there is no need for an absolute/indirect form here.
constexpr std::size_t kJumpBytes = 5;
/// Whether the function at @p entry starts with an `endbr32` landing pad.
/// @param entry The function's entry address (at least kLandingPadBytes readable bytes).
/// @return True if the first bytes are `endbr32`.
bool hasLandingPad(const unsigned char* entry)
{
return std::memcmp(entry, kEndbr32, kLandingPadBytes) == 0;
}
/// Writes the x86 relative jump at @p site, displacement first and the opcode last.
/// @param site Where the jump goes (just past the landing pad, if there is one).
/// @param target Address the jump transfers control to.
void writeJumpAt(unsigned char* site, const void* target)
{
std::int32_t rel32 = static_cast<std::int32_t>(
@@ -128,35 +152,47 @@ namespace hot_reload::detail
#elif defined(__aarch64__) || defined(_M_ARM64)
/// Size in bytes of the BTI landing pad a function may start with (one instruction word).
constexpr std::size_t kLandingPadBytes = 4;
// Matches any BTI variant (plain/C/J/JC): bits [31:6] are fixed
// at 0b010101000000001001001, differing only in bits [6:5]
// (the qualifier) -- equivalently, the low byte is one of
// 0x1F/0x5F/0x9F/0xDF and the next 3 bytes are always
// 24 03 D5. Verified with `as`/objdump for all 4 variants
// while building this library.
/// Whether the instruction word at @p entry is any BTI variant (plain/C/J/JC): bits
/// [31:6] are fixed at 0b010101000000001001001, differing only in bits [6:5] (the
/// qualifier) -- equivalently, the low byte is one of 0x1F/0x5F/0x9F/0xDF and the next 3
/// bytes are always 24 03 D5. Verified with `as`/objdump for all 4 variants while
/// building this library.
/// @param entry The function's entry address (at least 4 readable bytes).
/// @return True if the first instruction is a BTI landing pad.
bool isBti(const unsigned char* entry)
{
return (entry[0] & 0x3Fu) == 0x1Fu && entry[1] == 0x24 && entry[2] == 0x03 && entry[3] == 0xD5;
}
/// Whether the function at @p entry starts with a BTI landing pad.
/// @param entry The function's entry address (at least kLandingPadBytes readable bytes).
/// @return True if the first instruction is a BTI landing pad.
bool hasLandingPad(const unsigned char* entry)
{
return isBti(entry);
}
// ldr x17, #8 ; br x17 ; <8-byte absolute address>. x16/x17
// are AArch64's designated "intra-procedure-call" scratch
// registers (IP0/IP1) -- the AAPCS64 explicitly reserves them
// for exactly this kind of linker/veneer-style long branch, so
// clobbering them at a function's entry point, before any of
// its real prologue has executed, is always safe. Byte values
// verified with `as`/objdump while building this library.
/// Size in bytes of the jump written over a function's entry:
/// `ldr x17, #8 ; br x17 ; <8-byte absolute address>`. x16/x17 are AArch64's designated
/// "intra-procedure-call" scratch registers (IP0/IP1) -- the AAPCS64 explicitly reserves
/// them for exactly this kind of linker/veneer-style long branch, so clobbering them at
/// a function's entry point, before any of its real prologue has executed, is always
/// safe. Byte values verified with `as`/objdump while building this library.
constexpr std::size_t kJumpBytes = 16;
/// Encoding of `ldr x17, #8` (loads the literal address stored 8 bytes further on).
constexpr unsigned char kLdrX17Pc8[4] = {0x51, 0x00, 0x00, 0x58};
/// Encoding of `br x17` (branches to the address `ldr x17, #8` loaded).
constexpr unsigned char kBrX17[4] = {0x20, 0x02, 0x1F, 0xD6};
/// Writes the ARM64 `ldr`/`br` pair plus literal address at @p site, payload first and
/// the leading `ldr` last.
/// @param site Where the jump goes (just past the landing pad, if there is one).
/// @param target Address the jump transfers control to.
void writeJumpAt(unsigned char* site, const void* target)
{
std::uint64_t targetValue = 0;