Skip to content

Commit 43b1ba2

Browse files
authored
Merge pull request #6 from retrage/handle-svc-imm
Add option to build syscall table
2 parents 75b9451 + dc60d29 commit 43b1ba2

File tree

3 files changed

+112
-16
lines changed

3 files changed

+112
-16
lines changed

.github/workflows/build.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ jobs:
2222
env:
2323
CC: aarch64-linux-gnu-gcc
2424
FULL_CONTEXT: 0
25+
USE_SYSCALL_TABLE: 0
2526
SYSCALL_RECORD: 0
2627
run: |
2728
make clean
@@ -32,6 +33,7 @@ jobs:
3233
env:
3334
CC: aarch64-linux-gnu-gcc
3435
FULL_CONTEXT: 1
36+
USE_SYSCALL_TABLE: 1
3537
SYSCALL_RECORD: 1
3638
run: |
3739
make clean

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ ifeq ($(FULL_CONTEXT), 1)
2121
CFLAGS += -DFULL_CONTEXT
2222
endif
2323

24+
ifeq ($(USE_SYSCALL_TABLE), 1)
25+
CFLAGS += -DUSE_SYSCALL_TABLE
26+
endif
27+
2428
ifeq ($(SYSCALL_RECORD), 1)
2529
CFLAGS += -DSUPPLEMENTAL__SYSCALL_RECORD
2630
endif

main.c

Lines changed: 106 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ static void bm_increment(size_t syscall_nr) {
4646
}
4747
#endif /* SUPPLEMENTAL__SYSCALL_RECORD */
4848

49-
extern void syscall_addr(void);
5049
extern void do_rt_sigreturn(void);
5150
extern long enter_syscall(int64_t, int64_t, int64_t, int64_t, int64_t, int64_t,
5251
int64_t, int64_t);
@@ -93,6 +92,13 @@ extern void asm_syscall_hook(void);
9392
#define PUSH_CONTEXT(reg, size) "sub " #reg ", " #reg ", " STR(size) " \n\t"
9493
#define POP_CONTEXT(reg, size) "add " #reg ", " #reg ", " STR(size) " \n\t"
9594

95+
#ifdef USE_SYSCALL_TABLE
96+
void *syscall_table = NULL;
97+
size_t syscall_table_size = 0;
98+
#else
99+
extern void syscall_addr(void);
100+
#endif /* !USE_SYSCALL_TABLE */
101+
96102
void ____asm_impl(void) {
97103
/*
98104
* enter_syscall triggers a kernel-space system call
@@ -106,6 +112,17 @@ void ____asm_impl(void) {
106112
* @param a8 return address (x7)
107113
* @return return value (x0)
108114
*/
115+
#ifdef USE_SYSCALL_TABLE
116+
asm volatile(
117+
".extern syscall_table \n\t"
118+
".globl enter_syscall \n\t"
119+
"enter_syscall: \n\t"
120+
"mov x8, x6 \n\t"
121+
"ldr x6, =syscall_table \n\t"
122+
"ldr x6, [x6] \n\t"
123+
"add x6, x6, xzr, lsl #3 \n\t"
124+
"br x6 \n\t");
125+
#else
109126
asm volatile(
110127
".globl enter_syscall \n\t"
111128
"enter_syscall: \n\t"
@@ -114,6 +131,7 @@ void ____asm_impl(void) {
114131
"syscall_addr: \n\t"
115132
"svc #0 \n\t"
116133
"ret \n\t");
134+
#endif /* !USE_SYSCALL_TABLE */
117135

118136
/*
119137
* asm_syscall_hook is the address where the
@@ -267,13 +285,24 @@ static inline uint32_t gen_br(uint8_t rn) {
267285
return insn;
268286
}
269287

288+
static inline uint32_t gen_ret(void) { return 0xd65f03c0; }
289+
290+
static inline uint32_t gen_svc(uint16_t imm) {
291+
return 0xd4000001 | ((uint32_t)imm << 5);
292+
}
293+
270294
static inline bool is_svc(uint32_t insn) {
271295
return (insn & 0xffe0000f) == 0xd4000001;
272296
}
273297

298+
static inline uint16_t get_svc_imm(uint32_t insn) {
299+
return (uint16_t)((insn >> 5) & 0xffff);
300+
}
301+
274302
struct records_entry {
275303
uintptr_t *records;
276-
size_t records_size;
304+
uint16_t *imms;
305+
size_t records_size_max;
277306
size_t count;
278307
uintptr_t reachable_range_min;
279308
uintptr_t reachable_range_max;
@@ -287,33 +316,54 @@ LIST_HEAD(records_head, records_entry) head;
287316
#define PAGE_SIZE (0x1000)
288317
#endif
289318

319+
#define INITIAL_RECORDS_SIZE (PAGE_SIZE / sizeof(uintptr_t))
320+
290321
static const size_t jump_code_size = 5;
322+
323+
#ifdef USE_SYSCALL_TABLE
324+
static const size_t svc_entry_size = 2;
325+
static const size_t svc_gate_size = 6;
326+
#else
291327
static const size_t svc_gate_size = 5;
328+
#endif /* !USE_SYSCALL_TABLE */
292329

293330
static void init_records(struct records_entry *entry) {
294331
assert(entry != NULL);
295332
entry->trampoline = NULL;
296333
entry->reachable_range_min = 0;
297334
entry->reachable_range_max = UINT64_MAX;
298335
entry->count = 0;
299-
entry->records_size = PAGE_SIZE;
300-
entry->records = malloc(entry->records_size);
336+
entry->records_size_max = INITIAL_RECORDS_SIZE;
337+
entry->records = malloc(entry->records_size_max * sizeof(uintptr_t));
301338
assert(entry->records != NULL);
339+
entry->imms = malloc(entry->records_size_max * sizeof(uint16_t));
340+
assert(entry->imms != NULL);
302341
}
303342

304343
__attribute__((unused)) static void dump_records(struct records_entry *entry) {
305344
assert(entry != NULL);
306345
fprintf(stderr, "reachable_range: [0x%016lx-0x%016lx]\n",
307346
entry->reachable_range_min, entry->reachable_range_max);
308347
fprintf(stderr, "count: %ld\n", entry->count);
309-
fprintf(stderr, "records_size: 0x%lx\n", entry->records_size);
348+
fprintf(stderr, "records_size_max: 0x%lx\n", entry->records_size_max);
310349
for (size_t i = 0; i < entry->count; i++) {
311350
uintptr_t record = entry->records[i];
312351
fprintf(stderr, "record[%ld]: 0x%016lx %c%c%c\n", i, (record & ~0x3),
313352
(record & 0x2) ? 'r' : '-', (record & 0x1) ? 'w' : '-', 'x');
314353
}
315354
}
316355

356+
static inline bool should_hook(uintptr_t addr) {
357+
#ifdef USE_SYSCALL_TABLE
358+
return (addr != (uintptr_t)do_rt_sigreturn) &&
359+
(addr < (uintptr_t)syscall_table ||
360+
addr >= (uintptr_t)syscall_table + (uintptr_t)syscall_table_size);
361+
#else
362+
return (addr != (uintptr_t)do_rt_sigreturn) &&
363+
(addr != (uintptr_t)syscall_addr);
364+
#endif /* !USE_SYSCALL_TABLE */
365+
}
366+
317367
/* find svc using pattern matching */
318368
static void record_svc(char *code, size_t code_size, int mem_prot) {
319369
/* add PROT_READ to read the code */
@@ -327,13 +377,7 @@ static void record_svc(char *code, size_t code_size, int mem_prot) {
327377
}
328378
uintptr_t addr = (uintptr_t)ptr;
329379
assert((addr & 0x3ULL) == 0);
330-
if ((addr == (uintptr_t)syscall_addr) ||
331-
(addr == (uintptr_t)do_rt_sigreturn)) {
332-
/*
333-
* skip the syscall replacement for
334-
* our system call hook (enter_syscall)
335-
* so that it can issue system calls.
336-
*/
380+
if (!should_hook(addr)) {
337381
continue;
338382
}
339383

@@ -357,11 +401,16 @@ static void record_svc(char *code, size_t code_size, int mem_prot) {
357401
/* Embed mem prot info in the last two bits */
358402
uintptr_t record = addr | (has_r ? (1 << 1) : 0) | (has_w ? (1 << 0) : 0);
359403
entry->records[entry->count] = record;
404+
entry->imms[entry->count] = get_svc_imm(*ptr);
360405
entry->count += 1;
361-
if (entry->count * sizeof(uintptr_t) >= entry->records_size) {
362-
entry->records_size *= 2;
363-
entry->records = realloc(entry->records, entry->records_size);
406+
if (entry->count >= entry->records_size_max) {
407+
entry->records_size_max *= 2;
408+
entry->records =
409+
realloc(entry->records, entry->records_size_max * sizeof(uintptr_t));
364410
assert(entry->records != NULL);
411+
entry->imms =
412+
realloc(entry->imms, entry->records_size_max * sizeof(uint16_t));
413+
assert(entry->imms != NULL);
365414
}
366415

367416
entry->reachable_range_min = range_min;
@@ -484,6 +533,36 @@ static void rewrite_code(void) {
484533
}
485534
}
486535

536+
#ifdef USE_SYSCALL_TABLE
537+
/* Create a system call table for every svc #imm */
538+
/* NOTE: Although Linux does not use the #imm in svc instructions, some OSes
539+
* such as NetBSD and Windows use it to store the system call number. To support
540+
* such systems, we create a system call table for every svc #imm.
541+
*/
542+
static void setup_syscall_table(void) {
543+
const size_t nr_svc = UINT16_MAX + 1; /* 0x10000, as #imm is 16-bit */
544+
const size_t svc_table_size =
545+
align_up(sizeof(uint32_t) * svc_entry_size * nr_svc, PAGE_SIZE);
546+
void *svc_table = mmap(NULL, svc_table_size, PROT_READ | PROT_WRITE,
547+
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
548+
assert(svc_table != MAP_FAILED);
549+
550+
uint32_t *code = (uint32_t *)svc_table;
551+
size_t off = 0;
552+
for (size_t i = 0; i < nr_svc; i++) {
553+
assert(off == i * svc_entry_size);
554+
code[off++] = gen_svc(i); /* svc #i */
555+
code[off++] = gen_ret(); /* ret */
556+
assert(off - i * svc_entry_size == svc_entry_size);
557+
}
558+
559+
syscall_table = svc_table;
560+
syscall_table_size = svc_table_size;
561+
562+
assert(!mprotect(svc_table, svc_table_size, PROT_EXEC));
563+
}
564+
#endif /* USE_SYSCALL_TABLE */
565+
487566
static void setup_trampoline(void) {
488567
struct records_entry *entry = NULL;
489568

@@ -495,7 +574,7 @@ static void setup_trampoline(void) {
495574
assert(range_max > 0);
496575
assert(range_max - range_min >= PAGE_SIZE);
497576

498-
assert(entry->count * sizeof(uintptr_t) <= entry->records_size);
577+
assert(entry->count <= entry->records_size_max);
499578

500579
const size_t mem_size = align_up(
501580
jump_code_size + svc_gate_size * sizeof(uint32_t) * entry->count,
@@ -555,6 +634,9 @@ static void setup_trampoline(void) {
555634
/*
556635
* put 'gate' code for each svc instruction
557636
*
637+
* #ifdef USE_SYSCALL_TABLE
638+
* movz x6, (#imm & 0xffff)
639+
* #endif
558640
* movz x14, (#return_pc & 0xffff)
559641
* movk x14, ((#return_pc >> 16) & 0xffff), lsl 16
560642
* movk x14, ((#return_pc >> 32) & 0xffff), lsl 32
@@ -565,6 +647,11 @@ static void setup_trampoline(void) {
565647
const size_t gate_off = off;
566648
assert(gate_off == jump_code_size + svc_gate_size * i);
567649

650+
#ifdef USE_SYSCALL_TABLE
651+
const uint16_t imm = entry->imms[i];
652+
code[off++] = gen_movz(6, (imm >> 0) & 0xffff, 0);
653+
#endif /* USE_SYSCALL_TABLE */
654+
568655
const uintptr_t return_pc = (entry->records[i] & ~0x3) + sizeof(uint32_t);
569656
code[off++] = gen_movz(14, (return_pc >> 0) & 0xffff, 0);
570657
code[off++] = gen_movk(14, (return_pc >> 16) & 0xffff, 16);
@@ -627,6 +714,9 @@ __attribute__((constructor(0xffff))) static void __svc_hook_init(void) {
627714
bm_init();
628715
#endif /* SUPPLEMENTAL__SYSCALL_RECORD */
629716
scan_code();
717+
#ifdef USE_SYSCALL_TABLE
718+
setup_syscall_table();
719+
#endif /* USE_SYSCALL_TABLE */
630720
setup_trampoline();
631721
rewrite_code();
632722
load_hook_lib();

0 commit comments

Comments
 (0)