@@ -35,9 +35,31 @@ namespace hat {
3535 return static_cast <size_t >(read<Int>(offset)) / sizeof (ArrayType);
3636 }
3737
38- // / Resolve the relative address located at an offset from the signature result
39- [[nodiscard]] constexpr T rel (size_t offset) const {
40- return this ->has_result () ? this ->result + this ->read <rel_t >(offset) + offset + sizeof (rel_t ) : nullptr ;
38+ // / Resolve the relative address located at an offset from the signature result. The behavior is undefined if
39+ // / there is no result. The "offset" parameter is the number of bytes after the result's match that the relative
40+ // / address is located. For example:
41+ // /
42+ // / | result matches here
43+ // / | | relative address located at +3 (offset)
44+ // / v v
45+ // / 0x0: 48 8D 05 BE 53 23 01 lea rax, [rip+0x12353be]
46+ // / 0x7: <next instruction>
47+ // /
48+ // / The "remaining" parameter is the number of bytes after the relative address that the next instruction
49+ // / begins. In the majority of cases, this parameter can be left as 0. However, consider the following example:
50+ // /
51+ // / | result matches here
52+ // / | | relative address located at +2 (offset)
53+ // / | | | end of relative address
54+ // / v v v
55+ // / 0x0: 83 3D BE 53 23 01 00 cmp DWORD PTR [rip+0x12353be],0x0
56+ // / 0x7: <next instruction>
57+ // /
58+ // / The "0x0" operand comes after the relative address. The absolute address referred to by the RIP relative
59+ // / address in this case is 0x12353BE + 0x7 = 0x12353C5. Simply using rel(2) would yield an incorrect result of
60+ // / 0x12353C4. In this case, rel(2, 1) would yield the expected 0x12353C5.
61+ [[nodiscard]] constexpr T rel (size_t offset, size_t remaining = 0 ) const {
62+ return this ->result + this ->read <rel_t >(offset) + offset + sizeof (rel_t ) + remaining;
4163 }
4264
4365 [[nodiscard]] constexpr bool has_result () const {
0 commit comments