11/*
2- * Copyright (c) 2020, 2023 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 2020, 2024 , Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * The Universal Permissive License (UPL), Version 1.0
4141// skip GIL
4242package com .oracle .graal .python .runtime ;
4343
44+ import static com .oracle .graal .python .nodes .StringLiterals .J_DEFAULT ;
4445import static com .oracle .graal .python .nodes .StringLiterals .J_NATIVE ;
4546import static com .oracle .graal .python .nodes .StringLiterals .J_NFI_LANGUAGE ;
4647import static com .oracle .graal .python .nodes .StringLiterals .T_LLVM_LANGUAGE ;
9192import org .graalvm .nativeimage .ImageInfo ;
9293
9394import com .oracle .graal .python .PythonLanguage ;
95+ import com .oracle .graal .python .builtins .PythonBuiltinClassType ;
9496import com .oracle .graal .python .builtins .PythonOS ;
9597import com .oracle .graal .python .builtins .objects .exception .OSErrorEnum ;
98+ import com .oracle .graal .python .nodes .ErrorMessages ;
99+ import com .oracle .graal .python .nodes .PRaiseNode ;
96100import com .oracle .graal .python .runtime .PosixSupportLibrary .AcceptResult ;
97101import com .oracle .graal .python .runtime .PosixSupportLibrary .AddrInfoCursor ;
98102import com .oracle .graal .python .runtime .PosixSupportLibrary .AddrInfoCursorLibrary ;
@@ -297,7 +301,7 @@ private enum PosixNativeFunction {
297301 call_sem_trywait ("(pointer):sint32" ),
298302 call_sem_timedwait ("(pointer, sint64):sint32" ),
299303
300- call_crypt ("([sint8], [sint8], [sint32 ]):sint64" );
304+ crypt ("([sint8], [sint8]):sint64" );
301305
302306 private final String signature ;
303307
@@ -377,20 +381,8 @@ private static String getLibPath(PythonContext context) {
377381 @ TruffleBoundary
378382 private static void loadLibrary (NFIPosixSupport posix ) {
379383 String path = getLibPath (posix .context );
380- String backend = posix .nfiBackend .toJavaStringUncached ();
381- Env env = posix .context .getEnv ();
382-
383- posix .context .ensureNFILanguage (null , "PosixModuleBackend" , "native" );
384-
385- String withClause = backend .equals (J_NATIVE ) ? "" : "with " + backend ;
386- String src = String .format ("%sload (RTLD_LOCAL) \" %s\" " , withClause , path );
387- Source loadSrc = Source .newBuilder (J_NFI_LANGUAGE , src , "load:" + SUPPORTING_NATIVE_LIB_NAME ).internal (true ).build ();
388-
389- if (LOGGER .isLoggable (Level .FINE )) {
390- LOGGER .fine (String .format ("Loading native library: %s" , src ));
391- }
392384 try {
393- posix .nfiLibrary = env . parseInternal ( loadSrc ). call ( );
385+ posix .nfiLibrary = loadLibrary ( posix , path );
394386 } catch (Throwable e ) {
395387 throw new UnsupportedOperationException (String .format ("""
396388 Could not load posix support library from path '%s'. Troubleshooting:\s
@@ -400,6 +392,28 @@ private static void loadLibrary(NFIPosixSupport posix) {
400392 }
401393 }
402394
395+ @ TruffleBoundary
396+ private static Object loadLibrary (NFIPosixSupport posix , String path ) {
397+ String backend = posix .nfiBackend .toJavaStringUncached ();
398+ Env env = posix .context .getEnv ();
399+
400+ posix .context .ensureNFILanguage (null , "PosixModuleBackend" , "native" );
401+
402+ Source loadSrc ;
403+ if (path != null ) {
404+ String withClause = backend .equals (J_NATIVE ) ? "" : "with " + backend ;
405+ String src = String .format ("%sload (RTLD_LOCAL) \" %s\" " , withClause , path );
406+ if (LOGGER .isLoggable (Level .FINE )) {
407+ LOGGER .fine (String .format ("Loading native library: %s" , src ));
408+ }
409+ loadSrc = Source .newBuilder (J_NFI_LANGUAGE , src , "load:" + SUPPORTING_NATIVE_LIB_NAME ).internal (true ).build ();
410+ } else {
411+ loadSrc = Source .newBuilder (J_NFI_LANGUAGE , J_DEFAULT , J_DEFAULT ).internal (true ).build ();
412+ }
413+
414+ return env .parseInternal (loadSrc ).call ();
415+ }
416+
403417 @ TruffleBoundary
404418 private static void loadFunction (NFIPosixSupport posix , Object library , PosixNativeFunction function ) {
405419 Object unbound ;
@@ -430,6 +444,7 @@ public InteropLibrary getResultInterop() {
430444 private final PythonContext context ;
431445 private final TruffleString nfiBackend ;
432446 private volatile Object nfiLibrary ;
447+ private volatile Object cryptLibrary ;
433448 private final AtomicReferenceArray <Object > cachedFunctions ;
434449 @ CompilationFinal (dimensions = 1 ) private long [] constantValues ;
435450
@@ -1848,7 +1863,24 @@ public TruffleString crypt(TruffleString word, TruffleString salt,
18481863 @ Shared ("tsCopyBytes" ) @ Cached TruffleString .CopyToByteArrayNode copyToByteArrayNode ,
18491864 @ Shared ("tsFromBytes" ) @ Cached TruffleString .FromByteArrayNode fromByteArrayNode ,
18501865 @ Shared ("fromUtf8" ) @ Cached TruffleString .SwitchEncodingNode switchEncodingFromUtf8Node ) throws PosixException {
1851- int [] lenArray = new int [1 ];
1866+ /*
1867+ * We don't want to link the posix library with libcrypt, because it might not be available
1868+ * on the target system and would make the whole posix library fail to load. So we load it
1869+ * dynamically on demand.
1870+ */
1871+ if (injectBranchProbability (SLOWPATH_PROBABILITY , cryptLibrary == null )) {
1872+ try {
1873+ cryptLibrary = InvokeNativeFunction .loadLibrary (this , PythonOS .getPythonOS () != PythonOS .PLATFORM_DARWIN ? "libcrypt.so" : null );
1874+ } catch (Throwable e ) {
1875+ CompilerDirectives .transferToInterpreterAndInvalidate ();
1876+ throw PRaiseNode .raiseUncached (invokeNode , PythonBuiltinClassType .SystemError , ErrorMessages .UNABLE_TO_LOAD_LIBCRYPT );
1877+ }
1878+ }
1879+ PosixNativeFunction function = PosixNativeFunction .crypt ;
1880+ if (injectBranchProbability (SLOWPATH_PROBABILITY , cachedFunctions .get (function .ordinal ()) == null )) {
1881+ InvokeNativeFunction .loadFunction (this , cryptLibrary , function );
1882+ }
1883+ Object funObject = cachedFunctions .get (function .ordinal ());
18521884 /*
18531885 * From the manpage: Upon successful completion, crypt returns a pointer to a string which
18541886 * encodes both the hashed passphrase, and the settings that were used to encode it. See
@@ -1857,15 +1889,26 @@ public TruffleString crypt(TruffleString word, TruffleString salt,
18571889 * safe to call crypt from multiple threads simultaneously. Upon error, it may return a NULL
18581890 * pointer or a pointer to an invalid hash, depending on the implementation.
18591891 */
1860- // Note GIL is not enough as crypt is using global memory so we need a really global lock
1892+ // Note GIL is not enough as crypt is using global memory, so we need a really global lock
18611893 synchronized (CRYPT_LOCK ) {
1862- long resultPtr = invokeNode .callLong (this , PosixNativeFunction .call_crypt , stringToUTF8CString (word , switchEncodingToUtf8Node , copyToByteArrayNode ),
1863- stringToUTF8CString (salt , switchEncodingToUtf8Node , copyToByteArrayNode ), wrap (lenArray ));
1894+ long resultPtr ;
1895+ Object [] args = new Object []{
1896+ stringToUTF8CString (word , switchEncodingToUtf8Node , copyToByteArrayNode ),
1897+ stringToUTF8CString (salt , switchEncodingToUtf8Node , copyToByteArrayNode )};
1898+ try {
1899+ Object interopResult = invokeNode .functionInterop .execute (funObject , args );
1900+ resultPtr = invokeNode .getResultInterop ().asLong (interopResult );
1901+ } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e ) {
1902+ throw CompilerDirectives .shouldNotReachHere (e );
1903+ }
18641904 // CPython doesn't handle the case of "invalid hash" return specially and neither do we
18651905 if (resultPtr == 0 ) {
18661906 throw getErrnoAndThrowPosixException (invokeNode );
18671907 }
1868- int len = lenArray [0 ];
1908+ int len = 0 ;
1909+ while (UNSAFE .getByte (resultPtr + len ) != 0 ) {
1910+ len ++;
1911+ }
18691912 byte [] resultBytes = new byte [len ];
18701913 UNSAFE .copyMemory (null , resultPtr , resultBytes , Unsafe .ARRAY_BYTE_BASE_OFFSET , len );
18711914 return createString (resultBytes , 0 , resultBytes .length , false , fromByteArrayNode , switchEncodingFromUtf8Node );
0 commit comments