aboutsummaryrefslogtreecommitdiff
path: root/lib/dis.h
blob: 7455749ec96075c1e7ab9e29067c41a782b6d61e (plain) (blame)
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#pragma once

#include "substitute-internal.h"

#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

#define INLINE __attribute__((always_inline))
#define NOINLINE __attribute__((noinline))

struct bitslice_run {
    int inpos, outpos, len;
};

struct bitslice {
    int nruns;
    const struct bitslice_run *runs;
};

static inline int sext(unsigned val, int bits) {
    return val & (1 << (bits - 1)) ? ((int)val - (1 << bits)) : (int)val;
}

static inline unsigned bs_get(struct bitslice bs, unsigned op) {
    unsigned ret = 0;
    for(int i = 0; i < bs.nruns; i++) {
        const struct bitslice_run *run = &bs.runs[i];
        unsigned val = (op >> run->inpos) & ((1 << run->len) - 1);
        ret |= val << run->outpos;
    }
    return ret;
}

static inline unsigned bs_set(struct bitslice bs, unsigned new, unsigned op) {
    for(int i = 0; i < bs.nruns; i++) {
        const struct bitslice_run *run = &bs.runs[i];
        unsigned mask = (1 << run->len) - 1;
        unsigned val = (new >> run->outpos) & mask;
        op = (op & ~(mask << run->inpos)) | (val << run->inpos);
    }
    return op;
}

static inline struct bitslice bs_slice_(struct bitslice bs, struct bitslice_run *runs, int lo, int size) {
    int nruns = 0;
    for(int i = 0; i < bs.nruns; i++) {
        struct bitslice_run inr = bs.runs[i];
        inr.outpos -= lo;
        if(inr.outpos < 0) {
            inr.len += inr.outpos;
            inr.inpos -= inr.outpos;
            inr.outpos = 0;
        }
        if(inr.outpos + inr.len > size)
            inr.len = size - inr.outpos;
        if(inr.len > 0)
            runs[nruns++] = (struct bitslice_run) {inr.inpos, inr.outpos, inr.len};
    }
    return (struct bitslice) {nruns, runs};
}
#define bs_slice(bs, lo, size) \
    bs_slice_(bs, alloca((bs).nruns * sizeof(struct bitslice_run)), lo, size)

enum pcrel_load_mode {
    PLM_ADR, /* just want the address */
    PLM_U8,  PLM_S8,
    PLM_U16, PLM_S16,
    PLM_U32, PLM_S32,
    PLM_U64,
    PLM_U128, /* i.e. LDRD */
    PLM_U32_SIMD,
    PLM_U64_SIMD,
    PLM_U128_SIMD,
};

static const struct bitslice null_bs = { 0, NULL };
static const unsigned null_op = -0x100;
#define r(nn)           nn,                 false, true
#define rs(nn, l, s)    bs_slice(nn, l, s), false, true
#define rout(nn)        nn,                 true,  true
#define rsout(nn, l, s) bs_slice(nn, l, s), true,  true
#define rnull           null_bs,             false, false

#define data(...) data_flags(0, __VA_ARGS__)
#define data_flags(...) data_(__VA_ARGS__, rnull, rnull, rnull, rnull)
#define data_(...) data__(__VA_ARGS__)
#define data__(fl, b1, o1, v1, b2, o2, v2, b3, o3, v3, b4, o4, v4, ...) do { \
    P(data)(ctx, \
        v1 ? bs_get(b1, ctx->op) : null_op, \
        v2 ? bs_get(b2, ctx->op) : null_op, \
        v3 ? bs_get(b3, ctx->op) : null_op, \
        v4 ? bs_get(b4, ctx->op) : null_op, \
        (o1 << 0) | \
        (o2 << 1) | \
        (o3 << 2) | \
        (o4 << 3) | \
        fl); \
    if (TDIS_CTX_MODIFY(ctx)) { \
        unsigned new = ctx->op; \
        new = bs_set(b1, TDIS_CTX_NEWVAL(ctx, 0), new); \
        new = bs_set(b2, TDIS_CTX_NEWVAL(ctx, 1), new); \
        new = bs_set(b3, TDIS_CTX_NEWVAL(ctx, 2), new); \
        new = bs_set(b4, TDIS_CTX_NEWVAL(ctx, 3), new); \
        TDIS_CTX_SET_NEWOP(ctx, new); \
    } \
    return; \
} while (0)

#ifndef TARGET_DIS_SUPPORTED
    #error "no disassembler for the target architecture yet"
#endif

static inline void op64(void **codep, uint64_t op) {
    *(uint64_t *) *codep = op;
    *codep += 8;
}

static inline void op32(void **codep, uint32_t op) {
    *(uint32_t *) *codep = op;
    *codep += 4;
}

static inline void op16(void **codep, uint16_t op) {
    *(uint16_t *) *codep = op;
    *codep += 2;
}

static inline void op8(void **codep, uint8_t op) {
    *(uint8_t *) *codep = op;
    (*codep)++;
}

#define CC_CONDITIONAL 0x100
#define CC_CALL        0x200

struct dis_ctx_base {
    uint_tptr pc;
    const void *ptr;
#if defined(TARGET_x86_64) || defined(TARGET_i386)
    uint8_t newop[32];
#else
    uint8_t newop[4];
    uint32_t op;
#endif
    uint32_t newval[4];
    bool modify;
    int op_size, newop_size;
};

#include stringify(TARGET_DIR/arch-dis.h)