diff --git a/espresso/docs/hacking.md b/espresso/docs/hacking.md index e25c5585fec5..dafde8b46981 100644 --- a/espresso/docs/hacking.md +++ b/espresso/docs/hacking.md @@ -139,6 +139,20 @@ $ mx espresso --java.JavaHome=/path/to/java/8/home -version $ mx espresso --java.JavaHome=/path/to/java/11/home -version ``` +## No-Native Espresso + +To run Espresso without native access, use the experimental option `java.NativeBackend=no-native` via the command line: +```bash +$ mx espresso --experimental-options --java.NativeBackend=no-native +``` + +or on the context builder: +```java +builder.allowExperimentalOptions(true).option("java.NativeBackend", "no-native") +``` + +Disabling native access enhances security guarantees and sandboxing capabilities. In this mode, substitutions are used for Java's standard libraries, and virtualized memory will be provided (GR-70643). However, some functionality might be limited (e.g. you will have no access to LibAWT). + ## Limitations ### Linux diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java index 4c6fbd764620..a990b210ed0a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java @@ -266,8 +266,8 @@ private void initializeOptions(final TruffleLanguage.Env env) { case compact -> new CompactGuestFieldOffsetStrategy(); case graal -> new GraalGuestFieldOffsetStrategy(); }; - this.useEspressoLibs = env.getOptions().get(EspressoOptions.UseEspressoLibs); this.nativeBackendId = setNativeBackendId(env); + this.useEspressoLibs = setUseEspressoLibs(env); assert guestFieldOffsetStrategy.name().equals(strategy.name()); } @@ -355,6 +355,20 @@ private static String setNativeBackendId(final TruffleLanguage.Env env) { return nativeBackend; } + private boolean setUseEspressoLibs(final TruffleLanguage.Env env) { + // For no-native we turn on espressoLibs by default + boolean flagSet = env.getOptions().hasBeenSet(EspressoOptions.UseEspressoLibs); + boolean userFlag = env.getOptions().get(EspressoOptions.UseEspressoLibs); + if (nativeBackendId.equals(NoNativeAccess.Provider.ID)) { + if (flagSet && !userFlag) { + throw EspressoError.fatal("You should not set UseEspressoLibs to false with no-native backend!"); + } + return true; + } else { + return userFlag; + } + } + @Override protected boolean patchContext(EspressoContext context, Env newEnv) { // This check has to be done manually as long as language uses exclusive context sharing diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java index 22cc6fc3b32e..ab057f722b52 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java @@ -620,13 +620,16 @@ public Long apply(String size) { usageSyntax = "") // public static final OptionKey> VMArguments = OptionKey.mapOf(String.class); - @Option(help = "Native backend used by Espresso, if not specified, Espresso will pick one depending on the environment.", // + @Option(help = "Native backend used by Espresso, if not specified, Espresso will pick one depending on the environment.\\n" + + "If native access is disabled, native methods will be emulated in Java with limited functionality.\\n" + + "For example there will be no access to LibAWT.", // category = OptionCategory.EXPERT, // stability = OptionStability.EXPERIMENTAL, // usageSyntax = "") // public static final OptionKey NativeBackend = new OptionKey<>(""); @Option(help = "Enable use of a custom Espresso implementation of boot libraries, which allows for not entering native code.\\n" + + "Will be automatically enabled if there is NO native access.\\n" + "For example, this will replace the usual 'libjava'. Missing implementations will thus fail with 'UnsatifiedLinkError'.", // category = OptionCategory.EXPERT, // stability = OptionStability.EXPERIMENTAL, // diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/EspressoLibsNativeAccess.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/EspressoLibsNativeAccess.java index 802752bb6e1a..44d954ce1bd9 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/EspressoLibsNativeAccess.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/EspressoLibsNativeAccess.java @@ -45,13 +45,25 @@ /** * Uses Espresso's implementation of standard JDK libraries, falling back to a delegate if the - * implementation is missing. - *

- * This can be used in conjunction with option {@code 'java.GuestNativeAccess=no-native'} or - * {@code env.isNativeAccessAllowed() == false} to prevent guest code from accessing the native - * world, but still allowing the java standard library to work. + * implementation is missing. Espresso Libs is automatically enabled if native access is disabled + * (e.g., {@code java.GuestNativeAccess=no-native} or {@code env.isNativeAccessAllowed() == false}). + * + * Key implementation details: + *

    + *
  • Startup libraries (libnespresso, libjvm, libjimage) are substituted to prevent initialization + * of native environments such as {@link com.oracle.truffle.espresso.jni.JniEnv} .
  • + *
  • A custom guest FileSystem is backed by Truffle's VFS (see + * sun.nio.fs.TruffleFileSystemProvider). The semantics of the FileSystem is implemented in + * {@link com.oracle.truffle.espresso.io.TruffleIO} on the host side.
  • + *
  • Network functionality is implemented using host sockets, relying on + * {@code env.isSocketIOAllowed() + * == true}.
  • + *
  • Guest memory will be virtualized with (GR-70643).
  • + *
* * @see com.oracle.truffle.espresso.libs.Libs + * @see com.oracle.truffle.espresso.io.TruffleIO + * @see com.oracle.truffle.espresso.libs.LibsState */ public class EspressoLibsNativeAccess extends ContextAccessImpl implements NativeAccess { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/TruffleIO.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/TruffleIO.java index 888d11185d06..9488629b7c26 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/TruffleIO.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/TruffleIO.java @@ -90,8 +90,25 @@ import com.oracle.truffle.espresso.substitutions.JavaType; /** - * This class manages the set of file descriptors of a context. File descriptors are associated with - * {@link String} paths and {@link Channel}s, their capabilities depending on the kind of channel. + * Provides IO functionality in EspressoLibs mode (see + * {@link com.oracle.truffle.espresso.ffi.EspressoLibsNativeAccess}). This requires managing the set + * of file descriptors of a context. + *

+ * This class plays a crucial role in EspressoLibs mode. Every guest channel gets associated with a + * FileDescriptors which links to the corresponding host channel here {@link TruffleIO#files}. In + * substitutions of native IO methods we receive the FileDescriptor as an argument which is then + * used to retrieve the corresponding host channel. Then we can easily implement the semantics of + * the guest channel's native methods using this host channel. For example see + * {@link TruffleIO#readBytes(int, ByteBuffer)}. + *

+ * For file IO the host channels is created over the Truffle API + * {@link TruffleFile#newByteChannel(Set, FileAttribute[])} making this class practically a binding + * layer between the guest file system (see sun.nio.fs.TruffleFileSystemProvider) and Truffle's + * Virtual File System (see {@link org.graalvm.polyglot.io.FileSystem}). + *

+ * For socket IO, file descriptors are associated with host network channels (see + * {@link #openSocket(boolean, boolean, boolean, boolean)}). + *

* Adapted from GraalPy's PosixResources. */ public final class TruffleIO implements ContextAccess { @@ -166,6 +183,9 @@ public final class TruffleIO implements ContextAccess { * Context-local file-descriptor mappings. */ private final EspressoContext context; + /** + * The mapping between guest FileDescriptors and host channels. + */ private final Map files; // 0, 1 and 2 are reserved for standard streams. diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java index 65eb262c4b08..236e4b1edd00 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java @@ -46,6 +46,10 @@ import com.oracle.truffle.espresso.vm.InterpreterToVM; import com.oracle.truffle.espresso.vm.Management; +/** + * Class for maintaining state and providing utility in EspressoLibs mode. See + * {@link com.oracle.truffle.espresso.ffi.EspressoLibsNativeAccess} + */ public class LibsState { private static final TruffleLogger logger = TruffleLogger.getLogger(EspressoLanguage.ID, LibsState.class);