@@ -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 );
5049extern void do_rt_sigreturn (void );
5150extern 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+
96102void ____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+
270294static 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+
274302struct 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+
290321static 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
291327static const size_t svc_gate_size = 5 ;
328+ #endif /* !USE_SYSCALL_TABLE */
292329
293330static 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 */
318368static 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+
487566static 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