Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions espresso/docs/hacking.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -620,13 +620,16 @@ public Long apply(String size) {
usageSyntax = "<argument>") //
public static final OptionKey<OptionMap<String>> 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 = "<nativeBackend>") //
public static final OptionKey<String> 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, //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,25 @@

/**
* Uses Espresso's implementation of standard JDK libraries, falling back to a delegate if the
* implementation is missing.
* <p>
* 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:
* <ul>
* <li>Startup libraries (libnespresso, libjvm, libjimage) are substituted to prevent initialization
* of native environments such as {@link com.oracle.truffle.espresso.jni.JniEnv} .</li>
* <li>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.</li>
* <li>Network functionality is implemented using host sockets, relying on
* {@code env.isSocketIOAllowed()
* == true}.</li>
* <li>Guest memory will be virtualized with (GR-70643).</li>
* </ul>
*
* @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 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* <p>
* 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)}.
* <p>
* 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}).
* <p>
* For socket IO, file descriptors are associated with host network channels (see
* {@link #openSocket(boolean, boolean, boolean, boolean)}).
* <p>
* Adapted from GraalPy's PosixResources.
*/
public final class TruffleIO implements ContextAccess {
Expand Down Expand Up @@ -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<Integer, ChannelWrapper> files;

// 0, 1 and 2 are reserved for standard streams.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down