forked from advanced-microcode-patching/shiva
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathshiva_transform.c
649 lines (604 loc) · 23.7 KB
/
shiva_transform.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
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
#include "shiva.h"
#if __aarch64__
#include "shiva_aarch64.h"
#endif
#define COPY_FIRST_HALF 0
#define COPY_SECOND_HALF 2
#define COPY_TF_SOURCE 3
#define ARM_INSN_LEN 4
#define AARCH64_NOP 0xd503201f
/*
* Example of function transformation on foo() in some target executable.
*
* Tranformed version of foo():
*
* 1. First we copy the new function into place with it's spliced
* code.
* 2. Next we relink the first half and second half of the code (Avoiding the spliced in code)
* 3. Down the road aways when relocations are handled, the spliced code will be properly fixed up
* by the module relocator.
*
* .text
* int foo(void) {
* [original code] <- shiva_transform.c:shiva_tf_relink_new_func()
* [spliced code ] <- shiva_module.c:apply_relocation()
* [original code] <- shiva_transform.c:shiva_tf_relink_new_func()
* }
*/
/*
* shiva_tf_splice_function
* Copy the function that is being transformed into a new location
* while splicing the transform source (The patch) into place.
* If the original byte-code is smaller than the patch, then we overwrite
* the original code and then extend from that offset to make room for the
* rest of the patch.
*
* The patch code is relocatable, and will be properly relocated by apply_relocation().
* The code before and after the patch insertion may also need to be relinked due to
* offsets changing.
*/
static bool
shiva_tf_splice_function(struct shiva_module *linker, struct shiva_transform *transform,
uint8_t *dst)
{
/*
* Destination function
*/
uint8_t *dest = dst;
/*
* Source function
*/
uint8_t *source = (uint8_t *)transform->target_symbol.value + linker->target_base;
size_t copy_len;
/*
* Step 1 Inject first half
* Copy first half of function into module image.
*/
copy_len = transform->offset;
shiva_debug("COPY_FIRST_HALF: dest:%p, source:%p, len:%zu\n",
dest, source, copy_len);
memcpy(dest, source, copy_len);
transform->splice.copy_len1 = copy_len;
test_mark();
/* Step 2. Inject patch code. If the patch code is larger
* than the original code, then extend.
* If the patch code is the same size, then simply replace
* the code in question.
* If the patch code is smaller than the target area, then
* this implicitly instructs us to patch the remaining target
* code with NOPS.
* Copy the patch code (Transform source) into the correct
* offset (transform->offset to be exact) of the new function.
*/
copy_len = transform->new_len;
source = transform->ptr;
shiva_debug("note: transform_offset: %#zu\n", transform->offset);
shiva_debug("COPY_TF_SOURCE: dest:%#lx, source:%#lx, len:%zu\n",
dest + transform->offset, source, copy_len);
/*
* attribute((naked)) does not work with gcc aarch64.
* Our patch functions are compiled with a 4 byte prologue
* that we must NOP out. As well as an 8 byte epilogue.
*/
#ifdef __aarch64__
*(uint32_t *)&transform->ptr[0] = AARCH64_NOP;
*(uint32_t *)&transform->ptr[transform->new_len - 4] = AARCH64_NOP;
*(uint32_t *)&transform->ptr[transform->new_len - 8] = AARCH64_NOP;
#endif
memcpy(dest + transform->offset, transform->ptr, copy_len);
/*
* If the end address is further away than the amount of code
* we are copying, then overwrite the remaining target code
* with NOPS. This technique allows a splicer to overwrite
* multiple lines of code with just a single line of code in
* their place, overwriting the remaining area with nops.
*/
if (transform->flags & SHIVA_TRANSFORM_F_NOP_PAD) {
uint32_t nop_bytes = AARCH64_NOP;
size_t nop_len = transform->old_len - transform->new_len;
size_t i;
assert((nop_len % ARM_INSN_LEN) == 0);
shiva_debug("Copying in %d NOP instructions at %p\n",
(nop_len / 4) * ARM_INSN_LEN, dest);
for (i = 0; i < nop_len / 4; i++) {
memcpy(dest + transform->offset + copy_len + (i * ARM_INSN_LEN),
&nop_bytes, ARM_INSN_LEN );
}
copy_len += (nop_len / 4) * ARM_INSN_LEN;
}
transform->splice.copy_len2 = copy_len;
test_mark();
/*
* Step 3. Inject last half of function
* Copy the second half of the original function
* into place after the patch code.
*/
source = (uint8_t *)transform->target_symbol.value + linker->target_base;
copy_len = transform->target_symbol.value + transform->target_symbol.size;
copy_len = copy_len - (transform->target_symbol.value + transform->offset + transform->old_len);
shiva_debug("COPY_SECOND_HALF: dest:%#lx, source:%#lx, len:%zu\n",
dest + transform->offset + transform->new_len,
source + transform->offset + transform->old_len, copy_len);
memcpy(dest + transform->offset + transform->new_len,
source + transform->offset + transform->old_len, copy_len);
transform->splice.copy_len3 = copy_len;
test_mark();
/*
* Step 4. Create an extended area at the end of the function
* containing .text read-only data. The data stored here
* are typically read-only addresses that are referenced
* indirectly. Shiva fixes up these areas at runtime via
* ELF relocations, such as ADR_PREL_PG_HI21/ADD_ABS_LO12_NC
* To be more specific this area is for read-only data that
* normally exists within the .text right after a function
* in a relocatable object. This extended read-only data
* area that lives at the end of a function.
* In our specific case, the function is a transform source;
* that is code that is to be spliced into the program.
* At the end of our transform source function, i.e.
* __shiva_tf_splice_fn_foo(), there will exist some data
* encoded into the .text used by various instructions like
* adrp, and add. When we splice the source function (The patch) in with
* the target function, we will have to move this read-only .text
* to the end of the final spliced function. The relocations
* r_addend fields will have to be increased by:
* transform->splice.copy_len3 bytes
*
* foo() function layout after step 4
* 1. [first_half ]
* 2. [patch_code ]
* 3. [second_half]
* 4. [.text encoded values]
*/
shiva_debug("COPY_IN_TEXT_ENCDODINGS: dest:%#lx, source: %#lx, len: %zu\n",
dest + transform->offset + transform->new_len + transform->splice.copy_len3,
&transform->ptr[transform->source_symbol.size],
transform->ext_len);
memcpy(dest + transform->offset + transform->new_len + transform->splice.copy_len3,
&transform->ptr[transform->source_symbol.size],
transform->ext_len);
test_mark();
return true;
}
/*
* Thank you Humbly to the software engineers of GDB (particularly aarch64
* remotely simulated relocations in the GDB server for aarch64).
* Their code helped to shed some light on re-encoding the branches.
*/
#define BRANCH_IS_LOCAL(addr) \
(addr >= branch->current_function.value && \
addr < branch->current_function.value + branch->current_function.size)
#define BRANCH_LINK (1UL << 0)
#define RELOC_MASK(n) ((1U << n) - 1)
#define submask(x) ((1L << ((x) + 1)) - 1)
#define bits(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st)))
#define bit(obj,st) (((obj) >> (st)) & 1)
#define sbits(obj,st,fn) \
((long) (bits(obj,st,fn) | ((long) bit(obj,fn) * ~ submask (fn - st))))
/*
* XXX SECURITY ISSUES XXX
* Due to earlier design decisions, it's not clean to have 'shiva_ctx *ctx' passed
* to this function, therefore we are using 'struct shiva_ctx *ctx_global' which is
* a data segment variable that always points to the shiva ctx towards the top of
* the stack.
*
* We should probably remove ctx_global all together, atleast in this version of
* Shiva. It has some uses in the x86 version, but its dangerous and gives attackers
* a pointer to damn near the top of the stack. If /lib/shiva was PIE on aarch64
* it wouldn't matter so much, but its an ET_EXEC, so this ctx_global variable
* is predictable, and therefore makes the stack predictable.
*
*/
static bool
shiva_tf_relink_xref(struct shiva_module *linker, struct shiva_transform *transform,
struct shiva_xref_site *xref)
{
bool res;
shiva_error_t error;
uint32_t n_adrp_insn;
uint32_t n_add_insn;
uint32_t n_ldr_insn;
uint32_t n_str_insn;
int32_t rel_val, xoffset;
uint64_t rel_addr, adrp_off;
uint8_t *rel_unit;
shiva_debug("ctx_global: %p\n", ctx_global);
assert(xref->adrp_site >= transform->target_symbol.value &&
xref->adrp_site < transform->target_symbol.value + transform->target_symbol.size);
/*
* we get the offset of an adrp instruction from beginning of the
* transformed function. This offset can change if the transform
* new_len is > than old_len.
*/
adrp_off = xref->adrp_site - transform->target_symbol.value;
shiva_debug("Adrp offset: %#lx\n", adrp_off);
if (adrp_off > transform->offset + transform->old_len)
adrp_off += transform->new_len - transform->old_len;
rel_addr = linker->text_vaddr + transform->segment_offset + adrp_off;
shiva_debug("Symbol name: %s\n", xref->symbol.name);
shiva_debug("Source sym: %s\n", xref->current_function.name);
shiva_debug("Target symbol addr: %#lx\n", linker->target_base + xref->symbol.value);
xoffset = rel_val = (int32_t)
(ELF_PAGESTART(linker->target_base + xref->symbol.value) - ELF_PAGESTART(rel_addr));
shiva_debug("rel_val = %#lx - %#lx = %#lx\n", ELF_PAGESTART(linker->target_base + xref->symbol.value),
ELF_PAGESTART(rel_addr), rel_val);
rel_val >>= 12;
shiva_debug("rel_addr: %#lx\n", rel_addr);
shiva_debug("rel_val: %#lx\n", rel_val);
n_adrp_insn = xref->adrp_o_insn & 0xffffffff;
n_adrp_insn = (n_adrp_insn & ~((RELOC_MASK (2) << 29) | (RELOC_MASK(19) << 5)))
| ((rel_val & RELOC_MASK(2)) << 29) | ((rel_val & (RELOC_MASK(19) << 2)) << 3);
rel_unit = (uint8_t *)rel_addr;
memcpy(rel_unit, &n_adrp_insn, 4);
/*
* We are relinking a transformed function's adrp
* It is not necessarily to update the offset in the
* subsequent add/ldr/str instruction, because the
* target address lives in the executable, and the
* add/ldr/str offset will be fixed from there.
*
* In other scenarios, where we are relinking an
* adrp to a completely new variable, we do have
* to update the offset for the subsequent add/ldr/str.
*/
#if 0
switch(xref->type) {
case SHIVA_XREF_TYPE_UNKNOWN:
return false;
case SHIVA_XREF_TYPE_ADRP_ADD:
rel_unit = (uint8_t *)rel_addr;
shiva_debug("Installing SHIVA_XREF_TYPE_ADRP_ADD patch at %#lx to link symbol %s\n",
rel_addr, xref->symbol.name);
memcpy(rel_unit, &n_adrp_insn, 4);
#if 0
shiva_trace_write() cannot work here because it relies on shiva_maps_prot_by_addr()
which cannot find the memory mapping entry for the address we need to write
at: (void *)rel_unit. In the future I will fix this so that shiva_trace_write()
can work. Meanwhile, we will do this manually.
res = shiva_trace_write(ctx_global, 0, (void *)rel_unit,
(void *)&n_adrp_insn, 4, &error);
if (res == false) {
fprintf(stderr, "shiva_trace_write failed: %s\n", shiva_error_msg(&error));
return false;
}
#endif
rel_val = xref->symbol.value;
shiva_debug("Add offset: %#lx\n", rel_val);
n_add_insn = xref->next_o_insn;
n_add_insn = (n_add_insn & ~(RELOC_MASK(12) << 10)) | ((rel_val & RELOC_MASK(12)) << 10);
rel_unit += sizeof(uint32_t);
#if 0
XXX TODO
This function cannot work here because it relies on shiva_maps_prot_by_addr()
which cannot find the memory mapping entry for the address we need to write
at: (void *)rel_unit. In the future I will fix this so that shiva_trace_write()
can work. Meanwhile, we will do this manually.
res = shiva_trace_write(ctx_global, 0, (void *)rel_unit,
(void *)&n_add_insn, 4, &error);
if (res == false) {
fprintf(stderr, "shiva_trace_write failed: %s\n", shiva_error_msg(&error));
return false;
}
#endif
memcpy(rel_unit, &n_add_insn, sizeof(uint32_t));
break;
case SHIVA_XREF_TYPE_ADRP_LDR:
//rel_unit = (uint8_t *)rel_addr;
//rel_val = transform->target_symbol.value;
shiva_debug("Installing SHIVA_XREF_TYPE_ADRP_LDR patch at %#lx\n",
xref->adrp_site + linker->target_base);
shiva_debug("SHIVA_XREF_TYPE_ADRP_LDR not yet supported\n");
assert(true);
break;
}
#endif
return true;
}
static bool
shiva_tf_relink_global_branch(struct shiva_module *linker, struct shiva_transform *transform,
struct shiva_branch_site *branch)
{
/*
* Offset to branch instruction (Within our new transformed version of the function)
*/
size_t br_off = branch->branch_site - transform->target_symbol.value;
shiva_debug("br_off = %lx - %lx = %#lx\n", branch->branch_site, transform->target_symbol.value,
br_off);
/*
* mem points to the branch instruction within the new location of the
* spliced/transformed function.
*/
if (br_off > transform->offset + transform->old_len)
br_off += transform->new_len - transform->old_len;
uint8_t *mem = &linker->text_mem[transform->segment_offset + br_off];
/*
* 32bit width instruction
*/
uint32_t raw_insn = *(uint32_t *)mem;
uint64_t br_flag = 0;
uint64_t decoded_offset, target_vaddr;
uint64_t new_offset;
shiva_debug("insn_string: %s\n", branch->insn_string);
shiva_debug("insn: %#lx\n", raw_insn);
if (strncmp(branch->insn_string, "bl ", 3) == 0) {
if (branch->branch_flags & SHIVA_BRANCH_F_PLTCALL) {
shiva_debug("plt call to %s\n", branch->symbol.name);
} else {
shiva_debug("local call to %s\n", branch->symbol.name);
}
decoded_offset = sbits(raw_insn, 0, 25) << 2;
target_vaddr = linker->target_base + branch->branch_site + decoded_offset;
shiva_debug("Target vaddr: %#lx\n", target_vaddr);
new_offset = (target_vaddr - (linker->text_vaddr + transform->segment_offset + br_off)) >> 2;
raw_insn = (raw_insn & ~RELOC_MASK(26)) | (new_offset & RELOC_MASK(26));
*(uint32_t *)mem = raw_insn;
shiva_debug("Relinking branch %s to %#lx. Old offset: %#lx New offset: %#lx\n",
branch->insn_string, target_vaddr, decoded_offset, new_offset);
return true;
}
shiva_debug("No bl instruction found\n");
return false;
}
static bool
shiva_tf_relink_local_branch(struct shiva_module *linker, struct shiva_transform *transform,
struct shiva_branch_site *branch, ssize_t delta)
{
/*
* Offset to branch instruction
*/
size_t br_off = branch->branch_site - transform->target_symbol.value;
shiva_debug("br_off = %lx - %lx = %#lx\n", branch->branch_site, transform->target_symbol.value,
br_off);
/*
* mem points to the branch instruction within the new location of the
* spliced/transformed function.
*/
if (br_off > transform->offset + transform->old_len)
br_off += transform->new_len - transform->old_len;
uint8_t *mem = &linker->text_mem[transform->segment_offset + br_off];
/*
* 32bit ARM instruction
*/
uint32_t raw_insn = *(uint32_t *)mem;
uint64_t br_flag = 0;
uint64_t decoded_offset;
uint64_t o_target_vaddr, n_target_vaddr;
uint32_t br_cond, is64, rn, rt, bit;
uint32_t is_cbnz = 0, is_tbnz = 0;
shiva_debug("Decoding raw insn: %#x (%s)\n", raw_insn, branch->insn_string);
/*
* DECODE/RELINK INSTRUCTION: b / bl
*
* b 0001 01ii iiii iiii iiii iiii iiii iiii
* bl 1001 01ii iiii iiii iiii iiii iiii iiii
*/
if (((raw_insn & 0x7e000000) == 0x94000000) ||
((raw_insn & 0x7e000000) == 0x14000000)) {
shiva_debug("decoding b/bl\n");
br_flag |= (raw_insn >> 31) & 0x1 ? BRANCH_LINK : 0;
decoded_offset = sbits(raw_insn, 0, 25) << 2;
o_target_vaddr = branch->branch_site + decoded_offset - 4;
n_target_vaddr = linker->text_vaddr + transform->segment_offset
+ br_off + decoded_offset - 4;
shiva_debug("Relinking branch %s. Old offset: %#lx New offset: %#lx\n",
branch->insn_string, decoded_offset, decoded_offset + delta);
shiva_aarch64_emit_b(mem, br_flag & BRANCH_LINK, decoded_offset + delta);
/*
* DECODE/RELINK INSRUCTION b
* Not sure why the imm branch instructions are not encoded
* as in the specs... The above code only seems to work for
* bl instructions, but according to the spec it should work
* wit b instructions also. However they seem to be encoded
* differently.
*/
} else if ((raw_insn & 0x7e000000) == 0x16000000) {
shiva_debug("decoding b\n");
decoded_offset = sbits(raw_insn, 0, 25) << 2;
o_target_vaddr = branch->branch_site + decoded_offset - 4;
n_target_vaddr = linker->text_vaddr + transform->segment_offset
+ br_off + decoded_offset - 4;
shiva_debug("Relinking branch %s. Old offset: %#lx New offset: %#lx\n",
branch->insn_string, decoded_offset, decoded_offset + delta);
shiva_aarch64_emit_b2(mem, decoded_offset + delta);
/*
* DECODE/RELINK INSTRUCTION: b.cond
*/
/* b.cond 0101 0100 iiii iiii iiii iiii iii0 cccc */
} else if ((raw_insn & 0xff000010) == 0x54000000) {
shiva_debug("decoding b.cond\n");
br_cond = raw_insn & 0xf;
decoded_offset = sbits(raw_insn, 5, 23) << 2;
o_target_vaddr = branch->branch_site + decoded_offset - 4;
n_target_vaddr = linker->text_vaddr + transform->segment_offset
+ br_off + decoded_offset - 4;
shiva_debug("Relinking branch %s (br_cond: %d). Old offset: %#lx New offset: %#lx\n",
branch->insn_string, br_cond, decoded_offset, decoded_offset + delta);
shiva_aarch64_emit_bcond(mem, br_cond, decoded_offset + delta);
/*
* DECODE/RELINK INSTRUCTION: cbz/cbnz
* cbz T011 010o iiii iiii iiii iiii iiir rrrr
* cbnz T011 010o iiii iiii iiii iiii iiir rrrr
*/
} else if ((raw_insn & 0x7e000000) == 0x34000000) {
shiva_debug("decoding cbz/cbnz\n");
decoded_offset = sbits(raw_insn, 5, 23) << 2;
rn = (raw_insn >> 0) & 0x1f;
is64 = (raw_insn >> 31) & 0x1;
is_cbnz = (raw_insn >> 24) & 0x1;
o_target_vaddr = branch->branch_site + decoded_offset - 4;
n_target_vaddr = linker->text_vaddr + transform->segment_offset
+ br_off + decoded_offset - 4;
shiva_debug("Relinking branch %s. Old offset: %#lx New offset: %#lx\n",
branch->insn_string, decoded_offset, decoded_offset + delta);
shiva_aarch64_emit_cb(mem, is_cbnz, rn, is64, decoded_offset + delta);
/*
* DECODE/RELINK INSTRUCTION: tbz/tbnz
* tbz b011 0110 bbbb biii iiii iiii iiir rrrr
* tbnz B011 0111 bbbb biii iiii iiii iiir rrrr
*/
} else if ((raw_insn & 0x7e000000) == 0x36000000) {
shiva_debug("decoding tbz/tbnz\n");
decoded_offset = sbits(raw_insn, 5, 18) << 2;
rt = (raw_insn >> 0) & 0x1f;
is_tbnz = (raw_insn >> 24) & 0x1;
bit = ((raw_insn >> (31 - 4)) & 0x20) | ((raw_insn >> 19) & 0x1f);
o_target_vaddr = branch->branch_site + decoded_offset - 4;
n_target_vaddr = linker->text_vaddr + transform->segment_offset
+ br_off + decoded_offset - 4;
shiva_debug("Relinking branch %s. Old offset: %#lx New offset: %#lx\n",
branch->insn_string, decoded_offset, decoded_offset + delta);
shiva_aarch64_emit_tb(mem, is_tbnz, bit, rt, decoded_offset + delta);
} else {
fprintf(stderr, "Failed to decode branch instruction for %s\n",
branch->insn_string);
return false;
}
shiva_debug("Returning true\n");
return true;
}
static bool
shiva_tf_relink_new_func(struct shiva_module *linker,
struct shiva_transform *transform)
{
uint8_t *code_ptr;
struct shiva_branch_site *branch;
struct shiva_xref_site *xref;
ssize_t delta;
bool res;
/*
* Local branches (i.e. jmp's) and global branches (i.e. calls)
* must be re-linked.
*/
TAILQ_FOREACH(branch, &transform->branch_list, _linkage) {
if ((transform->flags & SHIVA_TRANSFORM_F_EXTEND) == 0)
continue;
/*
* Relink positive offsets that link to code
* after the splice insertion, from code before
* the splice insertion.
*/
shiva_debug("Processing transform branch: %#lx:%s\n", branch->branch_site,
branch->insn_string);
if (BRANCH_IS_LOCAL(branch->target_vaddr)) {
shiva_debug("Processing local branch: %#lx:%s\n", branch->branch_site,
branch->insn_string);
shiva_debug("Is branch_site %#lx < %#lx\n", branch->branch_site, transform->target_symbol.value +
transform->offset);
if ((branch->branch_site < transform->target_symbol.value + transform->offset) &&
branch->target_vaddr > transform->target_symbol.value + transform->offset) {
delta = transform->new_len - transform->old_len;
shiva_debug("Calling shiva_tf_relink_local_branch with delta %zd\n", delta);
res = shiva_tf_relink_local_branch(linker,
transform, branch, delta);
if (res == false) {
fprintf(stderr,
"shiva_tf_relink_local_branch() failed\n");
return false;
}
} else if ((branch->branch_site > transform->target_symbol.value +
transform->offset + transform->old_len) &&
branch->target_vaddr < transform->target_symbol.value + transform->offset) {
delta = transform->new_len - transform->old_len;
shiva_debug("Calling shiva_tf_relink_local_branch (Backwards) with delta: %d\n", -delta);
res = shiva_tf_relink_local_branch(linker,
transform, branch, -delta);
if (res == false) {
fprintf(stderr,
"shiva_tf_relink_local_branch() failed\n");
return false;
}
}
} else if ((branch->branch_site < transform->target_symbol.value + transform->offset) ||
(branch->branch_site > transform->target_symbol.value + transform->offset + transform->old_len)) {
shiva_debug("Calling shiva_tf_relink_global_branch\n");
res = shiva_tf_relink_global_branch(linker,
transform, branch);
if (res == false) {
fprintf(stderr,
"shiva_tf_relink_global_branch() failed\n");
return false;
}
}
}
/*
* Relink xrefs: instruction pairs such as 'adrp/add'
* are used to reference data. Now that our function has
* been transformed in a totally new memory location we
* must re-link any references to variables with the
* correct offsets, etc.
*/
TAILQ_FOREACH(xref, &transform->xref_list, _linkage) {
if ((xref->adrp_site < transform->target_symbol.value + transform->offset) ||
(xref->adrp_site >= transform->target_symbol.value + transform->offset + transform->old_len)) {
res = shiva_tf_relink_xref(linker, transform, xref);
if (res == false) {
fprintf(stderr,
"shiva_tf_relink_xrefs() failed\n");
return false;
}
}
}
return true;
}
/*
* NOTE to self! Don't forget to update the modules relocation entries
* for code and data that has been shifted forward due to splicing.
* In other words r_offset is updated and r_addend must be updated
* accordingly.
*/
#define ARM_INSN_LEN 4
bool
shiva_tf_process_transforms(struct shiva_module *linker, uint8_t *dst,
struct elf_section section, uint64_t *segment_offset)
{
uint8_t *ptr;
bool res = false;
struct shiva_transform *transform;
elf_symtab_iterator_t symtab;
struct elf_symbol symbol;
TAILQ_FOREACH(transform, &linker->tailq.transform_list, _linkage) {
switch(transform->type) {
case SHIVA_TRANSFORM_SPLICE_FUNCTION:
shiva_debug("Calling shiva_tf_splice_function\n");
shiva_debug("Transform offset: %#lx (%zu)\n", transform->offset,
transform->offset);
if (transform->flags & SHIVA_TRANSFORM_F_EXTEND) {
res = shiva_tf_splice_function(linker, transform, dst);
if (res == true ) {
transform->segment_offset = *segment_offset;
*segment_offset += transform->target_symbol.size;
*segment_offset += transform->source_symbol.size;
*segment_offset += transform->ext_len;
shiva_debug("setting segment_offset to %#lx\n", *segment_offset);
if (*segment_offset % ARM_INSN_LEN != 0) {
shiva_debug("Aligning *segment_offset to 4\n");
*segment_offset = *segment_offset + 4 & ~3;
}
shiva_debug("Calling shiva_tf_relink_new_func\n");
res = shiva_tf_relink_new_func(linker, transform);
}
break;
} else if (transform->flags & SHIVA_TRANSFORM_F_REPLACE) {
res = shiva_tf_splice_function(linker, transform, dst);
if (res == true) {
transform->segment_offset = *segment_offset;
*segment_offset += transform->target_symbol.size;
*segment_offset += transform->source_symbol.size;
*segment_offset += transform->ext_len;
shiva_debug("setting segment_offset to %#lx\n", *segment_offset);
if (*segment_offset % ARM_INSN_LEN != 0) {
shiva_debug("Aligning *segment_offset to 4\n");
*segment_offset = *segment_offset + 4 & ~3;
}
}
}
break;
case SHIVA_TRANSFORM_EMIT_BYTECODE:
default:
break;
}
}
linker->tf_text_offset = *segment_offset;
test_mark();
return res;
}