1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
#include "substitute-internal.h"
#ifdef TARGET_SUPPORTED
#include "substitute.h"
#include "dis.h"
#include <stdbool.h>
#include <stdint.h>
#define P(x) transform_dis_##x
struct transform_dis_ctx {
/* outputs */
bool modify;
int err;
bool pc_low_bit;
uintptr_t pc_patch_start;
uintptr_t pc_patch_end;
uintptr_t pc;
int op_size;
unsigned op;
unsigned newop;
unsigned newval[4];
const void *ptr;
void **rewritten_ptr_ptr;
void *write_newop_here;
struct arch_dis_ctx arch;
};
#define tdis_ctx struct transform_dis_ctx *
#define TDIS_CTX_MODIFY(ctx) ((ctx)->modify)
#define TDIS_CTX_NEWVAL(ctx, n) ((ctx)->newval[n])
#define TDIS_CTX_NEWOP(ctx) ((ctx)->newop)
#define TDIS_CTX_SET_NEWOP(ctx, new) ((ctx)->newop = (new))
/* largely similar to jump_dis */
static INLINE UNUSED void transform_dis_ret(struct transform_dis_ctx *ctx) {
/* ret is okay if it's at the end of the patch */
if (ctx->pc + ctx->op_size < ctx->pc_patch_end)
ctx->err = SUBSTITUTE_ERR_FUNC_TOO_SHORT;
}
static INLINE UNUSED void transform_dis_branch(struct transform_dis_ctx *ctx,
uintptr_t dpc, UNUSED bool conditional) {
#ifdef TRANSFORM_DIS_VERBOSE
printf("transform_dis (%p): branch => %p\n", (void *) ctx->pc, (void *) dpc);
#endif
if (dpc >= ctx->pc_patch_start && dpc < ctx->pc_patch_end) {
/* don't support this for now */
ctx->err = SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START;
}
/* branch out of bounds is fine */
}
static INLINE UNUSED void transform_dis_unidentified(UNUSED struct transform_dis_ctx *ctx) {
#ifdef TRANSFORM_DIS_VERBOSE
printf("transform_dis (%p): unidentified\n", (void *) ctx->pc);
#endif
/* this isn't exhaustive, so unidentified is fine */
}
static INLINE UNUSED void transform_dis_bad(struct transform_dis_ctx *ctx) {
ctx->err = SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START;
}
static void transform_dis_dis(struct transform_dis_ctx *ctx);
int transform_dis_main(const void *restrict code_ptr,
void **restrict rewritten_ptr_ptr,
uintptr_t pc_patch_start,
uintptr_t pc_patch_end,
bool pc_low_bit,
int *offset_by_pcdiff) {
struct transform_dis_ctx ctx;
ctx.pc_patch_start = pc_patch_start;
ctx.pc_patch_end = pc_patch_end;
ctx.pc_low_bit = pc_low_bit;
ctx.pc = pc_patch_start;
/* data is written to rewritten both by this function directly and, in case
* additional scaffolding is needed, by arch-specific transform_dis_* */
ctx.rewritten_ptr_ptr = rewritten_ptr_ptr;
void *rewritten_start = *rewritten_ptr_ptr;
int written_pcdiff = 0;
offset_by_pcdiff[written_pcdiff++] = 0;
while (ctx.pc < ctx.pc_patch_end) {
ctx.modify = false;
ctx.err = 0;
ctx.ptr = code_ptr + (ctx.pc - pc_patch_start);
void *rewritten_ptr = *rewritten_ptr_ptr;
ctx.write_newop_here = rewritten_ptr;
transform_dis_dis(&ctx);
if (ctx.err)
return ctx.err;
if (ctx.write_newop_here != NULL) {
if (!ctx.modify)
ctx.newop = ctx.op;
if (ctx.op_size == 4)
*(uint32_t *) ctx.write_newop_here = ctx.newop;
else if (ctx.op_size == 2)
*(uint16_t *) ctx.write_newop_here = ctx.newop;
else
__builtin_abort();
if (*rewritten_ptr_ptr == rewritten_ptr)
*rewritten_ptr_ptr += ctx.op_size;
}
ctx.pc += ctx.op_size;
int pcdiff = ctx.pc - ctx.pc_patch_start;
while (written_pcdiff < pcdiff)
offset_by_pcdiff[written_pcdiff++] = -1;
offset_by_pcdiff[written_pcdiff++] = (int) (*rewritten_ptr_ptr - rewritten_start);
}
return SUBSTITUTE_OK;
}
static inline void op32(struct transform_dis_ctx *ctx, uint32_t op) {
void **rpp = ctx->rewritten_ptr_ptr;
*(uint32_t *) *rpp = op;
*rpp += 4;
}
#ifdef TARGET_arm
#include "transform-dis-arm-multi.inc.h"
#else
#include "transform-dis-arm64.inc.h"
#endif
#include TARGET_DIS_HEADER
#endif /* TARGET_SUPPORTED */
|