diff --git a/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/DowncallSignature.java b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/DowncallSignature.java index 274e9210bf..b4d3e008d8 100644 --- a/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/DowncallSignature.java +++ b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/DowncallSignature.java @@ -55,4 +55,8 @@ Class retConversion() default void.class; Class[] argConversions() default {}; + + boolean critical() default false; + + boolean captureCallState() default false; } diff --git a/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java index c19609436f..9ab98a9bd6 100644 --- a/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java +++ b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java @@ -1411,8 +1411,8 @@ private void generateExternalFunctionInvoker(List options = new ArrayList<>();"); + lines.add(" if (critical) {"); + lines.add(" options.add(Linker.Option.critical(false));"); + lines.add(" }"); + lines.add(" if (captureCallState) {"); + lines.add(" options.add(CAPTURE_CALL_STATE_OPTION);"); + lines.add(" }"); + lines.add(" MethodHandle methodHandle = Linker.nativeLinker().downcallHandle(functionDescriptor, options.toArray(Linker.Option[]::new));"); lines.add(" methodHandle = MethodHandles.filterArguments(methodHandle, 0, OF_ADDRESS);"); lines.add(" return methodHandle.asType(methodType);"); lines.add(" }"); lines.add(""); lines.add(" @Override"); + lines.add(" protected Object createCapturedCallStateImpl(Object arena) {"); + lines.add(" return ((Arena) arena).allocate(CAPTURE_STATE_LAYOUT);"); + lines.add(" }"); + lines.add(""); + lines.add(" @TruffleBoundary"); + lines.add(" @Override"); + lines.add(" protected int readCapturedErrnoImpl(Object capturedCallState) {"); + lines.add(" return (int) ERRNO.get((MemorySegment) capturedCallState, 0L);"); + lines.add(" }"); + lines.add(""); + lines.add(" @TruffleBoundary"); + lines.add(" @Override"); + lines.add(" protected int readCapturedGetLastErrorImpl(Object capturedCallState) {"); + lines.add(" if (GET_LAST_ERROR == null) {"); + lines.add(" throw shouldNotReachHere(\"GetLastError is only captured on Windows\");"); + lines.add(" }"); + lines.add(" return (int) GET_LAST_ERROR.get((MemorySegment) capturedCallState, 0L);"); + lines.add(" }"); + lines.add(""); + lines.add(" @Override"); lines.add(" @SuppressWarnings(\"restricted\")"); lines.add(" protected long createClosureImpl(MethodHandle staticMethodHandle, NativeSimpleType resType, NativeSimpleType[] argTypes, Object arena) {"); lines.add(" FunctionDescriptor functionDescriptor = createFunctionDescriptor(resType, argTypes);"); @@ -1668,33 +1715,6 @@ private void generateNativeAccessSupport(Element[] origins) throws IOException { lines.add(" return resType == NativeSimpleType.VOID ? FunctionDescriptor.ofVoid(argLayouts) : FunctionDescriptor.of(asLayout(resType), argLayouts);"); lines.add(" }"); lines.add(""); - lines.add(" private static FunctionDescriptor createFunctionDescriptor(MethodType methodType) {"); - lines.add(" Class[] parameterTypes = methodType.parameterArray();"); - lines.add(" MemoryLayout[] argLayouts = new MemoryLayout[parameterTypes.length - 1];"); - lines.add(" for (int i = 1; i < parameterTypes.length; i++) {"); - lines.add(" argLayouts[i - 1] = asLayout(parameterTypes[i]);"); - lines.add(" }"); - lines.add(" Class returnType = methodType.returnType();"); - lines.add(" return returnType == void.class ? FunctionDescriptor.ofVoid(argLayouts) : FunctionDescriptor.of(asLayout(returnType), argLayouts);"); - lines.add(" }"); - lines.add(""); - lines.add(" private static MemoryLayout asLayout(Class type) {"); - lines.add(" if (type == byte.class) {"); - lines.add(" return ValueLayout.JAVA_BYTE;"); - lines.add(" } else if (type == short.class) {"); - lines.add(" return ValueLayout.JAVA_SHORT;"); - lines.add(" } else if (type == int.class) {"); - lines.add(" return ValueLayout.JAVA_INT;"); - lines.add(" } else if (type == long.class) {"); - lines.add(" return ValueLayout.JAVA_LONG;"); - lines.add(" } else if (type == float.class) {"); - lines.add(" return ValueLayout.JAVA_FLOAT;"); - lines.add(" } else if (type == double.class) {"); - lines.add(" return ValueLayout.JAVA_DOUBLE;"); - lines.add(" }"); - lines.add(" throw shouldNotReachHere(\"Unsupported layout carrier: \" + type);"); - lines.add(" }"); - lines.add(""); lines.add(" private static MemoryLayout asLayout(NativeSimpleType type) {"); lines.add(" return switch (type) {"); lines.add(" case VOID -> throw shouldNotReachHere(\"VOID has no layout\");"); @@ -1724,7 +1744,6 @@ private void generateDummyNativeAccessSupport(Element[] origins) throws IOExcept lines.add("package " + NATIVE_ACCESS_PACKAGE + ";"); lines.add(""); lines.add("import java.lang.invoke.MethodHandle;"); - lines.add("import java.lang.invoke.MethodType;"); lines.add(""); lines.add("import " + NativeSimpleType.class.getCanonicalName() + ";"); lines.add(""); @@ -1750,7 +1769,22 @@ private void generateDummyNativeAccessSupport(Element[] origins) throws IOExcept lines.add(" }"); lines.add(""); lines.add(" @Override"); - lines.add(" protected MethodHandle createDowncallHandleImpl(MethodType methodType, boolean critical) {"); + lines.add(" protected MethodHandle createDowncallHandleImpl(boolean critical, boolean captureCallState, NativeSimpleType resType, NativeSimpleType[] argTypes) {"); + lines.add(" throw unsupported();"); + lines.add(" }"); + lines.add(""); + lines.add(" @Override"); + lines.add(" protected Object createCapturedCallStateImpl(Object arena) {"); + lines.add(" throw unsupported();"); + lines.add(" }"); + lines.add(""); + lines.add(" @Override"); + lines.add(" protected int readCapturedErrnoImpl(Object state) {"); + lines.add(" throw unsupported();"); + lines.add(" }"); + lines.add(""); + lines.add(" @Override"); + lines.add(" protected int readCapturedGetLastErrorImpl(Object state) {"); lines.add(" throw unsupported();"); lines.add(" }"); lines.add(""); diff --git a/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/GenerateNativeDowncallsProcessor.java b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/GenerateNativeDowncallsProcessor.java index ede35d6f5f..eb5b9b9c21 100644 --- a/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/GenerateNativeDowncallsProcessor.java +++ b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/GenerateNativeDowncallsProcessor.java @@ -63,9 +63,12 @@ import com.oracle.graal.python.annotations.NativeSimpleType; public class GenerateNativeDowncallsProcessor extends AbstractProcessor { - private record NativeDowncallDesc(String name, String symbolName, NativeSimpleType returnType, List argumentTypes, List argumentNames) { + private record NativeDowncallDesc(String name, NativeSimpleType returnType, List argumentTypes, List argumentNames, boolean critical, boolean captureCallState) { } + private static final String CAPTURE_CALL_STATE_FIELD = "callState"; + private static final String NATIVE_MEMORY_SEGMENT_CLASS_FIELD = "NATIVE_MEMORY_SEGMENT_CLASS"; + @Override public Set getSupportedAnnotationTypes() { return Set.of(DowncallSignature.class.getName()); @@ -124,6 +127,9 @@ private void generateInvoker(TypeElement invokerElement) throws IOException, Pro throw error(invokerElement, "Annotated class does not declare any downcalls"); } + // Indicates if any of the annotations wants to capture the state. In this case, we need to generate appropriate code. + boolean hasCapturedCallStates = hasCapturedCallStates(downcalls); + String packageName = processingEnv.getElementUtils().getPackageOf(invokerElement).getQualifiedName().toString(); String invokerQualifiedName = invokerElement.getQualifiedName().toString(); String invokerTypeRef = invokerQualifiedName.startsWith(packageName + ".") ? invokerQualifiedName.substring(packageName.length() + 1) : invokerQualifiedName; @@ -135,28 +141,38 @@ private void generateInvoker(TypeElement invokerElement) throws IOException, Pro lines.add("// Generated by annotation processor: " + getClass().getName()); lines.add("package " + packageName + ";"); lines.add(""); + lines.add("import java.lang.foreign.MemorySegment;"); lines.add("import java.lang.invoke.MethodHandle;"); - lines.add("import java.lang.invoke.MethodType;"); lines.add("import java.util.concurrent.atomic.AtomicLongArray;"); + lines.add("import java.util.List;"); lines.add(""); + lines.add("import com.oracle.graal.python.annotations.NativeSimpleType;"); lines.add("import com.oracle.graal.python.runtime.nativeaccess.NativeAccessSupport;"); lines.add("import com.oracle.graal.python.runtime.nativeaccess.NativeLibrary;"); lines.add("import com.oracle.truffle.api.CompilerDirectives;"); lines.add("import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;"); lines.add(""); lines.add("final class " + className + " extends " + invokerTypeRef + " {"); + lines.add(" private static final Class " + NATIVE_MEMORY_SEGMENT_CLASS_FIELD + " = MemorySegment.NULL.getClass();"); lines.add(" private final PythonContext context;"); lines.add(" private final AtomicLongArray cachedFunctions = new AtomicLongArray(" + downcalls.size() + ");"); lines.add(" private volatile NativeLibrary nativeLibrary;"); + if (hasCapturedCallStates) { + lines.add(" private final ThreadLocal " + CAPTURE_CALL_STATE_FIELD + ";"); + } lines.add(""); for (NativeDowncallDesc downcall : downcalls) { - NativeDowncallMethodHandleGenerator.emitMethodHandleField(lines, methodHandleName(downcall.name), downcall.returnType, downcall.argumentTypes); + NativeDowncallMethodHandleGenerator.emitMethodHandleField(lines, methodHandleName(downcall.name), downcall.returnType, downcall.argumentTypes, downcall.critical, + downcall.captureCallState); } lines.add(""); lines.add(" " + className + "(PythonContext context) {"); lines.add(" this.context = context;"); + if (hasCapturedCallStates) { + lines.add(" this." + CAPTURE_CALL_STATE_FIELD + " = ThreadLocal.withInitial(() -> context.ensureNativeContext().getCapturedCallStateTL().get());"); + } lines.add(" }"); for (int i = 0; i < downcalls.size(); i++) { @@ -229,13 +245,13 @@ private static NativeDowncallDesc extractDowncall(ExecutableElement method) thro List argumentTypes = List.of(argTypes); List argumentNames = extractArgumentNames(method); - String symbolName = method.getSimpleName().toString(); return new NativeDowncallDesc( - symbolName, - symbolName, + method.getSimpleName().toString(), annotation.returnType(), argumentTypes, - argumentNames); + argumentNames, + annotation.critical(), + annotation.captureCallState()); } private static List extractArgumentNames(ExecutableElement method) throws ProcessingError { @@ -289,12 +305,12 @@ private static void emitDowncallMethod(List lines, NativeDowncallDesc do lines.add(" @TruffleBoundary(allowInlining = true, transferToInterpreterOnException = false)"); lines.add(" @Override"); lines.add(" " + nativeSimpleTypeToJavaType(downcall.returnType) + " " + downcall.name + "(" + typedArgs(downcall.argumentTypes, downcall.argumentNames) + ") {"); - lines.add(" long functionPointer = lookup(" + functionIndex + ", " + stringLiteral(downcall.symbolName) + ");"); + lines.add(" long functionPointer = lookup(" + functionIndex + ", " + stringLiteral(downcall.name) + ");"); lines.add(" try {"); if (NativeSimpleType.VOID == downcall.returnType) { - lines.add(" " + methodHandleName(downcall.name) + ".invokeExact(" + invokeArgs(downcall.argumentNames) + ");"); + lines.add(" " + methodHandleName(downcall.name) + ".invokeExact(" + invokeArgs(downcall) + ");"); } else { - lines.add(" return (" + nativeSimpleTypeToJavaType(downcall.returnType) + ") " + methodHandleName(downcall.name) + ".invokeExact(" + invokeArgs(downcall.argumentNames) + ");"); + lines.add(" return (" + nativeSimpleTypeToJavaType(downcall.returnType) + ") " + methodHandleName(downcall.name) + ".invokeExact(" + invokeArgs(downcall) + ");"); } lines.add(" } catch (Throwable t) {"); lines.add(" throw CompilerDirectives.shouldNotReachHere(t);"); @@ -314,12 +330,27 @@ private static String typedArgs(List argTypes, List ar return String.join(", ", args); } - private static String invokeArgs(List argNames) { - String args = String.join(", ", argNames); - return args.isEmpty() ? "functionPointer" : "functionPointer, " + args; + private static String invokeArgs(NativeDowncallDesc downcall) { + List allArgs = new ArrayList<>(); + allArgs.add("functionPointer"); + if (downcall.captureCallState) { + String capturedCallState = String.format("%s.cast(%s.get())", NATIVE_MEMORY_SEGMENT_CLASS_FIELD, CAPTURE_CALL_STATE_FIELD); + allArgs.add(capturedCallState); + } + allArgs.addAll(downcall.argumentNames); + return String.join(", ", allArgs); } private static String stringLiteral(String value) { return "\"" + value.replace("\\", "\\\\").replace("\"", "\\\"") + "\""; } + + private static boolean hasCapturedCallStates(List downcalls) { + for (NativeDowncallDesc downcall : downcalls) { + if (downcall.captureCallState) { + return true; + } + } + return false; + } } diff --git a/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/NativeDowncallMethodHandleGenerator.java b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/NativeDowncallMethodHandleGenerator.java index e31e059c70..cba07e32ee 100644 --- a/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/NativeDowncallMethodHandleGenerator.java +++ b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/NativeDowncallMethodHandleGenerator.java @@ -40,7 +40,6 @@ */ package com.oracle.graal.python.processor; -import java.util.ArrayList; import java.util.List; import com.oracle.graal.python.annotations.NativeSimpleType; @@ -60,48 +59,50 @@ static String methodHandleVarName(String signatureName) { return "NATIVE_METHOD_HANDLE_" + signatureName; } - static String toClassLiteral(String javaType) { + private static String toNativeSimpleTypeLiteral(String javaType) { return switch (javaType) { - case "void" -> "void.class"; - case "byte" -> "byte.class"; - case "short" -> "short.class"; - case "int" -> "int.class"; - case "long" -> "long.class"; - case "float" -> "float.class"; - case "double" -> "double.class"; + case "void" -> "NativeSimpleType.VOID"; + case "byte" -> "NativeSimpleType.SINT8"; + case "short" -> "NativeSimpleType.SINT16"; + case "int" -> "NativeSimpleType.SINT32"; + case "long" -> "NativeSimpleType.SINT64"; + case "float" -> "NativeSimpleType.FLOAT"; + case "double" -> "NativeSimpleType.DOUBLE"; default -> throw new IllegalArgumentException("Unexpected Java type: " + javaType); }; } - static String toClassLiteral(NativeSimpleType nativeType) { + private static String toNativeSimpleTypeLiteral(NativeSimpleType nativeType) { return switch (nativeType) { - case VOID -> "void.class"; - case SINT8 -> "byte.class"; - case SINT16 -> "short.class"; - case SINT32 -> "int.class"; - case SINT64, POINTER -> "long.class"; - case FLOAT -> "float.class"; - case DOUBLE -> "double.class"; + case VOID -> "NativeSimpleType.VOID"; + case SINT8 -> "NativeSimpleType.SINT8"; + case SINT16 -> "NativeSimpleType.SINT16"; + case SINT32 -> "NativeSimpleType.SINT32"; + case SINT64 -> "NativeSimpleType.SINT64"; + case FLOAT -> "NativeSimpleType.FLOAT"; + case DOUBLE -> "NativeSimpleType.DOUBLE"; + case POINTER -> "NativeSimpleType.POINTER"; }; } static void emitMethodHandleField(List lines, String fieldName, String returnType, List argTypes) { - List methodTypeArgs = new ArrayList<>(); - methodTypeArgs.add("long.class"); + StringBuilder createHandle = new StringBuilder("NativeAccessSupport.createDowncallHandle(false, false, "); + createHandle.append(toNativeSimpleTypeLiteral(returnType)); for (String argType : argTypes) { - methodTypeArgs.add(toClassLiteral(argType)); + createHandle.append(", ").append(toNativeSimpleTypeLiteral(argType)); } - lines.add(" private static final MethodHandle " + fieldName + " = NativeAccessSupport.createDowncallHandle(" + - "MethodType.methodType(" + toClassLiteral(returnType) + ", " + String.join(", ", methodTypeArgs) + "), false);"); + createHandle.append(")"); + lines.add(" private static final MethodHandle " + fieldName + " = " + createHandle + ";"); } - static void emitMethodHandleField(List lines, String fieldName, NativeSimpleType returnType, List argTypes) { - List methodTypeArgs = new ArrayList<>(); - methodTypeArgs.add("long.class"); + static void emitMethodHandleField(List lines, String fieldName, NativeSimpleType returnType, List argTypes, boolean critical, boolean captureCallState) { + StringBuilder createHandle = new StringBuilder("NativeAccessSupport.createDowncallHandle("); + createHandle.append(critical).append(", ").append(captureCallState).append(", "); + createHandle.append(toNativeSimpleTypeLiteral(returnType)); for (NativeSimpleType argType : argTypes) { - methodTypeArgs.add(toClassLiteral(argType)); + createHandle.append(", ").append(toNativeSimpleTypeLiteral(argType)); } - lines.add(" private static final MethodHandle " + fieldName + " = NativeAccessSupport.createDowncallHandle(" + - "MethodType.methodType(" + toClassLiteral(returnType) + ", " + String.join(", ", methodTypeArgs) + "), false);"); + createHandle.append(")"); + lines.add(" private static final MethodHandle " + fieldName + " = " + createHandle + ";"); } } diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py b/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py index 6511da2dca..0bd4a412a8 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py @@ -270,6 +270,19 @@ def test_open_bytes_path(self): except Exception: pass + def test_failed_read_write_errno(self): + read_fd, write_fd = os.pipe() + os.close(read_fd) + os.close(write_fd) + + with self.assertRaises(OSError) as read_error: + os.read(read_fd, 1) + self.assertEqual(errno.EBADF, read_error.exception.errno) + + with self.assertRaises(OSError) as write_error: + os.write(write_fd, b'x') + self.assertEqual(errno.EBADF, write_error.exception.errno) + def test_fd_converter(self): class MyInt(int): def fileno(self): return 0 @@ -554,6 +567,11 @@ def test_scandir_empty(self): with os.scandir(TEST_FULL_PATH1) as dir: self.assertEqual(0, len([entry for entry in dir])) + def test_scandir_empty_repeated_eof(self): + with os.scandir(TEST_FULL_PATH1) as dir: + self.assertRaises(StopIteration, next, dir) + self.assertRaises(StopIteration, next, dir) + def test_listdir_empty(self): self.assertEqual([], os.listdir(TEST_FULL_PATH1)) @@ -948,6 +966,17 @@ def sysconf_max(name): self.assertGreaterEqual(os.sysconf('SC_PHYS_PAGES'), 1) self.assertGreaterEqual(os.sysconf('SC_NPROCESSORS_CONF'), 1) + def test_sysconf_valid_minus_one(self): + for name in os.sysconf_names: + try: + value = os.sysconf(name) + except OSError: + continue + if value == -1: + self.assertEqual(-1, os.sysconf(name)) + return + raise unittest.SkipTest("platform has no sysconf name returning valid -1") + if __name__ == '__main__': unittest.main() diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/ArrowReleaseCallback.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/ArrowReleaseCallback.java index 85082a0d49..7d08c3021b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/ArrowReleaseCallback.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/ArrowReleaseCallback.java @@ -40,15 +40,16 @@ */ package com.oracle.graal.python.nodes.arrow; +import static com.oracle.graal.python.annotations.NativeSimpleType.POINTER; +import static com.oracle.graal.python.annotations.NativeSimpleType.VOID; + import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; import com.oracle.graal.python.runtime.nativeaccess.NativeAccessSupport; import com.oracle.truffle.api.CompilerDirectives; public final class ArrowReleaseCallback { - private static final MethodHandle HANDLE = NativeAccessSupport.createDowncallHandle( - MethodType.methodType(void.class, long.class, long.class), false); + private static final MethodHandle HANDLE = NativeAccessSupport.createDowncallHandle(false, false, VOID, POINTER); private ArrowReleaseCallback() { } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java index 3be698bca1..5f2e14066e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java @@ -147,6 +147,10 @@ /** * Implementation that invokes the native POSIX support library through generated native-access * downcalls. + * + * POSIX calls use a custom errno capture path instead of FFM call-state capture. The native wrapper + * functions store errno in a C thread-local only when the POSIX return value indicates an error. This + * avoids unconditional FFM call-state capture on the hot POSIX path. */ @ExportLibrary(PosixSupportLibrary.class) public final class NativePosixSupport extends PosixSupport { @@ -169,12 +173,9 @@ abstract static class PosixNativeFunctionInvoker { @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32}) abstract int init_constants(long out, int len); - @DowncallSignature(returnType = SINT32) + @DowncallSignature(critical = true, returnType = SINT32) abstract int get_errno(); - @DowncallSignature(returnType = VOID, argumentTypes = {SINT32}) - abstract void set_errno(int errno); - @DowncallSignature(returnType = SINT64, argumentTypes = {SINT64, SINT32, SINT32, SINT32, SINT64}) abstract long call_mmap(long length, int prot, int flags, int fd, long offset); @@ -603,7 +604,7 @@ static NativeLibrary loadNativeLibrary(PythonContext context) { } abstract static class CryptNativeFunctionInvoker { - @DowncallSignature(returnType = POINTER, argumentTypes = {POINTER, POINTER}) + @DowncallSignature(captureCallState = true, returnType = POINTER, argumentTypes = {POINTER, POINTER}) abstract long crypt(long word, long salt); @TruffleBoundary @@ -707,12 +708,8 @@ public long getpid() { } @ExportMessage - public int umask(int mask) throws PosixException { - int result = posixNativeFunctionInvoker.call_umask(mask); - if (result < 0) { - throw getErrnoAndThrowPosixException(); - } - return result; + public int umask(int mask) { + return posixNativeFunctionInvoker.call_umask(mask); } @ExportMessage @@ -744,7 +741,6 @@ public Buffer read(int fd, long length) throws PosixException { Buffer buffer = Buffer.allocate(count); long nativeBuffer = NativeMemory.mallocByteArrayOrNull(count); try { - posixNativeFunctionInvoker.set_errno(0); long n = posixNativeFunctionInvoker.call_read(fd, nativeBuffer, count); if (n < 0) { throw getErrnoAndThrowPosixException(); @@ -761,7 +757,6 @@ public long write(int fd, Buffer data) throws PosixException { long nativeBuffer = NativeMemory.mallocByteArrayOrNull(data.length); try { NativeMemory.writeByteArrayElements(nativeBuffer, 0, data.data, 0, (int) data.length); - posixNativeFunctionInvoker.set_errno(0); long n = posixNativeFunctionInvoker.call_write(fd, nativeBuffer, data.length); if (n < 0) { throw getErrnoAndThrowPosixException(); @@ -992,11 +987,8 @@ public int[] getTerminalSize(int fd) throws PosixException { @ExportMessage public long sysconf(int name) throws PosixException { long result = posixNativeFunctionInvoker.call_sysconf(name); - if (result == -1) { - int errno = posixNativeFunctionInvoker.get_errno(); - if (errno != 0) { - throw newPosixException(errno); - } + if (result == Long.MIN_VALUE) { + throw getErrnoAndThrowPosixException(); } return result; } @@ -1181,7 +1173,7 @@ public Object getcwd() throws PosixException { buffer = buffer.withLength(findZero(buffer.data)); return buffer; } - int errno = posixNativeFunctionInvoker.get_errno(); + int errno = getErrno(); if (errno != OSErrorEnum.ERANGE.getNumber()) { throw newPosixException(errno); } @@ -1261,23 +1253,22 @@ public Object readdir(Object dirStreamObj) throws PosixException { int result; do { result = posixNativeFunctionInvoker.call_readdir(dirStream, nativeName, DIRENT_NAME_BUF_LENGTH, nativeOut); - if (result != 0) { + if (result > 0) { NativeMemory.readByteArrayElements(nativeName, 0, name.data, 0, name.data.length); } - } while (result != 0 && name.data[0] == '.' && (name.data[1] == 0 || (name.data[1] == '.' && name.data[2] == 0))); - if (result != 0) { + } while (result > 0 && name.data[0] == '.' && (name.data[1] == 0 || (name.data[1] == '.' && name.data[2] == 0))); + if (result > 0) { NativeMemory.readLongArrayElements(nativeOut, 0, out, 0, out.length); return new DirEntry(name.withLength(findZero(name.data)), out[0], (int) out[1]); } + if (result < 0) { + throw getErrnoAndThrowPosixException(); + } } finally { NativeMemory.free(nativeOut); NativeMemory.free(nativeName); } - int errno = posixNativeFunctionInvoker.get_errno(); - if (errno == 0) { - return null; - } - throw newPosixException(errno); + return null; } @ExportMessage @@ -1448,7 +1439,7 @@ public boolean faccessat(int dirFd, Object path, int mode, boolean effectiveIds, NativeMemory.free(pathPtr); } if (ret != 0 && LOGGER.isLoggable(Level.FINE)) { - log(Level.FINE, "faccessat return value: %d, errno: %d", ret, posixNativeFunctionInvoker.get_errno()); + log(Level.FINE, "faccessat return value: %d, errno: %d", ret, getErrno()); } return ret == 0; } @@ -1527,8 +1518,8 @@ public void kill(long pid, int signal) throws PosixException { @ExportMessage public void raise(int signal) throws PosixException { int res = posixNativeFunctionInvoker.call_raise(signal); - if (res == -1) { - throw getErrnoAndThrowPosixException(); + if (res != 0) { + throw newPosixException(OSErrorEnum.EINVAL.getNumber()); } } @@ -1789,13 +1780,10 @@ public OpenPtyResult openpty() throws PosixException { @ExportMessage public TruffleString ctermid( @Bind Node inliningTarget, - @Shared("cString") @Cached NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode zeroTerminatedUtf8ToTruffleStringNode) throws PosixException { + @Shared("cString") @Cached NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode zeroTerminatedUtf8ToTruffleStringNode) { long nativeBuf = NativeMemory.mallocByteArray(L_ctermid.value); try { - int res = posixNativeFunctionInvoker.call_ctermid(nativeBuf); - if (res == -1) { - throw getErrnoAndThrowPosixException(); - } + posixNativeFunctionInvoker.call_ctermid(nativeBuf); // TODO PyUnicode_DecodeFSDefault return zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, nativeBuf); } finally { @@ -2251,7 +2239,6 @@ public int send(int sockfd, byte[] buf, int offset, int len, int flags) throws P long nativeBuffer = NativeMemory.mallocByteArrayOrNull(len); try { NativeMemory.writeByteArrayElements(nativeBuffer, 0, buf, offset, len); - posixNativeFunctionInvoker.set_errno(0); int result = posixNativeFunctionInvoker.call_send(sockfd, nativeBuffer, len, flags); if (result == -1) { throw getErrnoAndThrowPosixException(); @@ -2274,7 +2261,6 @@ public int sendto(int sockfd, byte[] buf, int offset, int len, int flags, Univer NativeMemory.writeByteArrayElements(nativeBuffer, 0, buf, offset, len); nativeDestAddr = NativeMemory.mallocByteArrayOrNull(destAddrLen); NativeMemory.writeByteArrayElements(nativeDestAddr, 0, destAddr.data, 0, destAddrLen); - posixNativeFunctionInvoker.set_errno(0); int result = posixNativeFunctionInvoker.call_sendto(sockfd, nativeBuffer, 0, len, flags, nativeDestAddr, destAddrLen); if (result == -1) { throw getErrnoAndThrowPosixException(); @@ -2291,7 +2277,6 @@ public int recv(int sockfd, byte[] buf, int offset, int len, int flags) throws P checkBounds(buf, offset, len); long nativeBuffer = NativeMemory.mallocByteArrayOrNull(len); try { - posixNativeFunctionInvoker.set_errno(0); int result = posixNativeFunctionInvoker.call_recv(sockfd, nativeBuffer, len, flags); if (result == -1) { throw getErrnoAndThrowPosixException(); @@ -2314,7 +2299,6 @@ public RecvfromResult recvfrom(int sockfd, byte[] buf, int offset, int len, int nativeBuffer = NativeMemory.mallocByteArrayOrNull(len); nativeSrcAddr = NativeMemory.mallocByteArray(srcAddr.data.length); nativeAddrLen = NativeMemory.mallocIntArray(1); - posixNativeFunctionInvoker.set_errno(0); int result = posixNativeFunctionInvoker.call_recvfrom(sockfd, nativeBuffer, 0, len, flags, nativeSrcAddr, nativeAddrLen); if (result == -1) { throw getErrnoAndThrowPosixException(); @@ -2574,7 +2558,7 @@ public TruffleString crypt(TruffleString word, TruffleString salt, // CPython doesn't handle the case of "invalid hash" return specially and neither do // we if (resultPtr == 0) { - throw getErrnoAndThrowPosixException(); + throw newPosixException(context.ensureNativeContext().getErrno()); } return zeroTerminatedUtf8ToTruffleStringNode.execute(raisingNode, resultPtr); } @@ -3008,7 +2992,7 @@ void semWait(long handle) throws PosixException { boolean semTryWait(long handle) throws PosixException { int res = posixNativeFunctionInvoker.call_sem_trywait(handle); if (res < 0) { - int errno = posixNativeFunctionInvoker.get_errno(); + int errno = getErrno(); if (errno == OSErrorEnum.EAGAIN.getNumber()) { return false; } @@ -3024,7 +3008,7 @@ boolean semTimedWait(long handle, long deadlineNs, if (PythonLanguage.getPythonOS() == PythonOS.PLATFORM_LINUX) { int res = posixNativeFunctionInvoker.call_sem_timedwait(handle, deadlineNs); if (res < 0) { - int errno = posixNativeFunctionInvoker.get_errno(); + int errno = getErrno(); if (errno == OSErrorEnum.ETIMEDOUT.getNumber()) { return false; } @@ -3337,7 +3321,7 @@ public String toString() { // Helpers private PosixException getErrnoAndThrowPosixException() throws PosixException { - throw newPosixException(posixNativeFunctionInvoker.get_errno()); + throw newPosixException(getErrno()); } @TruffleBoundary @@ -3421,4 +3405,8 @@ private static void log(Level level, String fmt, Object... args) { LOGGER.log(level, String.format(fmt, args)); } } + + private int getErrno() { + return posixNativeFunctionInvoker.get_errno(); + } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupport.java index f0caf3e8d1..e6651baf75 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupport.java @@ -104,12 +104,22 @@ static long lookupDefault(String name) { return INSTANCE.lookupDefaultImpl(name); } - static MethodHandle createDowncallHandle(NativeSimpleType resType, NativeSimpleType... argTypes) { - return INSTANCE.createTypedDowncallHandle(resType, argTypes); + public static MethodHandle createDowncallHandle(boolean critical, boolean captureCallState, NativeSimpleType resType, NativeSimpleType... argTypes) { + return INSTANCE.createDowncallHandleImpl(critical, captureCallState, resType, argTypes); } - public static MethodHandle createDowncallHandle(MethodType methodType, boolean critical) { - return INSTANCE.createDowncallHandleImpl(methodType, critical); + public static Object createCapturedCallState(Object arena) { + return INSTANCE.createCapturedCallStateImpl(arena); + } + + /** Read value of POSIX's {@code errno} from the captured call state buffer. */ + public static int readCapturedErrno(Object capturedCallStatePtr) { + return INSTANCE.readCapturedErrnoImpl(capturedCallStatePtr); + } + + /** Read value of WinAPI's {@code GetLastError} from the captured call state buffer. */ + public static int readCapturedGetLastError(Object capturedCallStatePtr) { + return INSTANCE.readCapturedGetLastErrorImpl(capturedCallStatePtr); } public static boolean isAvailable() { @@ -124,15 +134,6 @@ public static boolean isCurrentThreadVirtual() { return INSTANCE.isCurrentThreadVirtualImpl(); } - private MethodHandle createTypedDowncallHandle(NativeSimpleType resType, NativeSimpleType... argTypes) { - Class[] parameterTypes = new Class[argTypes.length + 1]; - parameterTypes[0] = long.class; - for (int i = 0; i < argTypes.length; i++) { - parameterTypes[i + 1] = asJavaType(argTypes[i]); - } - return createDowncallHandleImpl(MethodType.methodType(asJavaType(resType), parameterTypes), false); - } - protected abstract Object createArenaImpl(); protected abstract void closeArenaImpl(Object arena); @@ -141,7 +142,13 @@ private MethodHandle createTypedDowncallHandle(NativeSimpleType resType, NativeS protected abstract long lookupDefaultImpl(String name); - protected abstract MethodHandle createDowncallHandleImpl(MethodType methodType, boolean critical); + protected abstract MethodHandle createDowncallHandleImpl(boolean critical, boolean captureCallState, NativeSimpleType resType, NativeSimpleType[] argTypes); + + protected abstract Object createCapturedCallStateImpl(Object arena); + + protected abstract int readCapturedErrnoImpl(Object capturedCallState); + + protected abstract int readCapturedGetLastErrorImpl(Object capturedCallState); protected abstract long createClosureImpl(MethodHandle staticMethodHandle, NativeSimpleType resType, NativeSimpleType[] argTypes, Object arena); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupportJdk21.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupportJdk21.java index d19c26e267..bb3a5f524e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupportJdk21.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupportJdk21.java @@ -66,8 +66,36 @@ protected long lookupDefaultImpl(String name) { } @Override - protected MethodHandle createDowncallHandleImpl(MethodType methodType, boolean critical) { - return unsupportedDowncallHandle(methodType); + protected MethodHandle createDowncallHandleImpl(boolean critical, boolean captureCallState, NativeSimpleType resType, NativeSimpleType[] argTypes) { + return unsupportedDowncallHandle(createMethodType(captureCallState, resType, argTypes)); + } + + private static MethodType createMethodType(boolean captureCallState, NativeSimpleType resType, NativeSimpleType... argTypes) { + int injectedArgumentCount = captureCallState ? 2 : 1; + Class[] parameterTypes = new Class[argTypes.length + injectedArgumentCount]; + parameterTypes[0] = long.class; + if (captureCallState) { + parameterTypes[1] = Object.class; + } + for (int i = 0; i < argTypes.length; i++) { + parameterTypes[i + injectedArgumentCount] = asJavaType(argTypes[i]); + } + return MethodType.methodType(asJavaType(resType), parameterTypes); + } + + @Override + protected Object createCapturedCallStateImpl(Object arena) { + throw unsupported(); + } + + @Override + protected int readCapturedErrnoImpl(Object state) { + throw unsupported(); + } + + @Override + protected int readCapturedGetLastErrorImpl(Object state) { + throw unsupported(); } @Override diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeContext.java index 97e7aa5947..acc3a6a888 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeContext.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeContext.java @@ -40,11 +40,14 @@ */ package com.oracle.graal.python.runtime.nativeaccess; +import static com.oracle.graal.python.annotations.NativeSimpleType.POINTER; +import static com.oracle.graal.python.annotations.NativeSimpleType.SINT32; +import static com.oracle.graal.python.annotations.NativeSimpleType.SINT64; + import java.lang.invoke.MethodHandle; import java.util.concurrent.ConcurrentLinkedQueue; import com.oracle.graal.python.PythonLanguage; -import com.oracle.graal.python.annotations.NativeSimpleType; import com.oracle.graal.python.annotations.PythonOS; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; @@ -67,6 +70,7 @@ public final class NativeContext { private final ConcurrentLinkedQueue libraries = new ConcurrentLinkedQueue<>(); private final NativeLibrary defaultLibrary; + private final ThreadLocal callState; final Object arena; public static NativeContext create() { @@ -77,6 +81,7 @@ public static NativeContext create() { NativeContext() { arena = NativeAccessSupport.createArena(); defaultLibrary = isWindows() ? null : new NativeLibrary(this, getPosixDefaultLibraryHandle()); + callState = ThreadLocal.withInitial(() -> NativeAccessSupport.createCapturedCallState(arena)); } public void close() { @@ -106,7 +111,8 @@ public NativeLibrary loadLibrary(String name, int flags) throws NativeLibraryLoa try { if (isWindows()) { int callFlags = sanitizeWindowsLoadLibraryFlags(flags) | WINDOWS_DEFAULT_LOAD_LIBRARY_FLAGS; - lib = (long) LOAD_LIBRARY_EX.invokeExact(loadLibraryExPtr, nativeName, 0L, callFlags); + Object callStateBuffer = callState.get(); + lib = (long) LOAD_LIBRARY_EX_CAPTURED.invoke(loadLibraryExPtr, callStateBuffer, nativeName, 0L, callFlags); } else { int callFlags = flags; if ((callFlags & (RTLD_LAZY | RTLD_NOW)) == 0) { @@ -120,7 +126,7 @@ public NativeLibrary loadLibrary(String name, int flags) throws NativeLibraryLoa NativeMemory.free(nativeName); } if (lib == 0) { - throw createLoadLibraryException(); + throw createLoadLibraryException(isWindows() ? getLastError() : 0); } NativeLibrary library = new NativeLibrary(this, lib); libraries.add(library); @@ -163,17 +169,14 @@ private static boolean isWindows() { private static final long RTLD_DEFAULT_LINUX = 0L; private static final long RTLD_DEFAULT_DARWIN = -2L; - private static final MethodHandle DLOPEN = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64, NativeSimpleType.POINTER, NativeSimpleType.SINT32); - private static final MethodHandle DLCLOSE = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT32, NativeSimpleType.SINT64); - private static final MethodHandle DLSYM = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64, NativeSimpleType.SINT64, NativeSimpleType.POINTER); - private static final MethodHandle LOAD_LIBRARY_EX = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64, NativeSimpleType.POINTER, NativeSimpleType.POINTER, - NativeSimpleType.SINT32); - private static final MethodHandle FREE_LIBRARY = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT32, NativeSimpleType.SINT64); - private static final MethodHandle GET_PROC_ADDRESS = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64, NativeSimpleType.SINT64, NativeSimpleType.POINTER); - private static final MethodHandle GET_LAST_ERROR = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT32); - private static final MethodHandle FORMAT_MESSAGE = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT32, NativeSimpleType.SINT32, NativeSimpleType.POINTER, NativeSimpleType.SINT32, - NativeSimpleType.SINT32, NativeSimpleType.POINTER, NativeSimpleType.SINT32, NativeSimpleType.POINTER); - private static final MethodHandle DLERROR = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64); + private static final MethodHandle DLOPEN = NativeAccessSupport.createDowncallHandle(false, false, SINT64, POINTER, SINT32); + private static final MethodHandle DLCLOSE = NativeAccessSupport.createDowncallHandle(false, false, SINT32, SINT64); + private static final MethodHandle DLSYM = NativeAccessSupport.createDowncallHandle(false, false, SINT64, SINT64, POINTER); + private static final MethodHandle FREE_LIBRARY = NativeAccessSupport.createDowncallHandle(false, false, SINT32, SINT64); + private static final MethodHandle GET_PROC_ADDRESS = NativeAccessSupport.createDowncallHandle(false, false, SINT64, SINT64, POINTER); + private static final MethodHandle FORMAT_MESSAGE = NativeAccessSupport.createDowncallHandle(false, false, SINT32, SINT32, POINTER, SINT32, SINT32, POINTER, SINT32, POINTER); + private static final MethodHandle DLERROR = NativeAccessSupport.createDowncallHandle(false, false, SINT64); + private static final MethodHandle LOAD_LIBRARY_EX_CAPTURED = isWindows() ? NativeAccessSupport.createDowncallHandle(false, true, SINT64, POINTER, POINTER, SINT32) : null; private static long dlopenPtr; private static long dlclosePtr; @@ -182,7 +185,6 @@ private static boolean isWindows() { private static long loadLibraryExPtr; private static long freeLibraryPtr; private static long getProcAddressPtr; - private static long getLastErrorPtr; private static long formatMessagePtr; private static Object windowsLookupArena; private static NativeLibraryLookup windowsLookup; @@ -192,8 +194,8 @@ private static void ensureLoader() throws UnsupportedOperationException { if (loadLibraryExPtr != 0) { assert freeLibraryPtr != 0; assert getProcAddressPtr != 0; - assert getLastErrorPtr != 0; assert formatMessagePtr != 0; + assert LOAD_LIBRARY_EX_CAPTURED != null; return; } if (windowsLookup == null) { @@ -203,7 +205,6 @@ private static void ensureLoader() throws UnsupportedOperationException { loadLibraryExPtr = NativeAccessSupport.lookupSymbol(windowsLookup, "LoadLibraryExW"); freeLibraryPtr = NativeAccessSupport.lookupSymbol(windowsLookup, "FreeLibrary"); getProcAddressPtr = NativeAccessSupport.lookupSymbol(windowsLookup, "GetProcAddress"); - getLastErrorPtr = NativeAccessSupport.lookupSymbol(windowsLookup, "GetLastError"); formatMessagePtr = NativeAccessSupport.lookupSymbol(windowsLookup, "FormatMessageW"); return; } @@ -218,14 +219,13 @@ private static int sanitizeWindowsLoadLibraryFlags(int flags) { } @TruffleBoundary - private static NativeLibraryLoadException createLoadLibraryException() { + private static NativeLibraryLoadException createLoadLibraryException(int windowsErrorCode) { if (isWindows()) { - int errorCode = getLastError(); - String detail = formatWindowsError(errorCode); + String detail = formatWindowsError(windowsErrorCode); if (detail == null || detail.isBlank()) { - return new NativeLibraryLoadException("Windows error " + errorCode); + return new NativeLibraryLoadException("Windows error " + windowsErrorCode); } - return new NativeLibraryLoadException("Windows error " + errorCode + ": " + detail); + return new NativeLibraryLoadException("Windows error " + windowsErrorCode + ": " + detail); } String detail = getDlError(); if (detail == null || detail.isBlank()) { @@ -234,14 +234,6 @@ private static NativeLibraryLoadException createLoadLibraryException() { return new NativeLibraryLoadException(detail); } - private static int getLastError() { - try { - return (int) GET_LAST_ERROR.invokeExact(getLastErrorPtr); - } catch (Throwable e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - } - private static String getDlError() { try { long ptr = (long) DLERROR.invokeExact(dlerrorPtr); @@ -280,4 +272,20 @@ private static long getPosixDefaultLibraryHandle() { default -> throw new UnsupportedOperationException("Default library is only available on POSIX platforms."); }; } + + public ThreadLocal getCapturedCallStateTL() { + return callState; + } + + /** Get the value of {@code errno} captured by the last downcall which captures it. The capture buffer is thread-local, so no races can happen. */ + @TruffleBoundary(allowInlining = true) + public int getErrno() { + return NativeAccessSupport.readCapturedErrno(callState.get()); + } + + /** Get the value of WinAPI's {@code GetLastError} captured by the last downcall which captures it. The capture buffer is thread-local, so no races can happen. */ + @TruffleBoundary(allowInlining = true) + public int getLastError() { + return NativeAccessSupport.readCapturedGetLastError(callState.get()); + } } diff --git a/graalpython/python-libposix/src/errno_capture.h b/graalpython/python-libposix/src/errno_capture.h new file mode 100644 index 0000000000..c1696a1957 --- /dev/null +++ b/graalpython/python-libposix/src/errno_capture.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2026, 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef PYTHON_LIBPOSIX_ERRNO_CAPTURE_H +#define PYTHON_LIBPOSIX_ERRNO_CAPTURE_H + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define THREAD_LOCAL _Thread_local +#elif defined(_MSC_VER) + #define THREAD_LOCAL __declspec(thread) +#elif defined(__GNUC__) || defined(__clang__) + #define THREAD_LOCAL __thread +#else + #error "No thread-local storage support for this compiler" +#endif + +extern THREAD_LOCAL int errno_capture; + +static inline void capture_errno(void) { + errno_capture = errno; +} + +#define CAPTURE_ERRNO_AND_RETURN(indicator, expr) do { \ +__typeof__(expr) return_value = (expr); \ +if (return_value == (indicator)) { \ +capture_errno(); \ +} \ +return return_value; \ +} while (0) + +#endif //PYTHON_LIBPOSIX_ERRNO_CAPTURE_H diff --git a/graalpython/python-libposix/src/fork_exec.c b/graalpython/python-libposix/src/fork_exec.c index 476f72c987..46005b0b44 100644 --- a/graalpython/python-libposix/src/fork_exec.c +++ b/graalpython/python-libposix/src/fork_exec.c @@ -21,6 +21,8 @@ #include #include +#include "errno_capture.h" + // These definitions emulate CPython's equivalents so that the copy&pasted code below works without too many changes #define HAVE_DIRFD 1 #define HAVE_SETSID 1 @@ -603,6 +605,7 @@ int32_t fork_exec( int err = pthread_sigmask(SIG_BLOCK, &allSigs, &oldSigs); if (err) { errno = err; + capture_errno(); return -1; } oldSigmask = &oldSigs; @@ -631,5 +634,6 @@ int32_t fork_exec( } #endif + capture_errno(); return pid; } diff --git a/graalpython/python-libposix/src/posix.c b/graalpython/python-libposix/src/posix.c index 12893a061c..2832edde43 100644 --- a/graalpython/python-libposix/src/posix.c +++ b/graalpython/python-libposix/src/posix.c @@ -77,6 +77,10 @@ #include #include +#include "errno_capture.h" + +THREAD_LOCAL int errno_capture = 0; + int32_t signal_self_segv(void); #ifdef __APPLE__ @@ -92,7 +96,7 @@ int32_t signal_self_segv(void); #include #endif -int64_t call_getpid() { +int64_t call_getpid(void) { return getpid(); } @@ -103,7 +107,8 @@ int32_t call_umask(int32_t mask) { int32_t get_inheritable(int32_t fd) { int flags = fcntl(fd, F_GETFD, 0); if (flags < 0) { - return -1; + capture_errno(); + return flags; } return !(flags & FD_CLOEXEC); } @@ -121,55 +126,57 @@ int32_t set_inheritable(int32_t fd, int32_t inheritable) { if (new_flags != res) { res = fcntl(fd, F_SETFD, new_flags); } + } else { + capture_errno(); } return res; } int32_t call_openat(int32_t dirFd, const char *pathname, int32_t flags, int32_t mode) { - return openat(dirFd, pathname, flags, mode); + CAPTURE_ERRNO_AND_RETURN(-1, openat(dirFd, pathname, flags, mode)); } int32_t call_close(int32_t fd) { - return close(fd); + CAPTURE_ERRNO_AND_RETURN(-1, close(fd)); } int64_t call_read(int32_t fd, void *buf, uint64_t count) { - return read(fd, buf, count); + CAPTURE_ERRNO_AND_RETURN(-1, read(fd, buf, count)); } int64_t call_write(int32_t fd, void *buf, uint64_t count) { - return write(fd, buf, count); + CAPTURE_ERRNO_AND_RETURN(-1, write(fd, buf, count)); } int32_t call_dup(int32_t fd) { - return fcntl(fd, F_DUPFD_CLOEXEC, 0); + CAPTURE_ERRNO_AND_RETURN(-1, fcntl(fd, F_DUPFD_CLOEXEC, 0)); } int32_t call_dup2(int32_t oldfd, int32_t newfd, int32_t inheritable) { #ifdef __gnu_linux__ if (!inheritable) { - return dup3(oldfd, newfd, O_CLOEXEC); + CAPTURE_ERRNO_AND_RETURN(-1, dup3(oldfd, newfd, O_CLOEXEC)); } #endif int res = dup2(oldfd, newfd); if (res < 0) { + capture_errno(); return res; } - if (!inheritable) { - if (set_inheritable(res, 0) < 0) { - close(res); - return -1; - } + if (!inheritable && set_inheritable(res, 0) < 0) { + close(res); + return -1; } return res; } int32_t call_pipe2(int32_t *pipefd) { #ifdef __gnu_linux__ - return pipe2(pipefd, O_CLOEXEC); + CAPTURE_ERRNO_AND_RETURN(-1, pipe2(pipefd, O_CLOEXEC)); #else int res = pipe(pipefd); if (res != 0) { + capture_errno(); return res; } if (set_inheritable(pipefd[0], 0) < 0 || set_inheritable(pipefd[1], 0) < 0) { @@ -220,7 +227,7 @@ int32_t call_select(int32_t nfds, int32_t* readfds, int32_t readfdsLen, fill_select_result(readfds, readfdsLen, &readfdsSet, selected, 0); fill_select_result(writefds, writefdsLen, &writefdsSet, selected, readfdsLen); fill_select_result(errfds, errfdsLen, &errfdsSet, selected, readfdsLen + writefdsLen); - return (int32_t) result; + CAPTURE_ERRNO_AND_RETURN(-1, (int32_t) result); } int32_t call_poll(int32_t fd, int32_t writing, int64_t timeoutSec, int64_t timeoutUsec) { @@ -242,37 +249,39 @@ int32_t call_poll(int32_t fd, int32_t writing, int64_t timeoutSec, int64_t timeo timeout_ms = -1; } else if (timeoutSec > INT_MAX / 1000) { errno = EINVAL; + capture_errno(); return -1; } else { int64_t timeout_ms_64 = timeoutSec * 1000 + timeoutUsec / 1000; if (timeout_ms_64 > INT_MAX) { errno = EINVAL; + capture_errno(); return -1; } timeout_ms = (int)timeout_ms_64; } - return poll(&pollfd, 1, timeout_ms); + CAPTURE_ERRNO_AND_RETURN(-1, poll(&pollfd, 1, timeout_ms)); #endif } int64_t call_lseek(int32_t fd, int64_t offset, int32_t whence) { - return lseek(fd, offset, whence); + CAPTURE_ERRNO_AND_RETURN(-1, lseek(fd, offset, whence)); } int32_t call_ftruncate(int32_t fd, int64_t length) { - return ftruncate(fd, length); + CAPTURE_ERRNO_AND_RETURN(-1, ftruncate(fd, length)); } int32_t call_truncate(const char* path, int64_t length) { - return truncate(path, length); + CAPTURE_ERRNO_AND_RETURN(-1, truncate(path, length)); } int32_t call_fsync(int32_t fd) { - return fsync(fd); + CAPTURE_ERRNO_AND_RETURN(-1, fsync(fd)); } int32_t call_flock(int32_t fd, int32_t operation) { - return flock(fd, operation); + CAPTURE_ERRNO_AND_RETURN(-1, flock(fd, operation)); } int32_t call_fcntl_lock(int32_t fd, int32_t blocking, int32_t lockType, int32_t whence, int64_t start, int64_t length) { @@ -281,12 +290,13 @@ int32_t call_fcntl_lock(int32_t fd, int32_t blocking, int32_t lockType, int32_t l.l_whence = whence; l.l_start = start; l.l_len = length; - return fcntl(fd, blocking ? F_SETLKW : F_SETLK, &l); + CAPTURE_ERRNO_AND_RETURN(-1, fcntl(fd, blocking ? F_SETLKW : F_SETLK, &l)); } int32_t get_blocking(int32_t fd) { int flags = fcntl(fd, F_GETFL, 0); - if (flags < 0) { + if (flags == -1) { + capture_errno(); return -1; } return !(flags & O_NONBLOCK); @@ -303,7 +313,7 @@ int32_t set_blocking(int32_t fd, int32_t blocking) { } res = fcntl(fd, F_SETFL, flags); } - return res; + CAPTURE_ERRNO_AND_RETURN(-1, res); } int32_t get_terminal_size(int32_t fd, int32_t *size) { @@ -312,6 +322,8 @@ int32_t get_terminal_size(int32_t fd, int32_t *size) { if (res == 0) { size[0] = w.ws_col; size[1] = w.ws_row; + } else { + capture_errno(); } return res; } @@ -347,6 +359,8 @@ int32_t call_fstatat(int32_t dirFd, const char *path, int32_t followSymlinks, in int result = fstatat(dirFd, path, &st, followSymlinks ? 0 : AT_SYMLINK_NOFOLLOW); if (result == 0) { stat_struct_to_longs(&st, out); + } else { + capture_errno(); } return result; } @@ -356,6 +370,8 @@ int32_t call_fstat(int32_t fd, int64_t *out) { int result = fstat(fd, &st); if (result == 0) { stat_struct_to_longs(&st, out); + } else { + capture_errno(); } return result; } @@ -380,6 +396,8 @@ int32_t call_statvfs(const char *path, int64_t *out) { int result = statvfs(path, &st); if (result == 0) { statvfs_struct_to_longs(&st, out); + } else { + capture_errno(); } return result; } @@ -389,6 +407,8 @@ int32_t call_fstatvfs(int32_t fd, int64_t *out) { int result = fstatvfs(fd, &st); if (result == 0) { statvfs_struct_to_longs(&st, out); + } else { + capture_errno(); } return result; } @@ -402,44 +422,46 @@ int32_t call_uname(char *sysname, char *nodename, char *release, char *version, snprintf(release, size, "%s", buf.release); snprintf(version, size, "%s", buf.version); snprintf(machine, size, "%s", buf.machine); + } else { + capture_errno(); } return result; } int32_t call_unlinkat(int32_t dirFd, const char *pathname, int32_t rmdir) { - return unlinkat(dirFd, pathname, rmdir ? AT_REMOVEDIR : 0); + CAPTURE_ERRNO_AND_RETURN(-1, unlinkat(dirFd, pathname, rmdir ? AT_REMOVEDIR : 0)); } int32_t call_linkat(int32_t oldDirFd, const char *oldPath, int32_t newDirFd, const char *newPath, int32_t flags) { - return linkat(oldDirFd, oldPath, newDirFd, newPath, flags); + CAPTURE_ERRNO_AND_RETURN(-1, linkat(oldDirFd, oldPath, newDirFd, newPath, flags)); } int32_t call_symlinkat(const char *target, int32_t dirFd, const char *linkpath) { - return symlinkat(target, dirFd, linkpath); + CAPTURE_ERRNO_AND_RETURN(-1, symlinkat(target, dirFd, linkpath)); } int32_t call_mkdirat(int32_t dirFd, const char *pathname, int32_t mode) { - return mkdirat(dirFd, pathname, mode); + CAPTURE_ERRNO_AND_RETURN(-1, mkdirat(dirFd, pathname, mode)); } int32_t call_getcwd(char *buf, uint64_t size) { - return getcwd(buf, size) == NULL ? -1 : 0; + CAPTURE_ERRNO_AND_RETURN(-1, getcwd(buf, size) == NULL ? -1 : 0); } int32_t call_chdir(const char *path) { - return chdir(path); + CAPTURE_ERRNO_AND_RETURN(-1, chdir(path)); } int32_t call_fchdir(int32_t fd) { - return fchdir(fd); + CAPTURE_ERRNO_AND_RETURN(-1, fchdir(fd)); } int32_t call_fchown(int32_t fd, int64_t owner, int64_t group) { - return fchown(fd, owner, group); + CAPTURE_ERRNO_AND_RETURN(-1, fchown(fd, owner, group)); } int32_t call_fchownat(int32_t dirfd, const char *pathname, int64_t owner, int64_t group, int32_t followSymlinks) { - return fchownat(dirfd, pathname, owner, group, followSymlinks ? 0 : AT_SYMLINK_NOFOLLOW); + CAPTURE_ERRNO_AND_RETURN(-1, fchownat(dirfd, pathname, owner, group, followSymlinks ? 0 : AT_SYMLINK_NOFOLLOW)); } int32_t call_isatty(int32_t fd) { @@ -447,15 +469,15 @@ int32_t call_isatty(int32_t fd) { } intptr_t call_opendir(const char *name) { - return (intptr_t) opendir(name); + CAPTURE_ERRNO_AND_RETURN(0, (intptr_t) opendir(name)); } intptr_t call_fdopendir(int32_t fd) { - return (intptr_t) fdopendir(fd); + CAPTURE_ERRNO_AND_RETURN(0, (intptr_t) fdopendir(fd)); } int32_t call_closedir(intptr_t dirp) { - return closedir((DIR *) dirp); + CAPTURE_ERRNO_AND_RETURN(-1, closedir((DIR *) dirp)); } int32_t call_readdir(intptr_t dirp, char *nameBuf, uint64_t nameBufSize, int64_t *out) { @@ -467,6 +489,10 @@ int32_t call_readdir(intptr_t dirp, char *nameBuf, uint64_t nameBufSize, int64_t out[1] = dirEntry->d_type; return 1; } + if (errno != 0) { + capture_errno(); + return -1; + } return 0; } @@ -477,72 +503,72 @@ void call_rewinddir(intptr_t dirp) { #ifdef __gnu_linux__ int32_t call_utimensat(int32_t dirFd, const char *path, int64_t *timespec, int32_t followSymlinks) { if (!timespec) { - return utimensat(dirFd, path, NULL, followSymlinks ? 0 : AT_SYMLINK_NOFOLLOW); + CAPTURE_ERRNO_AND_RETURN(-1, utimensat(dirFd, path, NULL, followSymlinks ? 0 : AT_SYMLINK_NOFOLLOW)); } else { struct timespec times[2]; times[0].tv_sec = timespec[0]; times[0].tv_nsec = timespec[1]; times[1].tv_sec = timespec[2]; times[1].tv_nsec = timespec[3]; - return utimensat(dirFd, path, times, followSymlinks ? 0 : AT_SYMLINK_NOFOLLOW); + CAPTURE_ERRNO_AND_RETURN(-1, utimensat(dirFd, path, times, followSymlinks ? 0 : AT_SYMLINK_NOFOLLOW)); } } int32_t call_futimens(int32_t fd, int64_t *timespec) { if (!timespec) { - return futimens(fd, NULL); + CAPTURE_ERRNO_AND_RETURN(-1, futimens(fd, NULL)); } else { struct timespec times[2]; times[0].tv_sec = timespec[0]; times[0].tv_nsec = timespec[1]; times[1].tv_sec = timespec[2]; times[1].tv_nsec = timespec[3]; - return futimens(fd, times); + CAPTURE_ERRNO_AND_RETURN(-1, futimens(fd, times)); } } #endif int32_t call_futimes(int32_t fd, int64_t *timeval) { if (!timeval) { - return futimes(fd, NULL); + CAPTURE_ERRNO_AND_RETURN(-1, futimes(fd, NULL)); } else { struct timeval times[2]; times[0].tv_sec = timeval[0]; times[0].tv_usec = timeval[1]; times[1].tv_sec = timeval[2]; times[1].tv_usec = timeval[3]; - return futimes(fd, times); + CAPTURE_ERRNO_AND_RETURN(-1, futimes(fd, times)); } } int32_t call_lutimes(const char *filename, int64_t *timeval) { if (!timeval) { - return lutimes(filename, NULL); + CAPTURE_ERRNO_AND_RETURN(-1, lutimes(filename, NULL)); } else { struct timeval times[2]; times[0].tv_sec = timeval[0]; times[0].tv_usec = timeval[1]; times[1].tv_sec = timeval[2]; times[1].tv_usec = timeval[3]; - return lutimes(filename, times); + CAPTURE_ERRNO_AND_RETURN(-1, lutimes(filename, times)); } } int32_t call_utimes(const char *filename, int64_t *timeval) { if (!timeval) { - return utimes(filename, NULL); + CAPTURE_ERRNO_AND_RETURN(-1, utimes(filename, NULL)); } else { struct timeval times[2]; times[0].tv_sec = timeval[0]; times[0].tv_usec = timeval[1]; times[1].tv_sec = timeval[2]; times[1].tv_usec = timeval[3]; - return utimes(filename, times); + CAPTURE_ERRNO_AND_RETURN(-1, utimes(filename, times)); } } int32_t call_renameat(int32_t oldDirFd, const char *oldPath, int32_t newDirFd, const char *newPath) { - return renameat(oldDirFd, oldPath, newDirFd, newPath); + CAPTURE_ERRNO_AND_RETURN(-1, renameat(oldDirFd, oldPath, newDirFd, newPath)); } int32_t call_faccessat(int32_t dirFd, const char *path, int32_t mode, int32_t effectiveIds, int32_t followSymlinks) { @@ -553,23 +579,23 @@ int32_t call_faccessat(int32_t dirFd, const char *path, int32_t mode, int32_t ef if (effectiveIds) { flags |= AT_EACCESS; } - return faccessat(dirFd, path, mode, flags); + CAPTURE_ERRNO_AND_RETURN(-1, faccessat(dirFd, path, mode, flags)); } int32_t call_fchmodat(int32_t dirFd, const char *path, int32_t mode, int32_t followSymlinks) { - return fchmodat(dirFd, path, mode, followSymlinks ? 0 : AT_SYMLINK_NOFOLLOW); + CAPTURE_ERRNO_AND_RETURN(-1, fchmodat(dirFd, path, mode, followSymlinks ? 0 : AT_SYMLINK_NOFOLLOW)); } int32_t call_fchmod(int32_t fd, int32_t mode) { - return fchmod(fd, mode); + CAPTURE_ERRNO_AND_RETURN(-1, fchmod(fd, mode)); } int64_t call_readlinkat(int32_t dirFd, const char *path, char *buf, uint64_t size) { - return readlinkat(dirFd, path, buf, size); + CAPTURE_ERRNO_AND_RETURN(-1, readlinkat(dirFd, path, buf, size)); } int64_t call_waitpid(int64_t pid, int32_t *status, int32_t options) { - return waitpid(pid, status, options); + CAPTURE_ERRNO_AND_RETURN(-1, waitpid(pid, status, options)); } int32_t call_wcoredump(int32_t status) { @@ -605,7 +631,7 @@ int32_t call_wstopsig(int32_t status) { } int32_t call_kill(int64_t pid, int32_t signal) { - return kill(pid, signal); + CAPTURE_ERRNO_AND_RETURN(-1, kill(pid, signal)); } int32_t call_raise(int32_t signal) { @@ -635,6 +661,8 @@ int32_t call_getitimer(int32_t which, int64_t *current_value) { int32_t result = getitimer(which, ¤t); if (result == 0) { itimerval_to_long_array(¤t, current_value); + } else { + capture_errno(); } return result; } @@ -646,6 +674,8 @@ int32_t call_setitimer(int32_t which, int64_t *new_value, int64_t *old_value) { int32_t result = setitimer(which, &new_timer, &old_timer); if (result == 0) { itimerval_to_long_array(&old_timer, old_value); + } else { + capture_errno(); } return result; } @@ -659,53 +689,54 @@ int32_t signal_self(int32_t signal) { return signal_self_segv(); default: errno = EINVAL; + capture_errno(); return -1; } _exit(128 + signal); } int32_t call_killpg(int64_t pgid, int32_t signal) { - return killpg(pgid, signal); + CAPTURE_ERRNO_AND_RETURN(-1, killpg(pgid, signal)); } -int64_t call_getuid() { +int64_t call_getuid(void) { return getuid(); } -int64_t call_geteuid() { +int64_t call_geteuid(void) { return geteuid(); } -int64_t call_getgid() { +int64_t call_getgid(void) { return getgid(); } -int64_t call_getegid() { +int64_t call_getegid(void) { return getegid(); } -int64_t call_getppid() { +int64_t call_getppid(void) { return getppid(); } int64_t call_getpgid(int64_t pid) { - return getpgid(pid); + CAPTURE_ERRNO_AND_RETURN(-1, getpgid(pid)); } int32_t call_setpgid(int64_t pid, int64_t pgid) { - return setpgid(pid, pgid); + CAPTURE_ERRNO_AND_RETURN(-1, setpgid(pid, pgid)); } -int64_t call_getpgrp() { +int64_t call_getpgrp(void) { return getpgrp(); } int64_t call_getsid(int64_t pid) { - return getsid(pid); + CAPTURE_ERRNO_AND_RETURN(-1, getsid(pid)); } int64_t call_setsid() { - return setsid(); + CAPTURE_ERRNO_AND_RETURN(-1, setsid()); } int32_t call_getgroups(int64_t size, int64_t* out) { @@ -720,9 +751,9 @@ int32_t call_getgroups(int64_t size, int64_t* out) { out[i] = tmp[i]; } free(tmp); - return res; + CAPTURE_ERRNO_AND_RETURN(-1, res); } else { - return getgroups(size, NULL); + CAPTURE_ERRNO_AND_RETURN(-1, getgroups(size, NULL)); } } @@ -730,8 +761,8 @@ int32_t call_getrusage(int32_t who, uint64_t* out) { #ifndef _WIN32 struct rusage ru; int result = getrusage(who, &ru); - if (result != 0) { - return result; + if (result == -1) { + CAPTURE_ERRNO_AND_RETURN(-1, result); } int offset = 0; // POSIX prescribes only ru_utime and ru_stime members, macOS and Linux @@ -757,16 +788,17 @@ int32_t call_getrusage(int32_t who, uint64_t* out) { COPYLONG(ru.ru_nsignals); COPYLONG(ru.ru_nvcsw); COPYLONG(ru.ru_nivcsw); - return 0; + CAPTURE_ERRNO_AND_RETURN(-1, 0); # undef COPYLONG # undef COPYDOUBLE #else - return -1; + errno = ENOSYS; + capture_errno(); #endif } int32_t call_openpty(int32_t *outvars) { - return openpty(outvars, outvars + 1, NULL, NULL, NULL); + CAPTURE_ERRNO_AND_RETURN(-1, openpty(outvars, outvars + 1, NULL, NULL, NULL)); } int32_t call_ctermid(char *buf) { @@ -774,11 +806,11 @@ int32_t call_ctermid(char *buf) { } int32_t call_setenv(char *name, char *value, int overwrite) { - return setenv(name, value, overwrite); + CAPTURE_ERRNO_AND_RETURN(-1, setenv(name, value, overwrite)); } int32_t call_unsetenv(char *name) { - return unsetenv(name); + CAPTURE_ERRNO_AND_RETURN(-1, unsetenv(name)); } // See comment in NativePosixSupport.execv() for the description of arguments @@ -792,6 +824,7 @@ void call_execv(char *data, int64_t *offsets, int32_t offsetsLen) { char *pathname = strings[0]; char **argv = strings + 1; execv(pathname, argv); + capture_errno(); } int32_t call_system(const char *pathname) { @@ -800,11 +833,15 @@ int32_t call_system(const char *pathname) { int64_t call_mmap(int64_t length, int32_t prot, int32_t flags, int32_t fd, int64_t offset) { void *result = mmap(NULL, length, prot, flags, fd, offset); - return result == MAP_FAILED ? 0 : (int64_t) result; + if (result == MAP_FAILED) { + capture_errno(); + return 0; + } + return (int64_t) result; } int32_t call_munmap(int64_t address, int64_t length) { - return munmap((void *) address, length); + CAPTURE_ERRNO_AND_RETURN(-1, munmap((void *) address, length)); } void call_msync(int64_t address, int64_t offset, int64_t length) { @@ -814,7 +851,7 @@ void call_msync(int64_t address, int64_t offset, int64_t length) { } int32_t call_socket(int32_t family, int32_t type, int32_t protocol) { - return socket(family, type, protocol); + CAPTURE_ERRNO_AND_RETURN(-1, socket(family, type, protocol)); } // On Java side, socket addresses are stored in a Java byte[] (here represented by a int8_t *). @@ -831,23 +868,23 @@ int32_t call_accept(int32_t sockfd, int8_t *addr, int32_t *addr_len) { *addr_len = (int32_t)l; // ...so this unsigned->signed conversion is well defined memcpy(addr, &sa, l); } - return res; + CAPTURE_ERRNO_AND_RETURN(-1, res); } int32_t call_bind(int32_t sockfd, int8_t *addr, int32_t addr_len) { struct sockaddr_storage sa; memcpy(&sa, addr, addr_len); - return bind(sockfd, (struct sockaddr *) &sa, addr_len); + CAPTURE_ERRNO_AND_RETURN(-1, bind(sockfd, (struct sockaddr *) &sa, addr_len)); } int32_t call_connect(int32_t sockfd, int8_t *addr, int32_t addr_len) { struct sockaddr_storage sa; memcpy(&sa, addr, addr_len); - return connect(sockfd, (struct sockaddr *) &sa, addr_len); + CAPTURE_ERRNO_AND_RETURN(-1, connect(sockfd, (struct sockaddr *) &sa, addr_len)); } int32_t call_listen(int32_t sockfd, int32_t backlog) { - return listen(sockfd, backlog); + CAPTURE_ERRNO_AND_RETURN(-1, listen(sockfd, backlog)); } int32_t call_getpeername(int32_t sockfd, int8_t *addr, int32_t *addr_len) { @@ -858,6 +895,8 @@ int32_t call_getpeername(int32_t sockfd, int8_t *addr, int32_t *addr_len) { assert(l <= sizeof(sockaddr_storage)); // l is small enough to be representable by int32_t... *addr_len = (int32_t)l; // ...so this unsigned->signed conversion is well defined memcpy(addr, &sa, l); + } else { + capture_errno(); } return res; } @@ -870,39 +909,46 @@ int32_t call_getsockname(int32_t sockfd, int8_t *addr, int32_t *addr_len) { assert(l <= sizeof(sockaddr_storage)); // l is small enough to be representable by int32_t... *addr_len = (int32_t)l; // ...so this unsigned->signed conversion is well defined memcpy(addr, &sa, l); + } else { + capture_errno(); } return res; } //TODO len should be size_t, retval should be ssize_t int32_t call_send(int32_t sockfd, void *buf, int32_t len, int32_t flags) { - return send(sockfd, buf, len, flags); + ssize_t res = send(sockfd, buf, len, flags); + assert (res == (int32_t) res); + CAPTURE_ERRNO_AND_RETURN(-1, res); } int32_t call_sendto(int32_t sockfd, void *buf, int32_t offset, int32_t len, int32_t flags, int8_t *addr, int32_t addr_len) { struct sockaddr_storage sa; memcpy(&sa, addr, addr_len); - return sendto(sockfd, buf + offset, len, flags, (struct sockaddr *) &sa, addr_len); + CAPTURE_ERRNO_AND_RETURN(-1, sendto(sockfd, buf + offset, len, flags, (struct sockaddr *) &sa, addr_len)); } int32_t call_recv(int32_t sockfd, void *buf, int32_t len, int32_t flags) { - return recv(sockfd, buf, len, flags); + CAPTURE_ERRNO_AND_RETURN(-1, recv(sockfd, buf, len, flags)); } int32_t call_recvfrom(int32_t sockfd, void *buf, int32_t offset, int32_t len, int32_t flags, int8_t *src_addr, int32_t *addr_len) { struct sockaddr_storage sa; socklen_t l = sizeof(sa); - int res = recvfrom(sockfd, buf + offset, len, flags, (struct sockaddr *) &sa, &l); + ssize_t res = recvfrom(sockfd, buf + offset, len, flags, (struct sockaddr *) &sa, &l); if (res != -1) { assert(l <= sizeof(sockaddr_storage)); // l is small enough to be representable by int32_t... *addr_len = (int32_t)l; // ...so this unsigned->signed conversion is well defined memcpy(src_addr, &sa, l); + } else { + capture_errno(); } - return res; + assert (res == (int32_t) res); + return (int32_t) res; } int32_t call_shutdown(int32_t sockfd, int32_t how) { - return shutdown(sockfd, how); + CAPTURE_ERRNO_AND_RETURN(-1, shutdown(sockfd, how)); } #define MAX_SOCKOPT_LEN 1024 @@ -921,12 +967,15 @@ int32_t call_getsockopt(int32_t sockfd, int32_t level, int32_t optname, void *bu if (len > sizeof(alignedBuf)) { // If this ever happens, we can increase MAX_SOCKOPT_LEN or use malloc. errno = ENOMEM; + capture_errno(); return -1; } int res = getsockopt(sockfd, level, optname, alignedBuf, &len); if (res == 0) { *bufLen = len; memcpy(buf, alignedBuf, len); + } else { + capture_errno(); } return res; } @@ -936,10 +985,11 @@ int32_t call_setsockopt(int32_t sockfd, int32_t level, int32_t optname, void *bu char alignedBuf[MAX_SOCKOPT_LEN] __attribute__ ((aligned)); if (bufLen > sizeof(alignedBuf)) { errno = ENOMEM; + capture_errno(); return -1; } memcpy(alignedBuf, buf, bufLen); - return setsockopt(sockfd, level, optname, alignedBuf, bufLen); + CAPTURE_ERRNO_AND_RETURN(-1, setsockopt(sockfd, level, optname, alignedBuf, bufLen)); } int32_t call_inet_addr(const char *src) { @@ -966,16 +1016,16 @@ int32_t call_inet_ntoa(int32_t src, char *dst) { } int32_t call_inet_pton(int32_t family, const char *src, void *dst) { - return inet_pton(family, src, dst); + CAPTURE_ERRNO_AND_RETURN(-1, inet_pton(family, src, dst)); } int32_t call_inet_ntop(int32_t family, void *src, char *dst, int32_t dstSize) { const char *r = inet_ntop(family, src, dst, dstSize); - return r == NULL ? -1 : 0; + CAPTURE_ERRNO_AND_RETURN(-1, r == NULL ? -1 : 0); } int32_t call_gethostname(char *buf, int64_t bufLen) { - return gethostname(buf, bufLen); + CAPTURE_ERRNO_AND_RETURN(-1, gethostname(buf, bufLen)); } int32_t call_getnameinfo(int8_t *addr, int32_t addr_len, char *hostBuf, int32_t hostBufLen, char *servBuf, int32_t servBufLen, int32_t flags) { @@ -1035,50 +1085,54 @@ int32_t get_addrinfo_members(int64_t ptr, int32_t *intData, int64_t *longData, i sem_t* call_sem_open(const char *name, int32_t openFlags, int32_t mode, int32_t value) { sem_t* result = sem_open(name, openFlags, mode, value); - if (result == (sem_t*)SEM_FAILED) { - return NULL; + if (result == SEM_FAILED) { + capture_errno(); } return result; } int32_t call_sem_close(sem_t* handle) { - return sem_close(handle); + CAPTURE_ERRNO_AND_RETURN(-1, sem_close(handle)); } int32_t call_sem_unlink(const char *name) { - return sem_unlink(name); + CAPTURE_ERRNO_AND_RETURN(-1, sem_unlink(name)); } #ifdef __linux__ int32_t call_sem_getvalue(sem_t* handle, int32_t *value) { int valueInt; int res = sem_getvalue(handle, &valueInt); - *value = valueInt; + if (res == 0) { + *value = valueInt; + } else { + capture_errno(); + } return res; } #endif int32_t call_sem_post(sem_t* handle) { - return sem_post(handle); + CAPTURE_ERRNO_AND_RETURN(-1, sem_post(handle)); } int32_t call_sem_wait(sem_t* handle) { - return sem_wait(handle); + CAPTURE_ERRNO_AND_RETURN(-1, sem_wait(handle)); } int32_t call_sem_trywait(sem_t* handle) { - return sem_trywait(handle); + CAPTURE_ERRNO_AND_RETURN(-1, sem_trywait(handle)); } #ifdef __linux__ int32_t call_sem_timedwait(sem_t* handle, int64_t deadlineNs) { const int64_t nsInSec = 1000 * 1000 * 1000; struct timespec deadline = {deadlineNs / nsInSec, deadlineNs % nsInSec}; - return sem_timedwait(handle, &deadline); + CAPTURE_ERRNO_AND_RETURN(-1, sem_timedwait(handle, &deadline)); } #endif -int32_t get_sysconf_getpw_r_size_max() { +int64_t get_sysconf_getpw_r_size_max(void) { return sysconf(_SC_GETPW_R_SIZE_MAX); } @@ -1129,11 +1183,11 @@ int32_t call_getpwname_r(const char *name, char *buffer, int32_t bufferSize, uin // Following 3 functions are not thread safe: -void call_setpwent() { +void call_setpwent(void) { setpwent(); } -void call_endpwent() { +void call_endpwent(void) { endpwent(); } @@ -1143,6 +1197,8 @@ struct passwd *call_getpwent(int64_t *bufferSize) { // the +3 is for terminating '\0' *bufferSize = strlen(p->pw_name) + strlen(p->pw_dir) + strlen(p->pw_shell) + 3; } + // always capture errno because NULL result may also be a valid result + capture_errno(); return p; } @@ -1170,24 +1226,26 @@ int32_t get_getpwent_data(struct passwd *p, char *buffer, int32_t bufferSize, ui } int32_t call_ioctl_bytes(int32_t fd, uint64_t request, char* buffer) { - return ioctl(fd, request, buffer); + CAPTURE_ERRNO_AND_RETURN(-1, ioctl(fd, request, buffer)); } int32_t call_ioctl_int(int32_t fd, uint64_t request, int32_t arg) { - return ioctl(fd, request, (int)arg); + CAPTURE_ERRNO_AND_RETURN(-1, ioctl(fd, request, (int)arg)); } int64_t call_sysconf(int32_t name) { errno = 0; - return sysconf(name); + int64_t result = sysconf(name); + if (result == -1 && errno != 0) { + capture_errno(); + // Private sentinel: sysconf itself may return -1 without an error. + return INT64_MIN; + } + return result; } int32_t get_errno() { - return errno; -} - -void set_errno(int e) { - errno = e; + return errno_capture; } #ifdef _WIN32