One doesn't have to *use* the FFI. If the FFI isn't exposed via a primitive then no FFI. One can still have named primitives supported by the image and not by the VM and not use the FFI. To call a named primitive in a primitive plugin the following sequence occurs:
the method containing a named primitive spec is activated and the primitive call fails because its function pointer is null.
the failure code extracts the plugin name and invokes a primitive to load the plugin library
the failure code extracts the primitive name and uses the lookup primitive to find the function in the loaded plugin library
the failure code uses a primitive to slam the function pointer into the method
the failure code uses the executeMethodWithArgs primitive to retry the bound named primitive method
So the FFI is an optional extra. One needs four primitives, load library, lookup name in library, insert primitive function pointer. and executemethodWithArgs (thanks Tim!). Slamming the function into the method could also be done using, say, objectAt:.
So one can still have a nice small safe VM and have no direct support for named primitives in the VM.