16#include "../microfmt.hpp"
17#include "../reloco.hpp"
64 expected<memory_scan_result, address_space_error> (*scan)(
const void *ctx, uintptr_t start_addr,
65 size_t search_length, span<const std::byte> pattern,
66 span<const std::byte> mask)
noexcept =
nullptr;
76 std::enable_if_t<std::is_void_v<typename Traits::context_type>,
int> = 0>
83 template <
typename Tag,
typename Context,
typename Traits = memory_pattern_scanner_traits<Tag>,
84 std::enable_if_t<!std::is_
void_v<
typename Traits::context_type> &&
85 std::is_convertible_v<const Context *, const
typename Traits::context_type *>,
88 const Context &ctx RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
89 : ctx_(&ctx), vtbl_(&s_vtbl<Tag>) {}
91 template <
typename Tag,
typename Context, std::enable_if_t<!std::is_lvalue_reference_v<Context>,
int> = 0>
94 template <
typename Tag,
typename Traits = memory_pattern_scanner_traits<Tag>,
95 std::enable_if_t<std::is_
void_v<
typename Traits::context_type>,
int> = 0>
100 template <
typename Tag,
typename Context,
typename Traits = memory_pattern_scanner_traits<Tag>,
101 std::enable_if_t<!std::is_
void_v<
typename Traits::context_type>,
int> = 0>
106 template <
typename Tag,
typename Context, std::enable_if_t<!std::is_lvalue_reference_v<Context>,
int> = 0>
119 [[nodiscard]] expected<memory_scan_result, address_space_error>
scan(uintptr_t start_addr,
size_t search_length,
120 span<const std::byte> pattern,
121 span<const std::byte> mask = {})
const noexcept {
122 if (vtbl_ ==
nullptr)
123 return unexpected(address_space_error::invalid_handle);
124 return vtbl_->scan(ctx_.get(), start_addr, search_length, pattern, mask);
128 [[nodiscard]]
constexpr explicit operator bool() const noexcept {
return vtbl_ !=
nullptr; }
131 template <
typename Tag>
132 static expected<memory_scan_result, address_space_error>
133 scan_entry(
const void *context, uintptr_t start_addr,
size_t search_length, span<const std::byte> pattern,
134 span<const std::byte> mask)
noexcept {
136 if constexpr (std::is_void_v<context_type>) {
139 const auto &typed_context = *
static_cast<const context_type *
>(context);
140 return memory_pattern_scanner_traits<Tag>::scan(value_ref<const context_type>(typed_context), start_addr,
141 search_length, pattern, mask);
145 template <
typename Tag>
static constexpr vtable s_vtbl{&scan_entry<Tag>};
147 value_ptr<const void> ctx_{};
155template <typename Tag, bool Stateless = std::is_void_v<typename memory_pattern_scanner_traits<Tag>::context_type>>
170 [[nodiscard]]
constexpr value_ref<context_type>
context() &
noexcept RELOCO_LIFETIMEBOUND {
171 return value_ref<context_type>(context_);
174 [[nodiscard]]
constexpr value_ref<const context_type>
context() const & noexcept RELOCO_LIFETIMEBOUND {
175 return value_ref<const context_type>(context_);
183 value_ref<context_type>
context() && =
delete;
184 value_ref<const context_type>
context() const && = delete;
216 RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
217 : space(space_ref), scratch(scratch_buf) {}
226 static expected<memory_scan_result, address_space_error>
scan(value_ref<const context_type> ctx, uintptr_t start_addr,
227 size_t search_length, span<const std::byte> pattern,
228 span<const std::byte> mask)
noexcept {
229 if (pattern.empty()) {
232 if (ctx->scratch.size() < pattern.size()) {
233 return unexpected(address_space_error::invalid_buffer);
235 if (!mask.empty() && mask.size() != pattern.size()) {
236 return unexpected(address_space_error::invalid_buffer);
239 return unexpected(address_space_error::invalid_handle);
242 uintptr_t current_addr = start_addr;
243 size_t remaining_search = search_length;
246 while (remaining_search >= pattern.size()) {
247 const size_t to_read = (remaining_search < ctx->scratch.size()) ? remaining_search : ctx->scratch.size();
249 auto read_res = ctx->space.read_bytes(current_addr, ctx->scratch.data(), to_read);
252 return unexpected(read_res.error());
255 const size_t search_limit = to_read - pattern.size();
256 for (
size_t i = 0; i <= search_limit; ++i) {
257 if (check_match(ctx->scratch.subspan(i), pattern, mask)) {
258 if (current_addr > std::numeric_limits<uintptr_t>::max() - i) {
259 return unexpected(address_space_error::invalid_address);
266 const size_t advance = to_read - pattern.size() + 1;
267 if (current_addr > std::numeric_limits<uintptr_t>::max() - advance) {
268 return unexpected(address_space_error::invalid_address);
270 current_addr += advance;
271 remaining_search -= advance;
278 [[nodiscard]]
static bool check_match(span<const std::byte> mem_window, span<const std::byte> pattern,
279 span<const std::byte> mask)
noexcept {
280 for (
size_t i = 0; i < pattern.size(); ++i) {
282 if ((mem_window[i] & mask[i]) != (pattern[i] & mask[i])) {
286 if (mem_window[i] != pattern[i]) {
Type-erased remote address-space access and remote string/object views.
Type-erased, two-word handle to a remote address space.
Definition address_space.hpp:204
typename traits_type::context_type context_type
Definition memory_pattern_scanner.hpp:166
constexpr value_ref< const context_type > context() const &noexcept RELOCO_LIFETIMEBOUND
Definition memory_pattern_scanner.hpp:174
value_ref< const context_type > context() const &&=delete
constexpr value_ref< context_type > context() &noexcept RELOCO_LIFETIMEBOUND
Definition memory_pattern_scanner.hpp:170
value_ref< context_type > context() &&=delete
constexpr memory_pattern_scanner_ref ref() const &noexcept RELOCO_LIFETIMEBOUND
Creates a type-erased reference borrowing this scanner's context.
Definition memory_pattern_scanner.hpp:179
constexpr memory_pattern_scanner(context_type context) noexcept
Definition memory_pattern_scanner.hpp:168
static constexpr memory_pattern_scanner_ref ref() noexcept
Definition memory_pattern_scanner.hpp:198
Type-erased, zero-allocation handle to a memory pattern scanner.
Definition memory_pattern_scanner.hpp:61
static memory_pattern_scanner_ref make(Context &&)=delete
constexpr memory_pattern_scanner_ref() noexcept=default
Constructs an empty (invalid) handle.
static constexpr memory_pattern_scanner_ref make(const Context &ctx RELOCO_LIFETIMEBOUND) noexcept
Definition memory_pattern_scanner.hpp:102
static constexpr memory_pattern_scanner_ref make() noexcept
Definition memory_pattern_scanner.hpp:96
constexpr memory_pattern_scanner_ref(Tag, Context &&)=delete
expected< memory_scan_result, address_space_error > scan(uintptr_t start_addr, size_t search_length, span< const std::byte > pattern, span< const std::byte > mask={}) const noexcept
Executes a type-erased pattern scan across remote memory.
Definition memory_pattern_scanner.hpp:119
constexpr memory_pattern_scanner_ref(Tag, const Context &ctx RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
Constructs a handle for a stateful scanner tag.
Definition memory_pattern_scanner.hpp:87
Definition memory_pattern_scanner.hpp:156
#define MICROFMT_API_CLASS
Definition compat.hpp:181
State retained by the standard linear memory scanner.
Definition memory_pattern_scanner.hpp:209
span< std::byte > scratch
Caller-provided chunk buffer used for the sliding window.
Definition memory_pattern_scanner.hpp:213
constexpr linear_memory_scanner_context(address_space_ref space_ref, span< std::byte > scratch_buf RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
Definition memory_pattern_scanner.hpp:215
address_space_ref space
Target memory address space to scan.
Definition memory_pattern_scanner.hpp:211
Tag identifying the standard linear, sliding-window memory scanner.
Definition memory_pattern_scanner.hpp:206
Definition memory_pattern_scanner.hpp:63
static expected< memory_scan_result, address_space_error > scan(value_ref< const context_type > ctx, uintptr_t start_addr, size_t search_length, span< const std::byte > pattern, span< const std::byte > mask) noexcept
Definition memory_pattern_scanner.hpp:226
Static customization point for memory pattern scanners.
Definition memory_pattern_scanner.hpp:49
Result of a memory pattern scan operation.
Definition memory_pattern_scanner.hpp:30