diff --git a/lib/Immediate/Immediate.cpp b/lib/Immediate/Immediate.cpp index e2832712d5a26..b1e7a6f66580f 100644 --- a/lib/Immediate/Immediate.cpp +++ b/lib/Immediate/Immediate.cpp @@ -37,15 +37,17 @@ #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Support/Path.h" + #if defined(_MSC_VER) #include "Windows.h" #else #include #endif + using namespace swift; using namespace swift::immediate; -static bool loadRuntimeLib(StringRef runtimeLibPathWithName) { +static void *loadRuntimeLib(StringRef runtimeLibPathWithName) { #if defined(_MSC_VER) return LoadLibrary(runtimeLibPathWithName.str().c_str()); #else @@ -53,14 +55,14 @@ static bool loadRuntimeLib(StringRef runtimeLibPathWithName) { #endif } -static bool loadRuntimeLib(StringRef sharedLibName, StringRef runtimeLibPath) { +static void *loadRuntimeLib(StringRef sharedLibName, StringRef runtimeLibPath) { // FIXME: Need error-checking. llvm::SmallString<128> Path = runtimeLibPath; llvm::sys::path::append(Path, sharedLibName); return loadRuntimeLib(Path); } -bool swift::immediate::loadSwiftRuntime(StringRef runtimeLibPath) { +void *swift::immediate::loadSwiftRuntime(StringRef runtimeLibPath) { return loadRuntimeLib("libswiftCore" LTDL_SHLIB_EXT, runtimeLibPath); } @@ -303,6 +305,32 @@ int swift::RunImmediately(CompilerInstance &CI, const ProcessCmdLine &CmdLine, if (Context.hadError()) return -1; + // Load libSwiftCore to setup process arguments. + // + // This must be done here, before any library loading has been done, to avoid + // racing with the static initializers in user code. + auto stdlib = loadSwiftRuntime(Context.SearchPathOpts.RuntimeLibraryPath); + if (!stdlib) { + CI.getDiags().diagnose(SourceLoc(), + diag::error_immediate_mode_missing_stdlib); + return -1; + } + + // Setup interpreted process arguments. + using ArgOverride = void (*)(const char **, int); + auto emplaceProcessArgs + = (ArgOverride)dlsym(stdlib, "_swift_stdlib_overrideUnsafeArgvArgc"); + if (dlerror()) + return -1; + + SmallVector argBuf; + for (size_t i = 0; i < CmdLine.size(); ++i) { + argBuf.push_back(CmdLine[i].c_str()); + } + argBuf.push_back(nullptr); + + (*emplaceProcessArgs)(argBuf.data(), CmdLine.size()); + SmallVector InitFns; llvm::SmallPtrSet ImportedModules; if (IRGenImportedModules(CI, *Module, ImportedModules, InitFns, @@ -313,12 +341,6 @@ int swift::RunImmediately(CompilerInstance &CI, const ProcessCmdLine &CmdLine, PMBuilder.OptLevel = 2; PMBuilder.Inliner = llvm::createFunctionInliningPass(200); - if (!loadSwiftRuntime(Context.SearchPathOpts.RuntimeLibraryPath)) { - CI.getDiags().diagnose(SourceLoc(), - diag::error_immediate_mode_missing_stdlib); - return -1; - } - // Build the ExecutionEngine. llvm::EngineBuilder builder(std::move(ModuleOwner)); std::string ErrorMsg; diff --git a/lib/Immediate/ImmediateImpl.h b/lib/Immediate/ImmediateImpl.h index 3b0b979eb6386..b3be672433fcd 100644 --- a/lib/Immediate/ImmediateImpl.h +++ b/lib/Immediate/ImmediateImpl.h @@ -34,7 +34,9 @@ namespace swift { namespace immediate { -bool loadSwiftRuntime(StringRef runtimeLibPath); +// Returns a handle to the runtime suitable for other 'dlsym' or 'dlclose' +// calls or 'NULL' if an error occured. +void *loadSwiftRuntime(StringRef runtimeLibPath); bool tryLoadLibraries(ArrayRef LinkLibraries, SearchPathOptions SearchPathOpts, DiagnosticEngine &Diags); diff --git a/stdlib/public/SwiftShims/GlobalObjects.h b/stdlib/public/SwiftShims/GlobalObjects.h index 3e20f2e622496..7c9f21f49cf3e 100644 --- a/stdlib/public/SwiftShims/GlobalObjects.h +++ b/stdlib/public/SwiftShims/GlobalObjects.h @@ -42,9 +42,6 @@ struct _SwiftEmptyArrayStorage _swiftEmptyArrayStorage; extern SWIFT_RUNTIME_STDLIB_INTERFACE __swift_uint64_t _swift_stdlib_HashingDetail_fixedSeedOverride; -extern SWIFT_RUNTIME_STDLIB_INTERFACE -void *_swift_stdlib_ProcessArguments; - #ifdef __cplusplus }} // extern "C", namespace swift #endif diff --git a/stdlib/public/SwiftShims/RuntimeStubs.h b/stdlib/public/SwiftShims/RuntimeStubs.h index 6b520a066de29..f96c9d93cddda 100644 --- a/stdlib/public/SwiftShims/RuntimeStubs.h +++ b/stdlib/public/SwiftShims/RuntimeStubs.h @@ -31,6 +31,14 @@ SWIFT_RUNTIME_STDLIB_INTERFACE __swift_ssize_t swift_stdlib_readLine_stdin(char * _Nullable * _Nonnull LinePtr); +SWIFT_RUNTIME_STDLIB_INTERFACE +char * _Nullable * _Nonnull +_swift_stdlib_getUnsafeArgvArgc(int * _Nonnull outArgLen); + +SWIFT_RUNTIME_STDLIB_INTERFACE +void +_swift_stdlib_overrideUnsafeArgvArgc(char * _Nullable * _Nonnull argv, int argc); + SWIFT_END_NULLABILITY_ANNOTATIONS #ifdef __cplusplus diff --git a/stdlib/public/core/Process.swift b/stdlib/public/core/Process.swift index 64f64ed7f670d..0d54c8fa5a381 100644 --- a/stdlib/public/core/Process.swift +++ b/stdlib/public/core/Process.swift @@ -12,36 +12,26 @@ import SwiftShims -internal class _Box { - internal var _value: Wrapped - internal init(_ value: Wrapped) { self._value = value } -} - /// Command-line arguments for the current process. public enum Process { - /// Return an array of string containing the list of command-line arguments - /// with which the current process was invoked. - internal static func _computeArguments() -> [String] { - var result: [String] = [] - let argv = unsafeArgv - for i in 0..?>? - = nil + UnsafeMutablePointer?> + = _swift_stdlib_getUnsafeArgvArgc(&_argc) /// Access to the raw argc value from C. public static var argc: Int32 { + _ = Process.unsafeArgv // Force evaluation of argv. return _argc } @@ -49,32 +39,16 @@ public enum Process { /// through this pointer is unsafe. public static var unsafeArgv: UnsafeMutablePointer?> { - return _unsafeArgv! + return _unsafeArgv } /// Access to the swift arguments, also use lazy initialization of static /// properties to safely initialize the swift arguments. - /// - /// NOTE: we can not use static lazy let initializer as they can be moved - /// around by the optimizer which will break the data dependence on argc - /// and argv. - public static var arguments: [String] { - let argumentsPtr = UnsafeMutablePointer( - Builtin.addressof(&_swift_stdlib_ProcessArguments)) - - // Check whether argument has been initialized. - if let arguments = _stdlib_atomicLoadARCRef(object: argumentsPtr) { - return (arguments as! _Box<[String]>)._value - } - - let arguments = _Box<[String]>(_computeArguments()) - _stdlib_atomicInitializeARCRef(object: argumentsPtr, desired: arguments) - - return arguments._value - } + public static var arguments: [String] + = (0.. { + internal var _value: Wrapped + internal init(_ value: Wrapped) { self._value = value } +} + diff --git a/stdlib/public/runtime/Leaks.h b/stdlib/public/runtime/Leaks.h index 27b2f17d87823..fa233e61dae52 100644 --- a/stdlib/public/runtime/Leaks.h +++ b/stdlib/public/runtime/Leaks.h @@ -12,7 +12,7 @@ // // This is a very simple leak detector implementation that detects objects that // are allocated but not deallocated in a region. It is purposefully behind a -// flag since it is not meant to be used in +// flag since it is not meant to be used in production yet. // //===----------------------------------------------------------------------===// diff --git a/stdlib/public/stubs/CMakeLists.txt b/stdlib/public/stubs/CMakeLists.txt index 954657352f82d..1f4f15134fe4a 100644 --- a/stdlib/public/stubs/CMakeLists.txt +++ b/stdlib/public/stubs/CMakeLists.txt @@ -16,6 +16,7 @@ endif() add_swift_library(swiftStdlibStubs OBJECT_LIBRARY TARGET_LIBRARY Assert.cpp + CommandLine.cpp GlobalObjects.cpp LibcShims.cpp Stubs.cpp diff --git a/stdlib/public/stubs/CommandLine.cpp b/stdlib/public/stubs/CommandLine.cpp new file mode 100644 index 0000000000000..9c1b67c6f4c56 --- /dev/null +++ b/stdlib/public/stubs/CommandLine.cpp @@ -0,0 +1,120 @@ +//===--- CommandLine.cpp - OS-specific command line arguments -------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2016 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// OS-specific command line argument handling is defined here. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "swift/Runtime/Debug.h" + +#include "../SwiftShims/RuntimeStubs.h" +#include "../SwiftShims/GlobalObjects.h" + +// Backing storage for overrides of `Swift.Process.arguments`. +static char **_swift_stdlib_ProcessOverrideUnsafeArgv = nullptr; +static int _swift_stdlib_ProcessOverrideUnsafeArgc = 0; + +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE +extern "C" void _swift_stdlib_overrideUnsafeArgvArgc(char **argv, int argc) { + _swift_stdlib_ProcessOverrideUnsafeArgv = argv; + _swift_stdlib_ProcessOverrideUnsafeArgc = argc; +} + +#if defined(__APPLE__) +// NOTE: forward declare this rather than including crt_externs.h as not all +// SDKs provide it +extern "C" char ***_NSGetArgv(void); +extern "C" int *_NSGetArgc(void); + +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE +extern "C" char ** _swift_stdlib_getUnsafeArgvArgc(int *outArgLen) { + assert(outArgLen != nullptr); + + if (_swift_stdlib_ProcessOverrideUnsafeArgv) { + *outArgLen = _swift_stdlib_ProcessOverrideUnsafeArgc; + return _swift_stdlib_ProcessOverrideUnsafeArgv; + } + + *outArgLen = *_NSGetArgc(); + return *_NSGetArgv(); +} +#elif defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE +extern "C" char ** _swift_stdlib_getUnsafeArgvArgc(int *outArgLen) { + assert(outArgLen != nullptr); + + if (_swift_stdlib_ProcessOverrideUnsafeArgv) { + *outArgLen = _swift_stdlib_ProcessOverrideUnsafeArgc; + return _swift_stdlib_ProcessOverrideUnsafeArgv; + } + + FILE *cmdline = fopen("/proc/self/cmdline", "rb"); + if (!cmdline) { + swift::fatalError(0, + "fatal error: Unable to open interface to '/proc/self/cmdline'.\n"); + } + char *arg = nullptr; + size_t size = 0; + std::vector argvec; + while (getdelim(&arg, &size, 0, cmdline) != -1) { + argvec.push_back(strdup(arg)); + } + if (arg) { + free(arg); + } + fclose(cmdline); + *outArgLen = argvec.size(); + char **outBuf = (char **)calloc(argvec.size() + 1, sizeof(char *)); + std::copy(argvec.begin(), argvec.end(), outBuf); + outBuf[argvec.size()] = nullptr; + + return outBuf; +} +#elif defined (_MSC_VER) +extern int *__argc; +extern char **__argv; + +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE +extern "C" char ** _swift_stdlib_getUnsafeArgvArgc(int *outArgLen) { + assert(outArgLen != nullptr); + + if (_swift_stdlib_ProcessOverrideUnsafeArgv) { + *outArgLen = _swift_stdlib_ProcessOverrideUnsafeArgc; + return _swift_stdlib_ProcessOverrideUnsafeArgv; + } + + *outArgLen = __argc; + return __argv; +} +#else // __ANDROID__; Add your favorite arch's command line arg grabber here. +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE +extern "C" char ** _swift_stdlib_getUnsafeArgvArgc(int *outArgLen) { + if (_swift_stdlib_ProcessOverrideUnsafeArgv) { + *outArgLen = _swift_stdlib_ProcessOverrideUnsafeArgc; + return _swift_stdlib_ProcessOverrideUnsafeArgv; + } + + swift::fatalError(0, + "fatal error: Command line arguments not supported on this platform.\n"); +} +#endif + diff --git a/stdlib/public/stubs/GlobalObjects.cpp b/stdlib/public/stubs/GlobalObjects.cpp index 11a5d7ee6d2a1..f2d463f03d2be 100644 --- a/stdlib/public/stubs/GlobalObjects.cpp +++ b/stdlib/public/stubs/GlobalObjects.cpp @@ -41,9 +41,6 @@ swift::_SwiftEmptyArrayStorage swift::_swiftEmptyArrayStorage = { __swift_uint64_t swift::_swift_stdlib_HashingDetail_fixedSeedOverride = 0; -/// Backing storage for Swift.Process.arguments. -void *swift::_swift_stdlib_ProcessArguments = nullptr; - namespace llvm { namespace hashing { namespace detail { // An extern variable expected by LLVM's hashing templates. We don't link any // LLVM libs into the runtime, so define this here. diff --git a/test/1_stdlib/Inputs/ProcessStressTest/ProcessStressTest.c b/test/1_stdlib/Inputs/ProcessStressTest/ProcessStressTest.c new file mode 100644 index 0000000000000..a4ce77a0ecaec --- /dev/null +++ b/test/1_stdlib/Inputs/ProcessStressTest/ProcessStressTest.c @@ -0,0 +1,6 @@ +#include "ProcessStressTest.h" + +int main(int argc, char **argv) { + swift_process_test_getProcessArgs(); + return 0; +} diff --git a/test/1_stdlib/Inputs/ProcessStressTest/ProcessStressTest.h b/test/1_stdlib/Inputs/ProcessStressTest/ProcessStressTest.h new file mode 100644 index 0000000000000..fbdf898b19e6e --- /dev/null +++ b/test/1_stdlib/Inputs/ProcessStressTest/ProcessStressTest.h @@ -0,0 +1,2 @@ +// Declared in ProcessStressTest.swift. +extern void swift_process_test_getProcessArgs(void); diff --git a/test/1_stdlib/Inputs/ProcessStressTest/ProcessStressTest.swift b/test/1_stdlib/Inputs/ProcessStressTest/ProcessStressTest.swift new file mode 100644 index 0000000000000..7afac49544a77 --- /dev/null +++ b/test/1_stdlib/Inputs/ProcessStressTest/ProcessStressTest.swift @@ -0,0 +1,53 @@ +// Do not change the SIL name for this without also changing ProcessStressTest.c +@_silgen_name("swift_process_test_getProcessArgs") +public func runTest() { + let ProcessRaceTestSuite = TestSuite("Process Race") + + ProcessRaceTestSuite.test("passes") { + runRaceTest(ProcessRace.self, trials: 1) + } + + runAllTests() +} + +import StdlibUnittest + +struct ProcessRace : RaceTestWithPerTrialData { + class ProcessRaceData { + init() {} + } + + typealias ThreadLocalData = Void + typealias Observation = Observation1UInt + + func makeRaceData() -> ProcessRaceData { + return ProcessRaceData() + } + + func makeThreadLocalData() -> Void { + return Void() + } + + func thread1( + _ raceData: ProcessRaceData, _ threadLocalData: inout ThreadLocalData + ) -> Observation { + let argptr = Process.unsafeArgv + return Observation(unsafeBitCast(argptr, to: UInt.self)) + } + + func evaluateObservations( + _ observations: [Observation], + _ sink: (RaceTestObservationEvaluation) -> Void + ) { + guard let fstObs = observations.first?.data1 else { + return + } + for observation in observations { + if observation.data1 == fstObs { + sink(.pass) + } else { + sink(.failure) + } + } + } +} diff --git a/test/1_stdlib/Process.swift b/test/1_stdlib/Process.swift new file mode 100644 index 0000000000000..1205c3ac22aec --- /dev/null +++ b/test/1_stdlib/Process.swift @@ -0,0 +1,12 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// +// +// RUN: %target-build-swift %S/Inputs/ProcessStressTest/ProcessStressTest.swift -parse-as-library -force-single-frontend-invocation -module-name ProcessStressTestSwift -emit-object -o %t/ProcessStressTestSwift.o +// RUN: %clang -arch %target-cpu -c -o %t/ProcessStressTest.o -x c %S/Inputs/ProcessStressTest/ProcessStressTest.c +// RUN: %target-build-swift %t/ProcessStressTest.o %t/ProcessStressTestSwift.o -o %t/ProcessStressTest +// RUN: %target-run %t/ProcessStressTest foo bar baz qux quux corge grault garply waldo fred plugh xyzzy and thud +// REQUIRES: executable_test + +// This file is an empty stub to call into the process stress test which +// houses `main`.