Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ public UnsignedWord strlen(CCharPointer str) {
return PosixLibC.strlen(str);
}

@Override
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public CCharPointer strdup(CCharPointer str) {
return PosixLibC.strdup(str);
}

@Override
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public int strcmp(CCharPointer s1, CCharPointer s2) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ public UnsignedWord strlen(CCharPointer str) {
return WindowsLibC.strlen(str);
}

@Override
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public CCharPointer strdup(CCharPointer str) {
return WindowsLibC.strdup(str);
}

@Override
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public int strcmp(CCharPointer s1, CCharPointer s2) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.io.InputStream;
import java.io.OutputStream;

import jdk.internal.misc.Unsafe;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

Expand All @@ -39,6 +40,10 @@
final class NativeSecureRandomFilesCloserTearDownHook implements RuntimeSupport.Hook {
@Override
public void execute(boolean isFirstIsolate) {
if (Unsafe.getUnsafe().shouldBeInitialized(Target_sun_security_provider_NativePRNG.class)) {
// Avoid NativePRNG tear down hooks if platform native libs not initialized
return;
}
Target_sun_security_provider_NativePRNG_RandomIO instance = Target_sun_security_provider_NativePRNG.INSTANCE;
if (instance != null) {
close(instance.nextIn);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,29 @@
*/
package com.oracle.svm.core.c.function;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Objects;

import com.oracle.svm.core.util.HostedByteBufferPointer;
import org.graalvm.nativeimage.AnnotationAccess;

import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.c.type.CCharPointer;
import org.graalvm.word.Pointer;
import org.graalvm.word.WordFactory;

import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.c.CGlobalData;
import com.oracle.svm.core.c.CGlobalDataFactory;
import com.oracle.svm.core.util.VMError;

/**
Expand Down Expand Up @@ -125,7 +139,7 @@ private CEntryPointErrors() {
@Description("The isolate arguments could not be parsed.") //
public static final int ARGUMENT_PARSING_FAILED = 22;

@Description("Current target does not support the following CPU features that are required by the image.") //
@Description("Current target does not support the CPU features that are required by the image.") //
public static final int CPU_FEATURE_CHECK_FAILED = 23;

@Description("Image page size is incompatible with run-time page size. Rebuild image with -H:PageSize=[pagesize] to set appropriately.") //
Expand Down Expand Up @@ -166,7 +180,53 @@ public static String getDescription(int code) {
return result;
}

/**
* Gets the description for {@code code} as a C string.
*
* @param code an error code
* @return the description for {@code} or a null pointer if no description is available
*/
@Uninterruptible(reason = "Called when isolate creation fails.")
public static CCharPointer getDescriptionAsCString(int code) {
return (CCharPointer) getDescriptionAsCString(code, (Pointer) CSTRING_DESCRIPTIONS.get());
}

/**
* Searches {@code cstrings} for a description of {@code code}.
*
* @return the description for {@code} or a null pointer if no description is available
*/
@Uninterruptible(reason = "Called when isolate creation fails.")
private static Pointer getDescriptionAsCString(int code, Pointer cstrings) {
int offset = 0;
int startOffset = 0;
int codeIndex = 0;
while (true) {
byte ch = cstrings.readByte(offset);
if (ch == 1) {
break;
} else if (ch == 0) {
startOffset = offset + 1;
codeIndex++;
} else if (code == codeIndex) {
return cstrings.add(startOffset);
}
offset++;
}
return WordFactory.nullPointer();
}

private static final String[] DESCRIPTIONS;

/**
* The error descriptions as C strings in a single contiguous chunk of global memory. This is
* required to be able to access descriptions when errors occur during isolate creation. Without
* an isolate, static fields are not usable.
*
* @see #toCStrings
*/
private static final CGlobalData<CCharPointer> CSTRING_DESCRIPTIONS;

static {
try {
String[] array = new String[16];
Expand All @@ -183,9 +243,64 @@ public static String getDescription(int code) {
}
array[value] = description;
}
DESCRIPTIONS = Arrays.copyOf(array, maxValue + 1);
} catch (IllegalAccessException e) {
String[] descriptions = Arrays.copyOf(array, maxValue + 1);
byte[] cstrings = toCStrings(descriptions);
DESCRIPTIONS = descriptions;
CSTRING_DESCRIPTIONS = CGlobalDataFactory.createBytes(() -> cstrings);
} catch (IllegalAccessException | IOException e) {
throw VMError.shouldNotReachHere(e);
}
}

/**
* Converts all entries in {@code descriptions} to 0-terminated C strings and concatenates them
* into a single byte array with a final terminator of 1. Null entries are converted to 0 length
* C strings.
*/
@Platforms(Platform.HOSTED_ONLY.class)
private static byte[] toCStrings(String[] descriptions) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int code = 0; code < descriptions.length; code++) {
String description = descriptions[code];
if (description != null) {
baos.write(description.getBytes(StandardCharsets.UTF_8));
}
baos.write(0);
}
baos.write(1);
return checkedCStrings(descriptions, baos.toByteArray());
}

@Platforms(Platform.HOSTED_ONLY.class)
private static byte[] checkedCStrings(String[] descriptions, byte[] cstrings) {
for (int i = 0; i < cstrings.length; i++) {
byte ch = cstrings[i];
VMError.guarantee(ch != 1 || i == cstrings.length - 1, "only last byte in cstrings may be 1, got %d at index %d", ch, i);
}
for (int code = 0; code < descriptions.length; code++) {
String expect = descriptions[code];
Pointer cstringsPointer = new HostedByteBufferPointer(ByteBuffer.wrap(cstrings), 0);
String actual = toJavaString(getDescriptionAsCString(code, cstringsPointer));
if (!Objects.equals(expect, actual)) {
throw VMError.shouldNotReachHere("code %d: expected %s, got %s", code, expect, actual);
}
}
return cstrings;
}

@Platforms(Platform.HOSTED_ONLY.class)
private static String toJavaString(Pointer res) {
if (res.isNonNull()) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int i = 0;
while (true) {
byte ch = res.readByte(i++);
if (ch == 0) {
return new String(baos.toByteArray(), StandardCharsets.UTF_8);
}
baos.write(ch);
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ public static UnsignedWord strlen(CCharPointer str) {
return libc().strlen(str);
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static CCharPointer strdup(CCharPointer str) {
return libc().strdup(str);
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static int strcmp(CCharPointer s1, CCharPointer s2) {
return libc().strcmp(s1, s2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ public interface LibCSupport {
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
UnsignedWord strlen(CCharPointer str);

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
CCharPointer strdup(CCharPointer str);

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
int strcmp(CCharPointer s1, CCharPointer s2);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ static int enter(JNIJavaVMPointer vmBuf, JNIEnvironmentPointer penv, JNIJavaVMIn

boolean hasSpecialVmOptions = false;
CEntryPointCreateIsolateParameters params = WordFactory.nullPointer();
CCharPointerPointer errorstr = WordFactory.nullPointer();
if (vmArgs.isNonNull()) {
int vmArgc = vmArgs.getNOptions();
if (vmArgc > 0) {
Expand All @@ -145,7 +146,9 @@ static int enter(JNIJavaVMPointer vmBuf, JNIEnvironmentPointer penv, JNIJavaVMIn
if (optionString.isNonNull()) {
// Filter all special VM options as those must be parsed differently
// after the isolate creation.
if (Support.isSpecialVMOption(optionString)) {
if (LibC.strcmp(optionString, Support.CREATEVM_ERRORSTR_OPTION.get()) == 0) {
errorstr = (CCharPointerPointer) option.getExtraInfo();
} else if (Support.isSpecialVMOption(optionString)) {
hasSpecialVmOptions = true;
} else {
argv.addressOf(argc).write(optionString);
Expand Down Expand Up @@ -174,6 +177,11 @@ static int enter(JNIJavaVMPointer vmBuf, JNIEnvironmentPointer penv, JNIJavaVMIn
// The isolate was created successfully, so we can finish the initialization.
return Support.finishInitialization(vmBuf, penv, vmArgs, hasSpecialVmOptions);
}
if (errorstr.isNonNull()) {
CCharPointer msg = CEntryPointErrors.getDescriptionAsCString(code);
CCharPointer msgCopy = msg.isNonNull() ? LibC.strdup(msg) : WordFactory.nullPointer();
errorstr.write(msgCopy);
}
return JNIFunctions.Support.convertCEntryPointErrorToJNIError(code, true);
}
}
Expand All @@ -182,23 +190,28 @@ static int enter(JNIJavaVMPointer vmBuf, JNIEnvironmentPointer penv, JNIJavaVMIn
* This method supports the non-standard option strings detailed in the table below.
*
* <pre>
| optionString | meaning |
|===============|===================================================================================|
| _log | extraInfo is a pointer to a "void(const char *buf, size_t count)" function. |
| | Formatted low level log messages are sent to this function. |
| | If present, then _flush_log is also required to be present. |
|---------------|-----------------------------------------------------------------------------------|
| _fatal_log | extraInfo is a pointer to a "void(const char *buf, size_t count)" function. |
| | Formatted low level log messages are sent to this function. |
| | This log function is used for logging fatal crash data. |
|---------------|-----------------------------------------------------------------------------------|
| _flush_log | extraInfo is a pointer to a "void()" function. |
| | This function is called when the low level log stream should be flushed. |
| | If present, then _log is also required to be present. |
|---------------|-----------------------------------------------------------------------------------|
| _fatal | extraInfo is a pointer to a "void()" function. |
| | This function is called when a non-recoverable, fatal error occurs. |
|---------------|-----------------------------------------------------------------------------------|
| optionString | meaning |
|====================|===================================================================================|
| _log | extraInfo is a pointer to a "void(const char *buf, size_t count)" function. |
| | Formatted low level log messages are sent to this function. |
| | If present, then _flush_log is also required to be present. |
|--------------------|-----------------------------------------------------------------------------------|
| _fatal_log | extraInfo is a pointer to a "void(const char *buf, size_t count)" function. |
| | Formatted low level log messages are sent to this function. |
| | This log function is used for logging fatal crash data. |
|--------------------|-----------------------------------------------------------------------------------|
| _flush_log | extraInfo is a pointer to a "void()" function. |
| | This function is called when the low level log stream should be flushed. |
| | If present, then _log is also required to be present. |
|--------------------|-----------------------------------------------------------------------------------|
| _fatal | extraInfo is a pointer to a "void()" function. |
| | This function is called when a non-recoverable, fatal error occurs. |
|--------------------|-----------------------------------------------------------------------------------|
| _createvm_errorstr | extraInfo is a "const char**" value. |
| | If CreateJavaVM returns non-zero, then extraInfo is assigned a newly malloc'ed |
| | 0-terminated C string describing the error if a description is available, |
| | otherwise extraInfo is set to null. |
|--------------------|-----------------------------------------------------------------------------------|
* </pre>
*
* @see LogHandler
Expand Down Expand Up @@ -297,7 +310,9 @@ static int GetEnv(JNIJavaVM vm, WordPointer env, int version) {
* methods must match JNI invocation API functions.
*/
static class Support {

private static final CGlobalData<CCharPointer> JAVA_VM_ID_OPTION = CGlobalDataFactory.createCString("_javavm_id");
private static final CGlobalData<CCharPointer> CREATEVM_ERRORSTR_OPTION = CGlobalDataFactory.createCString("_createvm_errorstr");

static class JNIGetEnvPrologue implements CEntryPointOptions.Prologue {
@Uninterruptible(reason = "prologue")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,6 @@ interface VoidFunctionPointer extends CFunctionPointer {
void invoke();
}

interface FatalContextFunctionPointer extends CFunctionPointer {
@InvokeCFunctionPointer
boolean invoke(CodePointer callerIP, String msg, Throwable ex);
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static boolean isJniVMOption(CCharPointer optionString) {
return LibC.strcmp(optionString, LOG_OPTION.get()) == 0 ||
Expand Down