/
optimization.c
300 lines (272 loc) · 9.43 KB
/
optimization.c
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
/*
* (C) 2010 by Computer System Laboratory, IIS, Academia Sinica, Taiwan.
* See COPYRIGHT in top-level directory.
*/
#include <stdlib.h>
#include "exec-all.h"
#include "tcg-op.h"
#include "helper.h"
#define GEN_HELPER 1
#include "helper.h"
#include "optimization.h"
#define SHACK_INDEX_BITS 16
extern uint8_t *optimization_ret_addr;
/*
* Shadow Stack
*/
list_t *shadow_hash_list;
static inline void init_shack_hash(CPUState *env){
int i;
env->shadow_hash_list = malloc( (2 << SHACK_INDEX_BITS) * sizeof(struct shadow_pair*) );
for(i=0;i< (2 << SHACK_INDEX_BITS);i++){
struct shadow_pair* head = malloc(sizeof(struct shadow_pair));
head->guest_eip = 0;
head->l.next = NULL;
head->l.prev = NULL;
((struct shadow_pair**)env->shadow_hash_list)[i] = head;
}
}
static inline void shack_init(CPUState *env)
{
int i;
env->shack = (uint64_t*)malloc(SHACK_SIZE * sizeof(uint64_t));
env->shack_top = env->shack;
env->shack_end = env->shack + SHACK_SIZE;
env->shadow_ret_count = 0;
env->shadow_ret_addr = (unsigned long*)malloc(SHACK_SIZE * sizeof(unsigned long));
for(i = 0; i< SHACK_SIZE; ++i){ // i : store the position for host addr
env->shack[i] = (uint64_t)(unsigned long)(env->shadow_ret_addr + i); // the lower 32-bit will be the address of host slot
env->shadow_ret_addr[i] = 0;
}
// hash table
init_shack_hash(env);
}
struct shadow_pair** get_shadow_pair_head_from_hash(CPUState *env, target_ulong guest_eip){
int index = guest_eip >> SHACK_INDEX_BITS | ((guest_eip << SHACK_INDEX_BITS) >> SHACK_INDEX_BITS);
return (((struct shadow_pair **)env->shadow_hash_list) + index);
}
unsigned long lookup_shadow_ret_addr(CPUState *env, target_ulong pc){
static int count = 0;
struct shadow_pair *sp = *get_shadow_pair_head_from_hash(env, pc);
// search the head
int current_count = 0;
while( sp->l.next){ // has next (not the ending one)
++count;
++current_count;
if(sp->guest_eip == pc){
//fprintf(stderr,"lookup count: %d, total count: %d\n", current_count ,count);
return *sp->shadow_slot;
}
// go to next
sp = list_entry(sp->l.next, struct shadow_pair, l);
}
//fprintf(stderr,"lookup count: %d, total count: %d\n", current_count ,count);
return 0;
}
/*
* shack_set_shadow()
* Insert a guest eip to host eip pair if it is not yet created.
*/
void shack_set_shadow(CPUState *env, target_ulong guest_eip, unsigned long *host_eip)
{
static int count = 0;
struct shadow_pair *sp = *get_shadow_pair_head_from_hash(env, guest_eip);
// search the head
int current_count = 0;
while( sp->l.next){ // has next (not the ending one)
++current_count;
++count;
if(sp->guest_eip == guest_eip){
*sp->shadow_slot = (unsigned long)(host_eip);
break;
}
// go to next
sp = list_entry(sp->l.next, struct shadow_pair, l);
}
//fprintf(stderr,"search count: %d, total count: %d\n", current_count ,count);
}
/*
* helper_shack_flush()
* Reset shadow stack.
*/
void helper_shack_flush(CPUState *env)
{
int i;
// clean the hash
for(i=0;i< (2 << SHACK_INDEX_BITS);i++){
// get head
struct shadow_pair* sp = ((struct shadow_pair**)env->shadow_hash_list)[i];
// delete the head
while( sp->l.next){ // has next (not the ending one)
struct shadow_pair* next_sp = list_entry(sp->l.next, struct shadow_pair, l);
free(sp);
// go to next
sp = next_sp;
}
// here, sp will be the last
sp->l.prev = NULL;
((struct shadow_pair**)env->shadow_hash_list)[i] = sp;
}
}
void insert_unresolved_eip(CPUState *env, target_ulong next_eip, unsigned long *slot){
struct shadow_pair* sp = (struct shadow_pair*)malloc(sizeof(struct shadow_pair));
struct shadow_pair** old_sp_ptr = get_shadow_pair_head_from_hash(env, next_eip);
sp->guest_eip = next_eip;
sp->shadow_slot = slot;
sp->l.prev = NULL;
sp->l.next = &((*old_sp_ptr)->l);
(*old_sp_ptr)->l.prev = &sp->l;
*old_sp_ptr = sp;
}
// this helper does:
// - check if can find from hash
// - if not, push an entry
void helper_shack_push(CPUState *env, target_ulong guest_eip){
unsigned long *slot = &env->shadow_ret_addr[env->shack_top - env->shack];
// check if we need to translate this addr?
unsigned long host_eip = lookup_shadow_ret_addr(env, guest_eip);
if(host_eip > 0){
// just add the result into slot
*slot = host_eip;
}
else{
// insert unresolved eip
*slot = 0;
insert_unresolved_eip(env, guest_eip, slot);
}
}
/*
* push_shack()
* Push next guest eip into shadow stack.
*/
void push_shack(CPUState *env, TCGv_ptr cpu_env, target_ulong next_eip)
{
// label
int label_do_push = gen_new_label();
// prepare registers
TCGv_ptr temp_shack_end = tcg_temp_local_new_ptr(); // store shack end
TCGv_ptr temp_shack_top = tcg_temp_local_new_ptr(); // store shack top
TCGv temp_next_eip = tcg_temp_local_new(); // store eip
// load common values
tcg_gen_ld_ptr(temp_shack_end, cpu_env, offsetof(CPUState, shack_end));
tcg_gen_ld_ptr(temp_shack_top, cpu_env, offsetof(CPUState, shack_top));
tcg_gen_mov_tl(temp_next_eip, tcg_const_tl(next_eip));
// check shack full?
tcg_gen_brcond_ptr(TCG_COND_NE,temp_shack_top,temp_shack_end,label_do_push); // if not full
// flush here
TCGv_ptr temp_shack_start = tcg_temp_new_ptr(); // store shack start
//tcg_en_st_tl(tcg_const_tl(0), cpu_env, offsetof(CPUState, shadow_ret_count)); // reset ret count
tcg_gen_ld_ptr(temp_shack_start, cpu_env, offsetof(CPUState, shack));
tcg_gen_mov_tl(temp_shack_top, temp_shack_start);
tcg_temp_free_ptr(temp_shack_start);
// call helper: flush the hash
gen_helper_shack_flush(cpu_env);
// end of flush
gen_set_label(label_do_push);
// do push here
// push guest eip
tcg_gen_st_ptr(temp_next_eip, temp_shack_top, 0); // store guest eip
tcg_gen_addi_ptr(temp_shack_top, temp_shack_top, sizeof(uint64_t)); // increase top
// call helper: check if we can fill the ret directly, or need to add hash-pair
gen_helper_shack_push(cpu_env, temp_next_eip);
// store back top
tcg_gen_st_ptr(temp_shack_top, cpu_env, offsetof(CPUState, shack_top));
// clean up
tcg_temp_free(temp_next_eip);
tcg_temp_free_ptr(temp_shack_top);
tcg_temp_free_ptr(temp_shack_end);
}
/*
* pop_shack()
* Pop next host eip from shadow stack.
*/
void pop_shack(TCGv_ptr cpu_env, TCGv next_eip)
{
// labels
int label_end = gen_new_label();
// prepare registers
TCGv_ptr temp_shack_start = tcg_temp_local_new_ptr(); // store shack start
TCGv_ptr temp_shack_top = tcg_temp_local_new_ptr(); // store shack top
TCGv eip_on_shack = tcg_temp_local_new();
TCGv_ptr host_slot_addr = tcg_temp_local_new();
TCGv_ptr host_addr = tcg_temp_new();
// load common values
tcg_gen_ld_ptr(temp_shack_start, cpu_env, offsetof(CPUState, shack));
tcg_gen_ld_ptr(temp_shack_top, cpu_env, offsetof(CPUState, shack_top));
// check if stack empty?
tcg_gen_brcond_ptr(TCG_COND_EQ,temp_shack_top,temp_shack_start,label_end);
// stack not empty, pop one
tcg_gen_subi_ptr(temp_shack_top, temp_shack_top, sizeof(uint64_t)); // decrease top
tcg_gen_st_ptr(temp_shack_top, cpu_env, offsetof(CPUState, shack_top)); // store back top
tcg_gen_ld_tl(eip_on_shack, temp_shack_top, 0); // get eip
// check if the same?
tcg_gen_brcond_ptr(TCG_COND_NE,tcg_const_tl(next_eip),eip_on_shack,label_end); // go to "end" if not the same
tcg_gen_ld_tl(host_slot_addr, temp_shack_top, sizeof(unsigned long)); // get slot addr
tcg_gen_ld_ptr(host_addr, host_slot_addr, 0) ; // get host addr
tcg_gen_brcond_ptr(TCG_COND_EQ,tcg_const_tl(0),host_addr,label_end); // go to "end" if addr is zero
// jump!
*gen_opc_ptr++ = INDEX_op_jmp;
*gen_opparam_ptr++ = GET_TCGV_I32(host_addr);
// label: end
gen_set_label(label_end);
tcg_temp_free(eip_on_shack);
tcg_temp_free_ptr(host_slot_addr);
tcg_temp_free_ptr(host_addr);
tcg_temp_free_ptr(temp_shack_top);
tcg_temp_free_ptr(temp_shack_start);
}
/*
* Indirect Branch Target Cache
*/
__thread int update_ibtc;
static struct ibtc_table ibtc;
static target_ulong last_guest_eip;
/*
* helper_lookup_ibtc()
* Look up IBTC. Return next host eip if cache hit or
* back-to-dispatcher stub address if cache miss.
*/
void *helper_lookup_ibtc(target_ulong guest_eip)
{
int index;
last_guest_eip = guest_eip;
index = last_guest_eip & IBTC_CACHE_MASK;
void* addr = ibtc.htable[index].tb;
if(ibtc.htable[index].guest_eip == guest_eip && addr){
return addr;
}
else{
return optimization_ret_addr;
}
}
/*
* update_ibtc_entry()
* Populate eip and tb pair in IBTC entry.
*/
void update_ibtc_entry(TranslationBlock *tb)
{
int index = last_guest_eip & IBTC_CACHE_MASK;
ibtc.htable[index].guest_eip = last_guest_eip;
ibtc.htable[index].tb = tb;
}
/*
* ibtc_init()
* Create and initialize indirect branch target cache.
*/
static inline void ibtc_init(CPUState *env)
{
memset(&ibtc, 0, sizeof(ibtc));
}
/*
* init_optimizations()
* Initialize optimization subsystem.
*/
int init_optimizations(CPUState *env)
{
shack_init(env);
ibtc_init(env);
return 0;
}
/*
* vim: ts=8 sts=4 sw=4 expandtab
*/