Revision b8076a74 dyngen.h
b/dyngen.h | ||
---|---|---|
43 | 43 |
#ifdef __ia64__ |
44 | 44 |
static inline void flush_icache_range(unsigned long start, unsigned long stop) |
45 | 45 |
{ |
46 |
while (start < stop) { |
|
47 |
asm volatile ("fc %0" :: "r"(start)); |
|
48 |
start += 32; |
|
49 |
} |
|
50 |
asm volatile (";;sync.i;;srlz.i;;"); |
|
46 | 51 |
} |
47 | 52 |
#endif |
48 | 53 |
|
... | ... | |
204 | 209 |
} |
205 | 210 |
|
206 | 211 |
#endif /* __arm__ */ |
212 |
|
|
213 |
#ifdef __ia64 |
|
214 |
|
|
215 |
|
|
216 |
/* Patch instruction with "val" where "mask" has 1 bits. */ |
|
217 |
static inline void ia64_patch (uint64_t insn_addr, uint64_t mask, uint64_t val) |
|
218 |
{ |
|
219 |
uint64_t m0, m1, v0, v1, b0, b1, *b = (uint64_t *) (insn_addr & -16); |
|
220 |
# define insn_mask ((1UL << 41) - 1) |
|
221 |
unsigned long shift; |
|
222 |
|
|
223 |
b0 = b[0]; b1 = b[1]; |
|
224 |
shift = 5 + 41 * (insn_addr % 16); /* 5 template, 3 x 41-bit insns */ |
|
225 |
if (shift >= 64) { |
|
226 |
m1 = mask << (shift - 64); |
|
227 |
v1 = val << (shift - 64); |
|
228 |
} else { |
|
229 |
m0 = mask << shift; m1 = mask >> (64 - shift); |
|
230 |
v0 = val << shift; v1 = val >> (64 - shift); |
|
231 |
b[0] = (b0 & ~m0) | (v0 & m0); |
|
232 |
} |
|
233 |
b[1] = (b1 & ~m1) | (v1 & m1); |
|
234 |
} |
|
235 |
|
|
236 |
static inline void ia64_patch_imm60 (uint64_t insn_addr, uint64_t val) |
|
237 |
{ |
|
238 |
ia64_patch(insn_addr, |
|
239 |
0x011ffffe000UL, |
|
240 |
( ((val & 0x0800000000000000UL) >> 23) /* bit 59 -> 36 */ |
|
241 |
| ((val & 0x00000000000fffffUL) << 13) /* bit 0 -> 13 */)); |
|
242 |
ia64_patch(insn_addr - 1, 0x1fffffffffcUL, val >> 18); |
|
243 |
} |
|
244 |
|
|
245 |
static inline void ia64_imm64 (void *insn, uint64_t val) |
|
246 |
{ |
|
247 |
/* Ignore the slot number of the relocation; GCC and Intel |
|
248 |
toolchains differed for some time on whether IMM64 relocs are |
|
249 |
against slot 1 (Intel) or slot 2 (GCC). */ |
|
250 |
uint64_t insn_addr = (uint64_t) insn & ~3UL; |
|
251 |
|
|
252 |
ia64_patch(insn_addr + 2, |
|
253 |
0x01fffefe000UL, |
|
254 |
( ((val & 0x8000000000000000UL) >> 27) /* bit 63 -> 36 */ |
|
255 |
| ((val & 0x0000000000200000UL) << 0) /* bit 21 -> 21 */ |
|
256 |
| ((val & 0x00000000001f0000UL) << 6) /* bit 16 -> 22 */ |
|
257 |
| ((val & 0x000000000000ff80UL) << 20) /* bit 7 -> 27 */ |
|
258 |
| ((val & 0x000000000000007fUL) << 13) /* bit 0 -> 13 */) |
|
259 |
); |
|
260 |
ia64_patch(insn_addr + 1, 0x1ffffffffffUL, val >> 22); |
|
261 |
} |
|
262 |
|
|
263 |
static inline void ia64_imm60b (void *insn, uint64_t val) |
|
264 |
{ |
|
265 |
/* Ignore the slot number of the relocation; GCC and Intel |
|
266 |
toolchains differed for some time on whether IMM64 relocs are |
|
267 |
against slot 1 (Intel) or slot 2 (GCC). */ |
|
268 |
uint64_t insn_addr = (uint64_t) insn & ~3UL; |
|
269 |
|
|
270 |
if (val + ((uint64_t) 1 << 59) >= (1UL << 60)) |
|
271 |
fprintf(stderr, "%s: value %ld out of IMM60 range\n", |
|
272 |
__FUNCTION__, (int64_t) val); |
|
273 |
ia64_patch_imm60(insn_addr + 2, val); |
|
274 |
} |
|
275 |
|
|
276 |
static inline void ia64_imm22 (void *insn, uint64_t val) |
|
277 |
{ |
|
278 |
if (val + (1 << 21) >= (1 << 22)) |
|
279 |
fprintf(stderr, "%s: value %li out of IMM22 range\n", |
|
280 |
__FUNCTION__, (int64_t)val); |
|
281 |
ia64_patch((uint64_t) insn, 0x01fffcfe000UL, |
|
282 |
( ((val & 0x200000UL) << 15) /* bit 21 -> 36 */ |
|
283 |
| ((val & 0x1f0000UL) << 6) /* bit 16 -> 22 */ |
|
284 |
| ((val & 0x00ff80UL) << 20) /* bit 7 -> 27 */ |
|
285 |
| ((val & 0x00007fUL) << 13) /* bit 0 -> 13 */)); |
|
286 |
} |
|
287 |
|
|
288 |
/* Like ia64_imm22(), but also clear bits 20-21. For addl, this has |
|
289 |
the effect of turning "addl rX=imm22,rY" into "addl |
|
290 |
rX=imm22,r0". */ |
|
291 |
static inline void ia64_imm22_r0 (void *insn, uint64_t val) |
|
292 |
{ |
|
293 |
if (val + (1 << 21) >= (1 << 22)) |
|
294 |
fprintf(stderr, "%s: value %li out of IMM22 range\n", |
|
295 |
__FUNCTION__, (int64_t)val); |
|
296 |
ia64_patch((uint64_t) insn, 0x01fffcfe000UL | (0x3UL << 20), |
|
297 |
( ((val & 0x200000UL) << 15) /* bit 21 -> 36 */ |
|
298 |
| ((val & 0x1f0000UL) << 6) /* bit 16 -> 22 */ |
|
299 |
| ((val & 0x00ff80UL) << 20) /* bit 7 -> 27 */ |
|
300 |
| ((val & 0x00007fUL) << 13) /* bit 0 -> 13 */)); |
|
301 |
} |
|
302 |
|
|
303 |
static inline void ia64_imm21b (void *insn, uint64_t val) |
|
304 |
{ |
|
305 |
if (val + (1 << 20) >= (1 << 21)) |
|
306 |
fprintf(stderr, "%s: value %li out of IMM21b range\n", |
|
307 |
__FUNCTION__, (int64_t)val); |
|
308 |
ia64_patch((uint64_t) insn, 0x11ffffe000UL, |
|
309 |
( ((val & 0x100000UL) << 16) /* bit 20 -> 36 */ |
|
310 |
| ((val & 0x0fffffUL) << 13) /* bit 0 -> 13 */)); |
|
311 |
} |
|
312 |
|
|
313 |
static inline void ia64_nop_b (void *insn) |
|
314 |
{ |
|
315 |
ia64_patch((uint64_t) insn, (1UL << 41) - 1, 2UL << 37); |
|
316 |
} |
|
317 |
|
|
318 |
static inline void ia64_ldxmov(void *insn, uint64_t val) |
|
319 |
{ |
|
320 |
if (val + (1 << 21) < (1 << 22)) |
|
321 |
ia64_patch((uint64_t) insn, 0x1fff80fe000UL, 8UL << 37); |
|
322 |
} |
|
323 |
|
|
324 |
static inline int ia64_patch_ltoff(void *insn, uint64_t val, |
|
325 |
int relaxable) |
|
326 |
{ |
|
327 |
if (relaxable && (val + (1 << 21) < (1 << 22))) { |
|
328 |
ia64_imm22_r0(insn, val); |
|
329 |
return 0; |
|
330 |
} |
|
331 |
return 1; |
|
332 |
} |
|
333 |
|
|
334 |
struct ia64_fixup { |
|
335 |
struct ia64_fixup *next; |
|
336 |
void *addr; /* address that needs to be patched */ |
|
337 |
long value; |
|
338 |
}; |
|
339 |
|
|
340 |
#define IA64_PLT(insn, plt_index) \ |
|
341 |
do { \ |
|
342 |
struct ia64_fixup *fixup = alloca(sizeof(*fixup)); \ |
|
343 |
fixup->next = plt_fixes; \ |
|
344 |
plt_fixes = fixup; \ |
|
345 |
fixup->addr = (insn); \ |
|
346 |
fixup->value = (plt_index); \ |
|
347 |
plt_offset[(plt_index)] = 1; \ |
|
348 |
} while (0) |
|
349 |
|
|
350 |
#define IA64_LTOFF(insn, val, relaxable) \ |
|
351 |
do { \ |
|
352 |
if (ia64_patch_ltoff(insn, val, relaxable)) { \ |
|
353 |
struct ia64_fixup *fixup = alloca(sizeof(*fixup)); \ |
|
354 |
fixup->next = ltoff_fixes; \ |
|
355 |
ltoff_fixes = fixup; \ |
|
356 |
fixup->addr = (insn); \ |
|
357 |
fixup->value = (val); \ |
|
358 |
} \ |
|
359 |
} while (0) |
|
360 |
|
|
361 |
static inline void ia64_apply_fixes (uint8_t **gen_code_pp, |
|
362 |
struct ia64_fixup *ltoff_fixes, |
|
363 |
uint64_t gp, |
|
364 |
struct ia64_fixup *plt_fixes, |
|
365 |
int num_plts, |
|
366 |
unsigned long *plt_target, |
|
367 |
unsigned int *plt_offset) |
|
368 |
{ |
|
369 |
static const uint8_t plt_bundle[] = { |
|
370 |
0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, /* nop 0; movl r1=GP */ |
|
371 |
0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x60, |
|
372 |
|
|
373 |
0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, /* nop 0; brl IP */ |
|
374 |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0 |
|
375 |
}; |
|
376 |
uint8_t *gen_code_ptr = *gen_code_pp, *plt_start, *got_start, *vp; |
|
377 |
struct ia64_fixup *fixup; |
|
378 |
unsigned int offset = 0; |
|
379 |
struct fdesc { |
|
380 |
long ip; |
|
381 |
long gp; |
|
382 |
} *fdesc; |
|
383 |
int i; |
|
384 |
|
|
385 |
if (plt_fixes) { |
|
386 |
plt_start = gen_code_ptr; |
|
387 |
|
|
388 |
for (i = 0; i < num_plts; ++i) { |
|
389 |
if (plt_offset[i]) { |
|
390 |
plt_offset[i] = offset; |
|
391 |
offset += sizeof(plt_bundle); |
|
392 |
|
|
393 |
fdesc = (struct fdesc *) plt_target[i]; |
|
394 |
memcpy(gen_code_ptr, plt_bundle, sizeof(plt_bundle)); |
|
395 |
ia64_imm64 (gen_code_ptr + 0x02, fdesc->gp); |
|
396 |
ia64_imm60b(gen_code_ptr + 0x12, |
|
397 |
(fdesc->ip - (long) (gen_code_ptr + 0x10)) >> 4); |
|
398 |
gen_code_ptr += sizeof(plt_bundle); |
|
399 |
} |
|
400 |
} |
|
401 |
|
|
402 |
for (fixup = plt_fixes; fixup; fixup = fixup->next) |
|
403 |
ia64_imm21b(fixup->addr, |
|
404 |
((long) plt_start + plt_offset[fixup->value] |
|
405 |
- ((long) fixup->addr & ~0xf)) >> 4); |
|
406 |
} |
|
407 |
|
|
408 |
got_start = gen_code_ptr; |
|
409 |
|
|
410 |
/* First, create the GOT: */ |
|
411 |
for (fixup = ltoff_fixes; fixup; fixup = fixup->next) { |
|
412 |
/* first check if we already have this value in the GOT: */ |
|
413 |
for (vp = got_start; vp < gen_code_ptr; ++vp) |
|
414 |
if (*(uint64_t *) vp == fixup->value) |
|
415 |
break; |
|
416 |
if (vp == gen_code_ptr) { |
|
417 |
/* Nope, we need to put the value in the GOT: */ |
|
418 |
*(uint64_t *) vp = fixup->value; |
|
419 |
gen_code_ptr += 8; |
|
420 |
} |
|
421 |
ia64_imm22(fixup->addr, (long) vp - gp); |
|
422 |
} |
|
423 |
*gen_code_pp = gen_code_ptr; |
|
424 |
} |
|
425 |
|
|
426 |
#endif |
Also available in: Unified diff