Skip to content

[Stdlib] Fix entry-point-based process args #3108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 31 additions & 9 deletions lib/Immediate/Immediate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,30 +37,32 @@
#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 <dlfcn.h>
#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
return dlopen(runtimeLibPathWithName.str().c_str(), RTLD_LAZY | RTLD_GLOBAL);
#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);
}

Expand Down Expand Up @@ -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<const char *, 32> 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<llvm::Function*, 8> InitFns;
llvm::SmallPtrSet<swift::Module *, 8> ImportedModules;
if (IRGenImportedModules(CI, *Module, ImportedModules, InitFns,
Expand All @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion lib/Immediate/ImmediateImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ namespace swift {

namespace immediate {

bool loadSwiftRuntime(StringRef runtimeLibPath);
// Returns a handle to the runtime suitable for other 'dlsym' or 'dlclose'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: please make this a proper doc comment, so that it shows up in Quick Help.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll get it when I remove the compiler intrinsic.

// calls or 'NULL' if an error occured.
void *loadSwiftRuntime(StringRef runtimeLibPath);
bool tryLoadLibraries(ArrayRef<LinkLibrary> LinkLibraries,
SearchPathOptions SearchPathOpts,
DiagnosticEngine &Diags);
Expand Down
3 changes: 0 additions & 3 deletions stdlib/public/SwiftShims/GlobalObjects.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions stdlib/public/SwiftShims/RuntimeStubs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
61 changes: 21 additions & 40 deletions stdlib/public/core/Process.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,69 +12,43 @@

import SwiftShims

internal class _Box<Wrapped> {
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..<Int(argc) {
let arg = argv[i]!
let converted = String(cString: arg)
result.append(converted)
}
return result
}

/// The backing static variable for argument count may come either from the
/// entry point or it may need to be computed e.g. if we're in the REPL.
@_versioned
internal static var _argc: Int32 = Int32()

/// The backing static variable for arguments may come either from the
/// entry point or it may need to be computed e.g. if we're in the REPL.
///
/// Care must be taken to ensure that `_swift_stdlib_getUnsafeArgvArgc` is
/// not invoked more times than is necessary (at most once).
@_versioned
internal static var _unsafeArgv:
UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?
= nil
UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>
= _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
}

/// Access to the raw argv value from C. Accessing the argument vector
/// through this pointer is unsafe.
public static var unsafeArgv:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can even flatten this indirection, and just have _argc, argc, and unsafeArgv.

UnsafeMutablePointer<UnsafeMutablePointer<Int8>?> {
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<AnyObject?>(
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..<Int(argc)).map { String(cString: _unsafeArgv[$0]!) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably use unsafeArgv rather than _unsafeArgv. It's equivalent, sure, but this way no one has to think about whether the order is correct.

}

/// Intrinsic entry point invoked on entry to a standalone program's "main".
// FIXME(ABI): Remove this and the entrypoints in SILGen.
@_transparent
public // COMPILER_INTRINSIC
func _stdlib_didEnterMain(
Expand All @@ -85,3 +59,10 @@ func _stdlib_didEnterMain(
Process._argc = Int32(argc)
Process._unsafeArgv = argv
}

// FIXME: Move this to HashedCollections.swift.gyb
internal class _Box<Wrapped> {
internal var _value: Wrapped
internal init(_ value: Wrapped) { self._value = value }
}

2 changes: 1 addition & 1 deletion stdlib/public/runtime/Leaks.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
//===----------------------------------------------------------------------===//

Expand Down
1 change: 1 addition & 0 deletions stdlib/public/stubs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ endif()

add_swift_library(swiftStdlibStubs OBJECT_LIBRARY TARGET_LIBRARY
Assert.cpp
CommandLine.cpp
GlobalObjects.cpp
LibcShims.cpp
Stubs.cpp
Expand Down
120 changes: 120 additions & 0 deletions stdlib/public/stubs/CommandLine.cpp
Original file line number Diff line number Diff line change
@@ -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 <vector>
#include <string>
#include <cassert>
#include <climits>
#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#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<char *> 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

3 changes: 0 additions & 3 deletions stdlib/public/stubs/GlobalObjects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions test/1_stdlib/Inputs/ProcessStressTest/ProcessStressTest.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include "ProcessStressTest.h"

int main(int argc, char **argv) {
swift_process_test_getProcessArgs();
return 0;
}
2 changes: 2 additions & 0 deletions test/1_stdlib/Inputs/ProcessStressTest/ProcessStressTest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Declared in ProcessStressTest.swift.
extern void swift_process_test_getProcessArgs(void);
Loading