GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 9 / 0 / 9
Functions: 100.0% 7 / 0 / 7
Branches: 100.0% 4 / 0 / 4

include/DetourModKit/region.hpp
Line Branch Exec Source
1 #ifndef DETOURMODKIT_REGION_HPP
2 #define DETOURMODKIT_REGION_HPP
3
4 /**
5 * @file region.hpp
6 * @brief The Region value type and the Prot protection flags, the shared range-of-memory vocabulary.
7 * @details A Region pairs a base Address with a byte size, so a memory range travels as one value. Each named
8 * factory yields a Region that the caller stores, passes to a scan, and narrows with `sub()`. Value
9 * operations (`end`, `contains`, `sub`) are pure arithmetic and allocate nothing. Every factory is
10 * Setup/control-plane only, and only `module_named()` can allocate among them.
11 * @warning `[B-100]` `host()`, `own()`, and `module_named()` query loader state: never call them under the Windows
12 * loader lock. `whole_process()` does not query loader state. `RegionLoaderBoundary.*` pins this boundary.
13 */
14
15 #include "DetourModKit/address.hpp"
16 #include "DetourModKit/defines.hpp"
17
18 #include <cstddef>
19 #include <cstdint>
20 #include <string_view>
21
22 namespace DetourModKit
23 {
24 /**
25 * @struct Region
26 * @brief A half-open span of process memory: [base, base + size).
27 * @details A plain data aggregate. It maintains no invariant beyond what its fields hold, so it stays a POD-like
28 * struct with public fields and is freely copied. An empty Region (null base, zero size) is the
29 * fail-closed result every factory returns when its scope cannot be resolved, and `contains()` reports
30 * false for any address against it.
31 */
32 struct Region
33 {
34 /// Inclusive start of the span.
35 Address base{};
36 /// Length of the span in bytes; a size of 0 denotes an empty Region.
37 std::size_t size{0};
38
39 /// Returns the exclusive end address (base advanced by size).
40 2974 [[nodiscard]] constexpr Address end() const noexcept { return base.offset(static_cast<std::ptrdiff_t>(size)); }
41
42 /**
43 * @brief Tests whether @p address lies within the half-open span.
44 * @param address The address to test.
45 * @return True when base <= address < end(); false for any address against an empty Region.
46 */
47 19 [[nodiscard]] constexpr bool contains(Address address) const noexcept
48 {
49
4/4
✓ Branch 4 → 5 taken 17 times.
✓ Branch 4 → 10 taken 2 times.
✓ Branch 8 → 9 taken 12 times.
✓ Branch 8 → 10 taken 5 times.
19 return address >= base && address < end();
50 }
51
52 /**
53 * @brief Returns a sub-span starting @p offset bytes into this Region and running for @p length bytes.
54 * @param offset Byte offset from base at which the sub-span starts.
55 * @param length Length of the sub-span in bytes.
56 * @details Pure value arithmetic with no clamping: the caller owns keeping the sub-span inside the parent,
57 * matching how it is used to carve a known-good window out of an already-validated Region.
58 */
59 1 [[nodiscard]] constexpr Region sub(std::size_t offset, std::size_t length) const noexcept
60 {
61 1 return Region{base.offset(static_cast<std::ptrdiff_t>(offset)), length};
62 }
63
64 /**
65 * @brief Returns the Region spanning the host process image (the .exe the mod is injected into).
66 * @return The host module's mapped image span, or an empty Region if it cannot be resolved.
67 * @details The default scope for a cascade that carries no explicit range.
68 * @note Setup/control-plane only: loader-backed, see the file-level `[B-100]` warning.
69 */
70 [[nodiscard]] static Region host() noexcept;
71
72 /**
73 * @brief Returns the Region spanning the module DetourModKit is linked into (the calling DLL, or the EXE when
74 * DMK is statically linked into the host process).
75 * @return The owning module's mapped image span, or an empty Region if the lookup or PE-header read failed.
76 * @details DetourModKit is a static library, so `own()` resolves to whichever DLL or EXE consumed it. That is
77 * distinct from @ref host(), which is always the process EXE. The lookup resolves the module that
78 * contains this function's own code, so it stays correct however the consumer packaged the library.
79 * @note Setup/control-plane only: loader-backed, see the file-level `[B-100]` warning.
80 */
81 [[nodiscard]] static Region own() noexcept;
82
83 /**
84 * @brief Returns the Region spanning a named, already-loaded module.
85 * @param name UTF-8 module name as the loader knows it (e.g. "kernel32.dll").
86 * @return The module's mapped image span, or an empty Region if @p name is empty or the module is not loaded.
87 * @note Setup/control-plane only: loader-backed, see the file-level `[B-100]` warning. The name conversion
88 * allocates.
89 */
90 [[nodiscard]] static Region module_named(std::string_view name) noexcept;
91
92 /**
93 * @brief Returns the Region spanning this process's entire user-mode address window.
94 * @return The half-open span from the system minimum application address through the maximum (inclusive).
95 * @details The widest scope, for a scan that cannot assume which module holds the target. It reads the
96 * system's reported application-address window, never a hardcoded ceiling.
97 * @note Setup/control-plane only; a whole-process scan is a startup-time operation, never a per-frame one.
98 */
99 [[nodiscard]] static Region whole_process() noexcept;
100 };
101
102 /**
103 * @enum Prot
104 * @brief Page protection as composable read/write/execute flags.
105 * @details A backend-neutral spelling of memory protection: the scan and memory layers speak in `Prot::RW` rather
106 * than the platform's `PAGE_READWRITE` constants, so the public surface never leaks an OS protection
107 * value. The RW / RWX combinations are predefined for the common cases; arbitrary unions compose through
108 * the flag operators generated below.
109 */
110 enum class Prot : std::uint32_t
111 {
112 None = 0,
113 R = 1,
114 W = 2,
115 X = 4,
116 // Inside an enum with a fixed underlying type, the enumerators have that integral type (std::uint32_t), not
117 // Prot, until the closing brace, so these `R | W` initializers are a plain integer OR evaluated at this point.
118 // They do not depend on (and predate) the DMK_FLAG_ENUM(Prot) operators below, which only apply to Prot values.
119 RW = R | W,
120 RWX = R | W | X
121 };
122
123 // Emit Prot's bitwise/compound operators in this namespace (unqualified enum), so `Prot::R | Prot::W` composes and
124 // ADL finds the operators. See DMK_FLAG_ENUM in defines.hpp for why placement and the missing semicolon matter.
125 3663 DMK_FLAG_ENUM(Prot)
126
127 } // namespace DetourModKit
128
129 #endif // DETOURMODKIT_REGION_HPP
130