diff --git a/src/flamenco/runtime/tests/fd_vm_test.c b/src/flamenco/runtime/tests/fd_vm_test.c
index 20bb1c2e2c..2d5131dff0 100644
--- a/src/flamenco/runtime/tests/fd_vm_test.c
+++ b/src/flamenco/runtime/tests/fd_vm_test.c
@@ -115,6 +115,11 @@ do{
     ulong mask = (1UL << (max_pc % 64)) - 1UL;
     calldests[ max_pc / 64 ] &= mask;
   }
+  ulong entry_pc = fd_ulong_min( input->vm_ctx.entry_pc, rodata_sz / 8 - 1 );
+  if( input->vm_ctx.sbpf_version >= FD_SBPF_V3 ) {
+    /* in v3 we have to enable the entrypoint */
+    calldests[ entry_pc / 64 ] |= ( 1 << ( entry_pc % 64 ) );
+  }
 
   /* Setup syscalls. Have them all be no-ops */
   fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_valloc_malloc( valloc, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ) );
@@ -154,7 +159,7 @@ do{
     rodata_sz / 8, /* text_cnt */
     0, /* text_off */
     rodata_sz, /* text_sz */
-    input->vm_ctx.entry_pc,
+    entry_pc,
     calldests,
     input->vm_ctx.sbpf_version,
     syscalls,
diff --git a/src/flamenco/vm/fd_vm.c b/src/flamenco/vm/fd_vm.c
index 2bab9bd60d..f016c0a6c8 100644
--- a/src/flamenco/vm/fd_vm.c
+++ b/src/flamenco/vm/fd_vm.c
@@ -156,22 +156,29 @@ fd_vm_strerror( int err ) {
 int
 fd_vm_validate( fd_vm_t const * vm ) {
 
+  ulong sbpf_version = vm->sbpf_version;
+
   /* A mapping of all the possible 1-byte sBPF opcodes to their
      validation criteria. */
 
-# define FD_VALID       ((uchar)0) /* Valid opcode */
-# define FD_CHECK_JMP   ((uchar)1) /* Validation should check that the instruction is a valid jump */
-# define FD_CHECK_END   ((uchar)2) /* Validation should check that the instruction is a valid endianness conversion */
-# define FD_CHECK_ST    ((uchar)3) /* Validation should check that the instruction is a valid store */
-# define FD_CHECK_LDQ   ((uchar)4) /* Validation should check that the instruction is a valid load-quad */
-# define FD_CHECK_DIV   ((uchar)5) /* Validation should check that the instruction is a valid division by immediate */
-# define FD_CHECK_SH32  ((uchar)6) /* Validation should check that the immediate is a valid 32-bit shift exponent */
-# define FD_CHECK_SH64  ((uchar)7) /* Validation should check that the immediate is a valid 64-bit shift exponent */
-# define FD_INVALID     ((uchar)8) /* The opcode is invalid */
-# define FD_CHECK_CALLX ((uchar)9) /* Validation should check that callx has valid register number */
-# define FD_CHECK_CALLX_DEPR ((uchar)10) /* Older / deprecated FD_CHECK_CALLX */
-
-  static uchar validation_map[ 256 ] = {
+# define FD_VALID               ((uchar)0) /* Valid opcode */
+# define FD_CHECK_JMP_V3        ((uchar)1) /* Validation should check that the instruction is a valid jump (v3+) */
+# define FD_CHECK_END           ((uchar)2) /* Validation should check that the instruction is a valid endianness conversion */
+# define FD_CHECK_ST            ((uchar)3) /* Validation should check that the instruction is a valid store */
+# define FD_CHECK_LDQ           ((uchar)4) /* Validation should check that the instruction is a valid load-quad */
+# define FD_CHECK_DIV           ((uchar)5) /* Validation should check that the instruction is a valid division by immediate */
+# define FD_CHECK_SH32          ((uchar)6) /* Validation should check that the immediate is a valid 32-bit shift exponent */
+# define FD_CHECK_SH64          ((uchar)7) /* Validation should check that the immediate is a valid 64-bit shift exponent */
+# define FD_INVALID             ((uchar)8) /* The opcode is invalid */
+# define FD_CHECK_CALL_REG      ((uchar)9) /* Validation should check that callx has valid register number */
+# define FD_CHECK_CALL_REG_DEPR ((uchar)10) /* Older / deprecated FD_CHECK_CALLX */
+# define FD_CHECK_CALL_IMM      ((uchar)11) /* Check call against functions registry */
+# define FD_CHECK_SYSCALL       ((uchar)12) /* Check call against syscalls registry */
+# define FD_CHECK_JMP_V0        ((uchar)13) /* Validation should check that the instruction is a valid jump (v0..v2) */
+
+  uchar FD_CHECK_JMP = FD_VM_SBPF_STATIC_SYSCALLS (sbpf_version) ? FD_CHECK_JMP_V3 : FD_CHECK_JMP_V0;
+
+  uchar validation_map[ 256 ] = {
     /* 0x00 */ FD_INVALID,    /* 0x01 */ FD_INVALID,    /* 0x02 */ FD_INVALID,    /* 0x03 */ FD_INVALID,
     /* 0x04 */ FD_VALID,      /* 0x05 */ FD_CHECK_JMP,  /* 0x06 */ FD_INVALID,    /* 0x07 */ FD_VALID,
     /* 0x08 */ FD_INVALID,    /* 0x09 */ FD_INVALID,    /* 0x0a */ FD_INVALID,    /* 0x0b */ FD_INVALID,
@@ -205,13 +212,13 @@ fd_vm_validate( fd_vm_t const * vm ) {
     /* 0x78 */ FD_INVALID,    /* 0x79 */ FD_INVALID,    /* 0x7a */ FD_INVALID,    /* 0x7b */ FD_INVALID,
     /* 0x7c */ FD_VALID,      /* 0x7d */ FD_CHECK_JMP,  /* 0x7e */ FD_VALID,      /* 0x7f */ FD_VALID,
     /* 0x80 */ FD_INVALID,    /* 0x81 */ FD_INVALID,    /* 0x82 */ FD_INVALID,    /* 0x83 */ FD_INVALID,
-    /* 0x84 */ FD_INVALID,    /* 0x85 */ FD_VALID,      /* 0x86 */ FD_VALID,      /* 0x87 */ FD_CHECK_ST,
+    /* 0x84 */ FD_INVALID,    /* 0x85 */ FD_CHECK_CALL_IMM,/*0x86*/FD_VALID,      /* 0x87 */ FD_CHECK_ST,
     /* 0x88 */ FD_INVALID,    /* 0x89 */ FD_INVALID,    /* 0x8a */ FD_INVALID,    /* 0x8b */ FD_INVALID,
-    /* 0x8c */ FD_VALID,      /* 0x8d */ FD_CHECK_CALLX,/* 0x8e */ FD_VALID,      /* 0x8f */ FD_CHECK_ST,
+    /* 0x8c */ FD_VALID,      /* 0x8d */ FD_CHECK_CALL_REG,/*0x8e*/FD_VALID,      /* 0x8f */ FD_CHECK_ST,
     /* 0x90 */ FD_INVALID,    /* 0x91 */ FD_INVALID,    /* 0x92 */ FD_INVALID,    /* 0x93 */ FD_INVALID,
-    /* 0x94 */ FD_INVALID,    /* 0x95 */ FD_VALID,      /* 0x96 */ FD_VALID,      /* 0x97 */ FD_CHECK_ST,
+    /* 0x94 */ FD_INVALID,    /* 0x95 */ FD_CHECK_SYSCALL,/*0x96*/ FD_VALID,      /* 0x97 */ FD_CHECK_ST,
     /* 0x98 */ FD_INVALID,    /* 0x99 */ FD_INVALID,    /* 0x9a */ FD_INVALID,    /* 0x9b */ FD_INVALID,
-    /* 0x9c */ FD_VALID,      /* 0x9d */ FD_INVALID,    /* 0x9e */ FD_VALID,      /* 0x9f */ FD_CHECK_ST,
+    /* 0x9c */ FD_VALID,      /* 0x9d */ FD_VALID,      /* 0x9e */ FD_VALID,      /* 0x9f */ FD_CHECK_ST,
     /* 0xa0 */ FD_INVALID,    /* 0xa1 */ FD_INVALID,    /* 0xa2 */ FD_INVALID,    /* 0xa3 */ FD_INVALID,
     /* 0xa4 */ FD_VALID,      /* 0xa5 */ FD_CHECK_JMP,  /* 0xa6 */ FD_INVALID,    /* 0xa7 */ FD_VALID,
     /* 0xa8 */ FD_INVALID,    /* 0xa9 */ FD_INVALID,    /* 0xaa */ FD_INVALID,    /* 0xab */ FD_INVALID,
@@ -238,8 +245,6 @@ fd_vm_validate( fd_vm_t const * vm ) {
     /* 0xfc */ FD_INVALID,    /* 0xfd */ FD_INVALID,    /* 0xfe */ FD_VALID,      /* 0xff */ FD_INVALID,
   };
 
-  ulong sbpf_version = vm->sbpf_version;
-
   /* SIMD-0173: LDDW */
   validation_map[ 0x18 ] = FD_VM_SBPF_ENABLE_LDDW(sbpf_version) ? FD_CHECK_LDQ : FD_INVALID;
   validation_map[ 0xf7 ] = FD_VM_SBPF_ENABLE_LDDW(sbpf_version) ? FD_INVALID   : FD_VALID; /* HOR64 */
@@ -280,7 +285,7 @@ fd_vm_validate( fd_vm_t const * vm ) {
   validation_map[ 0x9f ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? FD_CHECK_ST  : FD_VALID;
 
   /* SIMD-0173: CALLX */
-  validation_map[ 0x8d ] = FD_VM_SBPF_CALLX_USES_SRC_REG(sbpf_version) ? FD_CHECK_CALLX : FD_CHECK_CALLX_DEPR;
+  validation_map[ 0x8d ] = FD_VM_SBPF_CALLX_USES_SRC_REG(sbpf_version) ? FD_CHECK_CALL_REG : FD_CHECK_CALL_REG_DEPR;
 
   /* SIMD-0174: MUL, DIV, MOD */
   validation_map[ 0x24 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? FD_INVALID : FD_VALID;
@@ -318,6 +323,11 @@ fd_vm_validate( fd_vm_t const * vm ) {
   validation_map[ 0xf6 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? FD_CHECK_DIV : FD_INVALID; /* SREM64 */
   validation_map[ 0xfe ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? FD_VALID     : FD_INVALID;
 
+  /* SIMD-0178: static syscalls */
+  validation_map[ 0x85 ] = FD_VM_SBPF_STATIC_SYSCALLS (sbpf_version) ? FD_CHECK_CALL_IMM : FD_VALID;
+  validation_map[ 0x95 ] = FD_VM_SBPF_STATIC_SYSCALLS (sbpf_version) ? FD_CHECK_SYSCALL : FD_VALID;
+  validation_map[ 0x9d ] = FD_VM_SBPF_STATIC_SYSCALLS (sbpf_version) ? FD_VALID : FD_INVALID;
+
   /* FIXME: These checks are not necessary assuming fd_vm_t is populated by metadata
      generated in fd_sbpf_elf_peek (which performs these checks). But there is no guarantee, and
      this non-guarantee is (rightfully) exploited by the fuzz harnesses.
@@ -345,20 +355,31 @@ fd_vm_validate( fd_vm_t const * vm ) {
 
     case FD_VALID: break;
 
-    case FD_CHECK_JMP: {
+    /* Store ops are special because they allow dreg==r10.
+       We use a special validation_code, used later in the
+       "Check registers" section.
+       But there's nothing to do at this time. */
+    case FD_CHECK_ST: break;
+
+    case FD_CHECK_JMP_V0: {
       long jmp_dst = (long)i + (long)instr.offset + 1L;
       if( FD_UNLIKELY( (jmp_dst<0) | (jmp_dst>=(long)text_cnt)                          ) ) return FD_VM_ERR_JMP_OUT_OF_BOUNDS;
+      //FIXME: this shouldn't be here?
       if( FD_UNLIKELY( fd_sbpf_instr( text[ jmp_dst ] ).opcode.raw==FD_SBPF_OP_ADDL_IMM ) ) return FD_VM_ERR_JMP_TO_ADDL_IMM;
       break;
     }
 
+    case FD_CHECK_JMP_V3: {
+      long jmp_dst = (long)i + (long)instr.offset + 1L;
+      if( FD_UNLIKELY( (jmp_dst<0) | (jmp_dst>=(long)text_cnt) ) ) return FD_VM_ERR_JMP_OUT_OF_BOUNDS;
+      break;
+    }
+
     case FD_CHECK_END: {
       if( FD_UNLIKELY( !((instr.imm==16) | (instr.imm==32) | (instr.imm==64)) ) ) return FD_VM_ERR_INVALID_END_IMM;
       break;
     }
 
-    case FD_CHECK_ST: break; /* FIXME: HMMM ... */
-
     /* https://github.com/solana-labs/rbpf/blob/b503a1867a9cfa13f93b4d99679a17fe219831de/src/verifier.rs#L244 */
     case FD_CHECK_LDQ: {
       /* https://github.com/solana-labs/rbpf/blob/b503a1867a9cfa13f93b4d99679a17fe219831de/src/verifier.rs#L131 */
@@ -376,7 +397,7 @@ fd_vm_validate( fd_vm_t const * vm ) {
     }
 
     case FD_CHECK_DIV: {
-      if( FD_UNLIKELY( instr.imm==0 ) ) return FD_VM_ERR_SIGFPE;  /* FIXME: SIGILL? */
+      if( FD_UNLIKELY( instr.imm==0 ) ) return FD_VM_ERR_SIGFPE;
       break;
     }
 
@@ -391,19 +412,44 @@ fd_vm_validate( fd_vm_t const * vm ) {
     }
 
     /* https://github.com/solana-labs/rbpf/blob/v0.8.5/src/verifier.rs#L207 */
-    case FD_CHECK_CALLX: {
+    case FD_CHECK_CALL_REG: {
       if( FD_UNLIKELY( instr.src_reg > 9 ) ) {
         return FD_VM_ERR_INVALID_REG;
       }
       break;
     }
-    case FD_CHECK_CALLX_DEPR: {
+    case FD_CHECK_CALL_REG_DEPR: {
       if( FD_UNLIKELY( instr.imm > 9 ) ) {
         return FD_VM_ERR_INVALID_REG;
       }
       break;
     }
 
+    /* https://github.com/solana-labs/rbpf/blob/4ad935be/src/verifier.rs#L411-L418 */
+    case FD_CHECK_CALL_IMM: {
+      ulong target_pc = (ulong)( fd_long_sat_add( (long)i, fd_long_sat_add( (long)(int)instr.imm, 1 ) ) );
+      if( FD_UNLIKELY( target_pc>text_cnt || !fd_sbpf_calldests_test( vm->calldests, target_pc ) ) ) {
+        return FD_VM_INVALID_FUNCTION;
+      }
+      break;
+    }
+
+    /* https://github.com/solana-labs/rbpf/blob/4ad935be/src/verifier.rs#L423-L428 */
+    case FD_CHECK_SYSCALL: {
+      uint imm = instr.imm;
+      /* check out of bound */
+      if( FD_UNLIKELY( imm==0 || imm >= FD_VM_SBPF_STATIC_SYSCALLS_LIST_SZ ) ) {
+        return FD_VM_INVALID_SYSCALL;
+      }
+      uint syscall_key = FD_VM_SBPF_STATIC_SYSCALLS_LIST[ imm ];
+      /* check active syscall */
+      fd_sbpf_syscalls_t const * syscall = fd_sbpf_syscalls_query_const( vm->syscalls, syscall_key, NULL );
+      if( FD_UNLIKELY( !syscall ) ) {
+        return FD_VM_INVALID_SYSCALL;
+      }
+      break;
+    }
+
     case FD_INVALID: default: return FD_VM_ERR_INVALID_OPCODE;
     }
 
diff --git a/src/flamenco/vm/fd_vm_base.h b/src/flamenco/vm/fd_vm_base.h
index 0e8f2f91e1..51361abea7 100644
--- a/src/flamenco/vm/fd_vm_base.h
+++ b/src/flamenco/vm/fd_vm_base.h
@@ -75,6 +75,8 @@
 #define FD_VM_ERR_BAD_TEXT          (-36) /* detected a bad text section (overflow, outside rodata boundary, etc.,)*/
 #define FD_VM_SH_OVERFLOW           (-37) /* detected a shift overflow, equivalent to VeriferError::ShiftWithOverflow */
 #define FD_VM_TEXT_SZ_UNALIGNED     (-38) /* detected a text section that is not a multiple of 8 */
+#define FD_VM_INVALID_FUNCTION      (-39) /* detected an invalid function */
+#define FD_VM_INVALID_SYSCALL       (-40) /* detected an invalid syscall */
 
 /* Syscall Errors
    https://github.com/anza-xyz/agave/blob/v2.0.7/programs/bpf_loader/src/syscalls/mod.rs#L81 */
diff --git a/src/flamenco/vm/fd_vm_interp_core.c b/src/flamenco/vm/fd_vm_interp_core.c
index fdf4bb41f5..a207f5bc85 100644
--- a/src/flamenco/vm/fd_vm_interp_core.c
+++ b/src/flamenco/vm/fd_vm_interp_core.c
@@ -116,6 +116,11 @@
   interp_jump_table[ 0x14 ] = FD_VM_SBPF_SWAP_SUB_REG_IMM_OPERANDS(sbpf_version) ? &&interp_0x14 : &&interp_0x14depr;
   interp_jump_table[ 0x17 ] = FD_VM_SBPF_SWAP_SUB_REG_IMM_OPERANDS(sbpf_version) ? &&interp_0x17 : &&interp_0x17depr;
 
+  /* SIMD-0178: static syscalls */
+  interp_jump_table[ 0x85 ] = FD_VM_SBPF_STATIC_SYSCALLS (sbpf_version) ? &&interp_0x85 : &&interp_0x85depr;
+  interp_jump_table[ 0x95 ] = FD_VM_SBPF_STATIC_SYSCALLS (sbpf_version) ? &&interp_0x95 : &&interp_0x9d;
+  interp_jump_table[ 0x9d ] = FD_VM_SBPF_STATIC_SYSCALLS (sbpf_version) ? &&interp_0x9d : &&sigill;
+
   /* Unpack the VM state */
 
   ulong pc        = vm->pc;
@@ -179,6 +184,76 @@
   reg_src = reg[ src ];                  /* Guaranteed in-bounds */                              \
   goto *interp_jump_table[ opcode ]      /* Guaranteed in-bounds */
 
+/* FD_VM_INTERP_SYSCALL_EXEC
+   (macro to handle the logic of 0x85 pre- and post- SIMD-0178: static syscalls)
+
+   Setup.
+   Update the vm with the current vm execution state for the
+   syscall.  Note that BRANCH_BEGIN has pc at the syscall and
+   already updated ic and cu to reflect all instructions up to
+   and including the syscall instruction itself.
+
+   Execution.
+   Do the syscall.  We use ret reduce the risk of the syscall
+   accidentally modifying other registers (note however since a
+   syscall has the vm handle it still do arbitrary modifications
+   to the vm state) and the risk of a pointer escape on reg from
+   inhibiting compiler optimizations (this risk is likely low in
+   as this is the only point in the whole interpreter core that
+   calls outside this translation unit).
+   At this point, vm->cu is positive.
+
+   Error handling.
+   If we trust syscall implementations to handle the vm state
+   correctly, the below could be implemented as unpacking the vm
+   state and jumping to sigsys on error.  But we provide some
+   extra protection to make various strong guarantees:
+
+   - We do not let the syscall modify pc currently as nothing
+     requires this and it reduces risk of a syscall bug mucking
+     up the interpreter.  If there ever was a syscall that
+     needed to modify the pc (e.g. a syscall that has execution
+     resume from a different location than the instruction
+     following the syscall), do "pc = vm->pc" below.
+
+   - We do not let the syscall modify ic currently as nothing
+     requires this and it keeps the ic precise.  If a future
+     syscall needs this, do "ic = vm->ic" below.
+
+   - We do not let the syscall increase cu as nothing requires
+     this and it guarantees the interpreter will halt in a
+     reasonable finite amount of time.  If a future syscall
+     needs this, do "cu = vm->cu" below.
+
+   - A syscall that returns SIGCOST is always treated as though
+     it also zerod cu.
+
+   At this point, vm->cu is whatever the syscall tried to set
+   and cu is positive.
+
+   Exit
+   At this point, cu is positive and err is clear.
+*/
+# define FD_VM_INTERP_SYSCALL_EXEC                                          \
+  /* Setup */                                                               \
+  vm->pc        = pc;                                                       \
+  vm->ic        = ic;                                                       \
+  vm->cu        = cu;                                                       \
+  vm->frame_cnt = frame_cnt;                                                \
+  /* Execution */                                                           \
+  ulong ret[1];                                                             \
+  err = syscall->func( vm, reg[1], reg[2], reg[3], reg[4], reg[5], ret );   \
+  reg[0] = ret[0];                                                          \
+  /* Error handling */                                                      \
+  ulong cu_req = vm->cu;                                                    \
+  cu = fd_ulong_min( cu_req, cu );                                          \
+  if( FD_UNLIKELY( err ) ) {                                                \
+    if( err==FD_VM_ERR_SIGCOST ) cu = 0UL; /* cmov */                       \
+    goto sigsyscall;                                                        \
+  }                                                                         \
+  /* Exit */
+
+
   /* FD_VM_INTERP_INSTR_BEGIN / FD_VM_INTERP_INSTR_END bracket opcode's
      implementation for an opcode that does not branch.  On entry, the
      instruction word has been unpacked into dst / src / offset / imm
@@ -657,6 +732,12 @@
   FD_VM_INTERP_INSTR_END;
 
   FD_VM_INTERP_BRANCH_BEGIN(0x85) /* FD_SBPF_OP_CALL_IMM */
+    /* imm has already been validated */
+    FD_VM_INTERP_STACK_PUSH;
+    pc = (ulong)( (long)pc + (long)(int)imm );
+  FD_VM_INTERP_BRANCH_END;
+
+  FD_VM_INTERP_BRANCH_BEGIN(0x85depr) { /* FD_SBPF_OP_CALL_IMM */
 
     fd_sbpf_syscalls_t const * syscall = fd_sbpf_syscalls_query_const( syscalls, imm, NULL );
     if( FD_UNLIKELY( !syscall ) ) { /* Optimize for the syscall case */
@@ -682,22 +763,19 @@
           is a best-effort implementation that should match the VM state
           in all ways except error code. */
 
-      /* Note the original implementation had the imm magic number
-          check after the calldest check.  But fd_pchash_inverse of the
-          magic number is 0xb00c380U.  This is beyond possible text_cnt
-          values.  So we do it first to simplify the code and clean up
-          fault handling. */
-
-      if( FD_UNLIKELY( imm==0x71e3cf81U ) ){
+      /* Special case to handle entrypoint.
+         ebpf::hash_symbol_name(b"entrypoint") = 0xb00c380, and
+         fd_pchash_inverse( 0xb00c380U ) = 0x71e3cf81U */
+      if( FD_UNLIKELY( imm==0x71e3cf81U ) ) {
         FD_VM_INTERP_STACK_PUSH;
-        pc = entry_pc; /* FIXME: MAGIC NUMBER */
+        pc = entry_pc - 1;
       } else {
-        
+
         ulong target_pc = (ulong)fd_pchash_inverse( imm );
         if( FD_UNLIKELY( target_pc>text_cnt ) ){
           /* ...to match state of Agave VM when faulting
-             Note: this check MUST be BEFORE fd_sbpf_calldests_test,
-             because it prevents overflowing calldests. */
+              Note: this check MUST be BEFORE fd_sbpf_calldests_test,
+              because it prevents overflowing calldests. */
           FD_VM_INTERP_STACK_PUSH;
           goto sigtextbr;
         }
@@ -707,74 +785,15 @@
         }
 
         FD_VM_INTERP_STACK_PUSH;
-        pc = target_pc;
+        pc = target_pc - 1;
       }
-      pc--;
 
     } else {
 
-      /* Update the vm with the current vm execution state for the
-          syscall.  Note that BRANCH_BEGIN has pc at the syscall and
-          already updated ic and cu to reflect all instructions up to
-          and including the syscall instruction itself. */
-
-      vm->pc        = pc;
-      vm->ic        = ic;
-      vm->cu        = cu;
-      vm->frame_cnt = frame_cnt;
-
-      /* Do the syscall.  We use ret reduce the risk of the syscall
-          accidentally modifying other registers (note however since a
-          syscall has the vm handle it still do arbitrary modifications
-          to the vm state) and the risk of a pointer escape on reg from
-          inhibiting compiler optimizations (this risk is likely low in
-          as this is the only point in the whole interpreter core that
-          calls outside this translation unit). */
-
-      /* At this point, vm->cu is positive */
-
-      ulong ret[1];
-      err = syscall->func( vm, reg[1], reg[2], reg[3], reg[4], reg[5], ret );
-      reg[0] = ret[0];
-
-      /* If we trust syscall implementations to handle the vm state
-          correctly, the below could be implemented as unpacking the vm
-          state and jumping to sigsys on error.  But we provide some
-          extra protection to make various strong guarantees:
-
-          - We do not let the syscall modify pc currently as nothing
-            requires this and it reduces risk of a syscall bug mucking
-            up the interpreter.  If there ever was a syscall that
-            needed to modify the pc (e.g. a syscall that has execution
-            resume from a different location than the instruction
-            following the syscall), do "pc = vm->pc" below.
-
-          - We do not let the syscall modify ic currently as nothing
-            requires this and it keeps the ic precise.  If a future
-            syscall needs this, do "ic = vm->ic" below.
-
-          - We do not let the syscall increase cu as nothing requires
-            this and it guarantees the interpreter will halt in a
-            reasonable finite amount of time.  If a future syscall
-            needs this, do "cu = vm->cu" below.
-
-          - A syscall that returns SIGCOST is always treated as though
-            it also zerod cu. */
-
-      /* At this point, vm->cu is whatever the syscall tried to set
-          and cu is positive */
-
-      ulong cu_req = vm->cu;
-      cu = fd_ulong_min( cu_req, cu );
-      if( FD_UNLIKELY( err ) ) {
-        if( err==FD_VM_ERR_SIGCOST ) cu = 0UL; /* cmov */
-        goto sigsyscall;
-      }
+      FD_VM_INTERP_SYSCALL_EXEC;
 
-      /* At this point, cu is positive and err is clear */
     }
-
-  FD_VM_INTERP_BRANCH_END;
+  } FD_VM_INTERP_BRANCH_END;
 
   FD_VM_INTERP_INSTR_BEGIN(0x86) /* FD_SBPF_OP_LMUL32_IMM */
     reg[ dst ] = (ulong)( (uint)reg_dst * imm );
@@ -861,21 +880,20 @@
     reg[ dst ] = (ulong)( (uint)reg_dst % imm );
   FD_VM_INTERP_INSTR_END;
 
-  FD_VM_INTERP_BRANCH_BEGIN(0x95) /* FD_SBPF_OP_EXIT */
-      /* Agave JIT VM exit implementation analysis below.
+  FD_VM_INTERP_BRANCH_BEGIN(0x95) { /* FD_SBPF_OP_SYSCALL */
+    /* imm has already been validated to not overflow */
+    uint syscall_key = FD_VM_SBPF_STATIC_SYSCALLS_LIST[ imm ];
+    fd_sbpf_syscalls_t const * syscall = fd_sbpf_syscalls_query_const( syscalls, syscall_key, NULL );
 
-       Agave references:
-       https://github.com/solana-labs/rbpf/blob/v0.8.5/src/interpreter.rs#L503-L509
-       https://github.com/solana-labs/rbpf/blob/v0.8.5/src/jit.rs#L697-L702 */
-    if( FD_UNLIKELY( !frame_cnt ) ) goto sigexit; /* Exit program */
-    frame_cnt--;
-    reg[6]   = shadow[ frame_cnt ].r6;
-    reg[7]   = shadow[ frame_cnt ].r7;
-    reg[8]   = shadow[ frame_cnt ].r8;
-    reg[9]   = shadow[ frame_cnt ].r9;
-    reg[10]  = shadow[ frame_cnt ].r10;
-    pc       = shadow[ frame_cnt ].pc;
-  FD_VM_INTERP_BRANCH_END;
+    /* this check is probably useless, as validation includes checking that the
+       syscall is active in this epoch.
+       However, it's safe to keep it here, because at the time of writing this
+       code we're not (re)validating all programs at every new epoch. */
+    if( FD_UNLIKELY( !syscall ) ) goto sigill;
+
+    FD_VM_INTERP_SYSCALL_EXEC;
+
+  } FD_VM_INTERP_BRANCH_END;
 
   FD_VM_INTERP_INSTR_BEGIN(0x96) /* FD_SBPF_OP_LMUL64_IMM */
     reg[ dst ] = reg_dst * (ulong)(long)(int)imm;
@@ -901,6 +919,22 @@
   }
   FD_VM_INTERP_INSTR_END;
 
+  FD_VM_INTERP_BRANCH_BEGIN(0x9d) /* FD_SBPF_OP_EXIT */
+      /* Agave JIT VM exit implementation analysis below.
+
+       Agave references:
+       https://github.com/solana-labs/rbpf/blob/v0.8.5/src/interpreter.rs#L503-L509
+       https://github.com/solana-labs/rbpf/blob/v0.8.5/src/jit.rs#L697-L702 */
+    if( FD_UNLIKELY( !frame_cnt ) ) goto sigexit; /* Exit program */
+    frame_cnt--;
+    reg[6]   = shadow[ frame_cnt ].r6;
+    reg[7]   = shadow[ frame_cnt ].r7;
+    reg[8]   = shadow[ frame_cnt ].r8;
+    reg[9]   = shadow[ frame_cnt ].r9;
+    reg[10]  = shadow[ frame_cnt ].r10;
+    pc       = shadow[ frame_cnt ].pc;
+  FD_VM_INTERP_BRANCH_END;
+
   FD_VM_INTERP_INSTR_BEGIN(0x9e) /* FD_SBPF_OP_LMUL64_REG */
     reg[ dst ] = reg_dst * reg_src;
   FD_VM_INTERP_INSTR_END;
diff --git a/src/flamenco/vm/fd_vm_private.h b/src/flamenco/vm/fd_vm_private.h
index 8999bcbc5c..f49bac38d4 100644
--- a/src/flamenco/vm/fd_vm_private.h
+++ b/src/flamenco/vm/fd_vm_private.h
@@ -85,6 +85,8 @@ typedef struct fd_vm_vec fd_vm_vec_t;
    and create a huge mess.
    We define both, so hopefully it's foolproof. */
 
+#define FD_VM_SBPF_REJECT_RODATA_STACK_OVERLAP(v)  ( v != FD_SBPF_V0 )
+#define FD_VM_SBPF_ENABLE_ELF_VADDR(v)             ( v != FD_SBPF_V0 )
 /* SIMD-0166 */
 #define FD_VM_SBPF_DYNAMIC_STACK_FRAMES(v)         ( v >= FD_SBPF_V1 )
 /* SIMD-0173 */
@@ -100,17 +102,103 @@ typedef struct fd_vm_vec fd_vm_vec_t;
 #define FD_VM_SBPF_ENABLE_NEG(v)                   ( v <  FD_SBPF_V2 )
 #define FD_VM_SBPF_SWAP_SUB_REG_IMM_OPERANDS(v)    ( v >= FD_SBPF_V2 )
 #define FD_VM_SBPF_EXPLICIT_SIGN_EXT(v)            ( v >= FD_SBPF_V2 )
-/* SIMD-0176 */
+/* SIMD-0178 + SIMD-0179 */
 #define FD_VM_SBPF_STATIC_SYSCALLS(v)              ( v >= FD_SBPF_V3 )
-/* SIMD-XXXX */
-#define FD_VM_SBPF_STRICTER_CONTROLFLOW(v)         ( v >= FD_SBPF_V3 )
-#define FD_VM_SBPF_REJECT_RODATA_STACK_OVERLAP(v)  ( v >= FD_SBPF_V3 )
-#define FD_VM_SBPF_ENABLE_ELF_VADDR(v)             ( v >= FD_SBPF_V3 )
+/* SIMD-0189 */
+#define FD_VM_SBPF_ENABLE_STRICTER_ELF_HEADERS(v)  ( v >= FD_SBPF_V3 )
+#define FD_VM_SBPF_ENABLE_LOWER_BYTECODE_VADDR(v)  ( v >= FD_SBPF_V3 )
 
 #define FD_VM_SBPF_DYNAMIC_STACK_FRAMES_ALIGN      (64U)
 
 #define FD_VM_OFFSET_MASK (0xffffffffUL)
 
+static const uint FD_VM_SBPF_STATIC_SYSCALLS_LIST[] = {
+  0,
+  //  1 = abort
+  0xb6fc1a11,
+  //  2 = sol_panic_
+  0x686093bb,
+  //  3 = sol_memcpy_
+  0x717cc4a3,
+  //  4 = sol_memmove_
+  0x434371f8,
+  //  5 = sol_memset_
+  0x3770fb22,
+  //  6 = sol_memcmp_
+  0x5fdcde31,
+  //  7 = sol_log_
+  0x207559bd,
+  //  8 = sol_log_64_
+  0x5c2a3178,
+  //  9 = sol_log_pubkey
+  0x7ef088ca,
+  // 10 = sol_log_compute_units_
+  0x52ba5096,
+  // 11 = sol_alloc_free_
+  0x83f00e8f,
+  // 12 = sol_invoke_signed_c
+  0xa22b9c85,
+  // 13 = sol_invoke_signed_rust
+  0xd7449092,
+  // 14 = sol_set_return_data
+  0xa226d3eb,
+  // 15 = sol_get_return_data
+  0x5d2245e4,
+  // 16 = sol_log_data
+  0x7317b434,
+  // 17 = sol_sha256
+  0x11f49d86,
+  // 18 = sol_keccak256
+  0xd7793abb,
+  // 19 = sol_secp256k1_recover
+  0x17e40350,
+  // 20 = sol_blake3
+  0x174c5122,
+  // 21 = sol_poseidon
+  0xc4947c21,
+  // 22 = sol_get_processed_sibling_instruction
+  0xadb8efc8,
+  // 23 = sol_get_stack_height
+  0x85532d94,
+  // 24 = sol_curve_validate_point
+  0xaa2607ca,
+  // 25 = sol_curve_group_op
+  0xdd1c41a6,
+  // 26 = sol_curve_multiscalar_mul
+  0x60a40880,
+  // 27 = sol_curve_pairing_map
+  0xf111a47e,
+  // 28 = sol_alt_bn128_group_op
+  0xae0c318b,
+  // 29 = sol_alt_bn128_compression
+  0x334fd5ed,
+  // 30 = sol_big_mod_exp
+  0x780e4c15,
+  // 31 = sol_remaining_compute_units
+  0xedef5aee,
+  // 32 = sol_create_program_address
+  0x9377323c,
+  // 33 = sol_try_find_program_address
+  0x48504a38,
+  // 34 = sol_get_sysvar
+  0x13c1b505,
+  // 35 = sol_get_epoch_stake
+  0x5be92f4a,
+  // 36 = sol_get_clock_sysvar
+  0xd56b5fe9,
+  // 37 = sol_get_epoch_schedule_sysvar
+  0x23a29a61,
+  // 38 = sol_get_last_restart_slot
+  0x188a0031,
+  // 39 = sol_get_epoch_rewards_sysvar
+  0xfdba2b3b,
+  // 40 = sol_get_fees_sysvar
+  0x3b97b73c,
+  // 41 = sol_get_rent_sysvar
+  0xbf7188f6,
+};
+#define FD_VM_SBPF_STATIC_SYSCALLS_LIST_SZ (sizeof(FD_VM_SBPF_STATIC_SYSCALLS_LIST) / sizeof(uint))
+
 FD_PROTOTYPES_BEGIN
 
 /* Log error within the instr_ctx to match Agave/Rust error. */
diff --git a/src/flamenco/vm/test_vm_interp.c b/src/flamenco/vm/test_vm_interp.c
index feb15452f4..08afe8183d 100644
--- a/src/flamenco/vm/test_vm_interp.c
+++ b/src/flamenco/vm/test_vm_interp.c
@@ -290,6 +290,60 @@ test_0cu_exit( void ) {
   fd_sha256_delete( fd_sha256_leave( sha ) );
 }
 
+static void
+test_static_syscalls_list( void ) {
+  const char *static_syscalls_from_simd[] = {
+    "abort",
+    "sol_panic_",
+    "sol_memcpy_",
+    "sol_memmove_",
+    "sol_memset_",
+    "sol_memcmp_",
+    "sol_log_",
+    "sol_log_64_",
+    "sol_log_pubkey",
+    "sol_log_compute_units_",
+    "sol_alloc_free_",
+    "sol_invoke_signed_c",
+    "sol_invoke_signed_rust",
+    "sol_set_return_data",
+    "sol_get_return_data",
+    "sol_log_data",
+    "sol_sha256",
+    "sol_keccak256",
+    "sol_secp256k1_recover",
+    "sol_blake3",
+    "sol_poseidon",
+    "sol_get_processed_sibling_instruction",
+    "sol_get_stack_height",
+    "sol_curve_validate_point",
+    "sol_curve_group_op",
+    "sol_curve_multiscalar_mul",
+    "sol_curve_pairing_map",
+    "sol_alt_bn128_group_op",
+    "sol_alt_bn128_compression",
+    "sol_big_mod_exp",
+    "sol_remaining_compute_units",
+    "sol_create_program_address",
+    "sol_try_find_program_address",
+    "sol_get_sysvar",
+    "sol_get_epoch_stake",
+    "sol_get_clock_sysvar",
+    "sol_get_epoch_schedule_sysvar",
+    "sol_get_last_restart_slot",
+    "sol_get_epoch_rewards_sysvar",
+    "sol_get_fees_sysvar",
+    "sol_get_rent_sysvar",
+  };
+
+  FD_TEST( FD_VM_SBPF_STATIC_SYSCALLS_LIST[0]==0 );
+  for( ulong i=1; i<FD_VM_SBPF_STATIC_SYSCALLS_LIST_SZ; i++ ) {
+    const char *name = static_syscalls_from_simd[i-1];
+    uint key = fd_murmur3_32( name, strlen(name), 0 );
+    FD_TEST( FD_VM_SBPF_STATIC_SYSCALLS_LIST[i]==key );
+  }
+}
+
 static fd_sbpf_syscalls_t _syscalls[ FD_SBPF_SYSCALLS_SLOT_CNT ];
 
 int
@@ -958,6 +1012,8 @@ main( int     argc,
   fd_sbpf_syscalls_delete( fd_sbpf_syscalls_leave( syscalls ) );
   test_vm_exec_instr_ctx_delete( instr_ctx );
 
+  test_static_syscalls_list();
+
   FD_LOG_NOTICE(( "pass" ));
   fd_rng_delete( fd_rng_leave( rng ) );
   fd_halt();
diff --git a/src/util/bits/fd_sat.h b/src/util/bits/fd_sat.h
index 87035cbe45..9c85a2edef 100644
--- a/src/util/bits/fd_sat.h
+++ b/src/util/bits/fd_sat.h
@@ -59,6 +59,27 @@ fd_ulong_sat_sub( ulong x, ulong y ) {
   return fd_ulong_if( cf, 0UL, res );
 }
 
+FD_FN_CONST static inline long
+fd_long_sat_add( long x, long y ) {
+  long res;
+  int cf = __builtin_saddl_overflow ( x, y, &res );
+  /* https://stackoverflow.com/a/56531252
+     x + y overflows => x, y have the same sign
+     we can use either to determine the result,
+     with the trick described in the SO answe.
+     We chose x because it works also for sub. */
+  return fd_long_if( cf, (long)((ulong)x >> 63) + LONG_MAX, res );
+}
+
+FD_FN_CONST static inline long
+fd_long_sat_sub( long x, long y ) {
+  long res;
+  int cf = __builtin_ssubl_overflow ( x, y, &res );
+  return fd_long_if( cf, (long)((ulong)x >> 63) + LONG_MAX, res );
+}
+
+/* fd_long_sat_mul is left as an exercise to the reader */
+
 FD_FN_CONST static inline uint
 fd_uint_sat_add( uint x, uint y ) {
   uint res;
diff --git a/src/util/bits/test_sat.c b/src/util/bits/test_sat.c
index fa503688a1..b6834ccfcb 100644
--- a/src/util/bits/test_sat.c
+++ b/src/util/bits/test_sat.c
@@ -90,6 +90,36 @@ fd_ulong_sat_mul_ref( ulong x,
   }
 }
 
+static inline long
+fd_long_sat_add_ref( long x,
+                     long y ) {
+  int128 ref = x;
+  ref += y;
+
+  if( ref > LONG_MAX ) {
+    return LONG_MAX;
+  } else if( ref < LONG_MIN ) {
+    return LONG_MIN;
+  } else {
+    return (long) ref;
+  }
+}
+
+static inline long
+fd_long_sat_sub_ref( long x,
+                     long y ) {
+  int128 ref = x;
+  ref -= y;
+
+  if( ref > LONG_MAX ) {
+    return LONG_MAX;
+  } else if( ref < LONG_MIN ) {
+    return LONG_MIN;
+  } else {
+    return (long) ref;
+  }
+}
+
 static inline uint
 fd_uint_sat_add_ref( uint x,
                        uint y ) {
@@ -183,6 +213,24 @@ main( int     argc,
 
 #   undef TEST
 
+#   define TEST(op,x,y)                                   \
+    do {                                              \
+      long ref   = fd_long_sat_##op##_ref ( x, y ); \
+      long res   = fd_long_sat_##op ( x, y );    \
+      if( ref != res ) { \
+        FD_LOG_ERR(( "FAIL: fd_long_sat_" #op " x %ld y %ld ref %ld res %ld", (long)x, (long)y, ref, res )); \
+      } \
+    } while(0)
+
+    TEST(add,0,LONG_MAX);
+    TEST(add,LONG_MAX,10);
+    TEST(add,LONG_MAX - 10,LONG_MAX - 10);
+    TEST(sub,0,LONG_MAX);
+    TEST(add,LONG_MAX,10);
+    TEST(sub,LONG_MAX - 10,LONG_MAX - 10);
+
+#   undef TEST
+
 #   define TEST(op,x,y)                                \
     do {                                               \
       uint ref   = fd_uint_sat_##op##_ref ( x, y ); \
@@ -259,6 +307,23 @@ main( int     argc,
 
 #   undef TEST
 
+#   define TEST(op)                                  \
+    do {                                             \
+      uint  t =  fd_rng_uint ( rng );                             \
+      long x  = (long)make_test_rand_ulong( fd_rng_ulong( rng ), &t ); \
+      long y  = (long)make_test_rand_ulong( fd_rng_ulong( rng ), &t ); \
+      long ref   = fd_long_sat_##op##_ref ( x, y ); \
+      long res   = fd_long_sat_##op       ( x, y ); \
+      if( ref != res ) { \
+        FD_LOG_ERR(( "FAIL: %i fd_long_sat_" #op " x %ld y %ld ref %ld res %ld", i, (long)x, (long)y, ref, res )); \
+      } \
+    } while(0)
+
+    TEST(add);
+    TEST(sub);
+
+#   undef TEST
+
 #   define TEST(op)                                  \
     do {                                             \
       uint t =  fd_rng_uint ( rng );                \