Skip to content

Commit dc88e51

Browse files
committed
Nuke entry-point-based process args
Provides a new fallback for Process arguments for those instances where we do not own main (e.g. Frameworks, Objective-C owns main.m or main.c, etc.). This includes a number of platform-specific specializations of argument grabbing logic and a new thread-safe interface to Process.unsafeArgv. main() | _NSGetArgc/_NSGetArgv | /proc/self/cmdline | __argc/__argv --------|--------------------------|------------------------|--------------- Scripts | OS X, iOS, tvOS, watchOS | Linux, FreeBSD, Cygwin | Windows For interpreted Swift where we must filter out the arguments we now do so by loading the standard library and calling into new SPI to override the arguments that would have been grabbed by the runtime. This implementation completely subsumes the use of the entry point '_stdlib_didEnterMain' and it will be removed in a future commit.
1 parent 93c8784 commit dc88e51

File tree

13 files changed

+258
-57
lines changed

13 files changed

+258
-57
lines changed

lib/Immediate/Immediate.cpp

+31-9
Original file line numberDiff line numberDiff line change
@@ -37,30 +37,32 @@
3737
#include "llvm/Transforms/IPO.h"
3838
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
3939
#include "llvm/Support/Path.h"
40+
4041
#if defined(_MSC_VER)
4142
#include "Windows.h"
4243
#else
4344
#include <dlfcn.h>
4445
#endif
46+
4547
using namespace swift;
4648
using namespace swift::immediate;
4749

48-
static bool loadRuntimeLib(StringRef runtimeLibPathWithName) {
50+
static void *loadRuntimeLib(StringRef runtimeLibPathWithName) {
4951
#if defined(_MSC_VER)
5052
return LoadLibrary(runtimeLibPathWithName.str().c_str());
5153
#else
5254
return dlopen(runtimeLibPathWithName.str().c_str(), RTLD_LAZY | RTLD_GLOBAL);
5355
#endif
5456
}
5557

56-
static bool loadRuntimeLib(StringRef sharedLibName, StringRef runtimeLibPath) {
58+
static void *loadRuntimeLib(StringRef sharedLibName, StringRef runtimeLibPath) {
5759
// FIXME: Need error-checking.
5860
llvm::SmallString<128> Path = runtimeLibPath;
5961
llvm::sys::path::append(Path, sharedLibName);
6062
return loadRuntimeLib(Path);
6163
}
6264

63-
bool swift::immediate::loadSwiftRuntime(StringRef runtimeLibPath) {
65+
void *swift::immediate::loadSwiftRuntime(StringRef runtimeLibPath) {
6466
return loadRuntimeLib("libswiftCore" LTDL_SHLIB_EXT, runtimeLibPath);
6567
}
6668

@@ -303,6 +305,32 @@ int swift::RunImmediately(CompilerInstance &CI, const ProcessCmdLine &CmdLine,
303305
if (Context.hadError())
304306
return -1;
305307

308+
// Load libSwiftCore to setup process arguments.
309+
//
310+
// This must be done here, before any library loading has been done, to avoid
311+
// racing with the static initializers in user code.
312+
auto stdlib = loadSwiftRuntime(Context.SearchPathOpts.RuntimeLibraryPath);
313+
if (!stdlib) {
314+
CI.getDiags().diagnose(SourceLoc(),
315+
diag::error_immediate_mode_missing_stdlib);
316+
return -1;
317+
}
318+
319+
// Setup interpreted process arguments.
320+
using ArgOverride = void (*)(const char **, int);
321+
auto emplaceProcessArgs
322+
= (ArgOverride)dlsym(stdlib, "_swift_stdlib_overrideUnsafeArgvArgc");
323+
if (dlerror())
324+
return -1;
325+
326+
SmallVector<const char *, 32> argBuf;
327+
for (size_t i = 0; i < CmdLine.size(); ++i) {
328+
argBuf.push_back(CmdLine[i].c_str());
329+
}
330+
argBuf.push_back(nullptr);
331+
332+
(*emplaceProcessArgs)(argBuf.data(), CmdLine.size());
333+
306334
SmallVector<llvm::Function*, 8> InitFns;
307335
llvm::SmallPtrSet<swift::Module *, 8> ImportedModules;
308336
if (IRGenImportedModules(CI, *Module, ImportedModules, InitFns,
@@ -313,12 +341,6 @@ int swift::RunImmediately(CompilerInstance &CI, const ProcessCmdLine &CmdLine,
313341
PMBuilder.OptLevel = 2;
314342
PMBuilder.Inliner = llvm::createFunctionInliningPass(200);
315343

316-
if (!loadSwiftRuntime(Context.SearchPathOpts.RuntimeLibraryPath)) {
317-
CI.getDiags().diagnose(SourceLoc(),
318-
diag::error_immediate_mode_missing_stdlib);
319-
return -1;
320-
}
321-
322344
// Build the ExecutionEngine.
323345
llvm::EngineBuilder builder(std::move(ModuleOwner));
324346
std::string ErrorMsg;

lib/Immediate/ImmediateImpl.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ namespace swift {
3434

3535
namespace immediate {
3636

37-
bool loadSwiftRuntime(StringRef runtimeLibPath);
37+
// Returns a handle to the runtime suitable for other 'dlsym' or 'dlclose'
38+
// calls or 'NULL' if an error occured.
39+
void *loadSwiftRuntime(StringRef runtimeLibPath);
3840
bool tryLoadLibraries(ArrayRef<LinkLibrary> LinkLibraries,
3941
SearchPathOptions SearchPathOpts,
4042
DiagnosticEngine &Diags);

stdlib/public/SwiftShims/GlobalObjects.h

-3
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,6 @@ struct _SwiftEmptyArrayStorage _swiftEmptyArrayStorage;
4242
extern SWIFT_RUNTIME_STDLIB_INTERFACE
4343
__swift_uint64_t _swift_stdlib_HashingDetail_fixedSeedOverride;
4444

45-
extern SWIFT_RUNTIME_STDLIB_INTERFACE
46-
void *_swift_stdlib_ProcessArguments;
47-
4845
#ifdef __cplusplus
4946
}} // extern "C", namespace swift
5047
#endif

stdlib/public/SwiftShims/RuntimeStubs.h

+8
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ SWIFT_RUNTIME_STDLIB_INTERFACE
3131
__swift_ssize_t
3232
swift_stdlib_readLine_stdin(char * _Nullable * _Nonnull LinePtr);
3333

34+
SWIFT_RUNTIME_STDLIB_INTERFACE
35+
char * _Nullable * _Nonnull
36+
_swift_stdlib_getUnsafeArgvArgc(int * _Nonnull outArgLen);
37+
38+
SWIFT_RUNTIME_STDLIB_INTERFACE
39+
void
40+
_swift_stdlib_overrideUnsafeArgvArgc(char * _Nullable * _Nonnull argv, int argc);
41+
3442
SWIFT_END_NULLABILITY_ANNOTATIONS
3543

3644
#ifdef __cplusplus

stdlib/public/core/Process.swift

+21-40
Original file line numberDiff line numberDiff line change
@@ -12,69 +12,43 @@
1212

1313
import SwiftShims
1414

15-
internal class _Box<Wrapped> {
16-
internal var _value: Wrapped
17-
internal init(_ value: Wrapped) { self._value = value }
18-
}
19-
2015
/// Command-line arguments for the current process.
2116
public enum Process {
22-
/// Return an array of string containing the list of command-line arguments
23-
/// with which the current process was invoked.
24-
internal static func _computeArguments() -> [String] {
25-
var result: [String] = []
26-
let argv = unsafeArgv
27-
for i in 0..<Int(argc) {
28-
let arg = argv[i]!
29-
let converted = String(cString: arg)
30-
result.append(converted)
31-
}
32-
return result
33-
}
34-
17+
/// The backing static variable for argument count may come either from the
18+
/// entry point or it may need to be computed e.g. if we're in the REPL.
3519
@_versioned
3620
internal static var _argc: Int32 = Int32()
3721

22+
/// The backing static variable for arguments may come either from the
23+
/// entry point or it may need to be computed e.g. if we're in the REPL.
24+
///
25+
/// Care must be taken to ensure that `_swift_stdlib_getUnsafeArgvArgc` is
26+
/// not invoked more times than is necessary (at most once).
3827
@_versioned
3928
internal static var _unsafeArgv:
40-
UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?
41-
= nil
29+
UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>
30+
= _swift_stdlib_getUnsafeArgvArgc(&_argc)
4231

4332
/// Access to the raw argc value from C.
4433
public static var argc: Int32 {
34+
_ = Process.unsafeArgv // Force evaluation of argv.
4535
return _argc
4636
}
4737

4838
/// Access to the raw argv value from C. Accessing the argument vector
4939
/// through this pointer is unsafe.
5040
public static var unsafeArgv:
5141
UnsafeMutablePointer<UnsafeMutablePointer<Int8>?> {
52-
return _unsafeArgv!
42+
return _unsafeArgv
5343
}
5444

5545
/// Access to the swift arguments, also use lazy initialization of static
5646
/// properties to safely initialize the swift arguments.
57-
///
58-
/// NOTE: we can not use static lazy let initializer as they can be moved
59-
/// around by the optimizer which will break the data dependence on argc
60-
/// and argv.
61-
public static var arguments: [String] {
62-
let argumentsPtr = UnsafeMutablePointer<AnyObject?>(
63-
Builtin.addressof(&_swift_stdlib_ProcessArguments))
64-
65-
// Check whether argument has been initialized.
66-
if let arguments = _stdlib_atomicLoadARCRef(object: argumentsPtr) {
67-
return (arguments as! _Box<[String]>)._value
68-
}
69-
70-
let arguments = _Box<[String]>(_computeArguments())
71-
_stdlib_atomicInitializeARCRef(object: argumentsPtr, desired: arguments)
72-
73-
return arguments._value
74-
}
47+
public static var arguments: [String]
48+
= (0..<Int(argc)).map { String(cString: _unsafeArgv[$0]!) }
7549
}
7650

77-
/// Intrinsic entry point invoked on entry to a standalone program's "main".
51+
// FIXME(ABI): Remove this and the entrypoints in SILGen.
7852
@_transparent
7953
public // COMPILER_INTRINSIC
8054
func _stdlib_didEnterMain(
@@ -85,3 +59,10 @@ func _stdlib_didEnterMain(
8559
Process._argc = Int32(argc)
8660
Process._unsafeArgv = argv
8761
}
62+
63+
// FIXME: Move this to HashedCollections.swift.gyb
64+
internal class _Box<Wrapped> {
65+
internal var _value: Wrapped
66+
internal init(_ value: Wrapped) { self._value = value }
67+
}
68+

stdlib/public/runtime/Leaks.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
//
1313
// This is a very simple leak detector implementation that detects objects that
1414
// are allocated but not deallocated in a region. It is purposefully behind a
15-
// flag since it is not meant to be used in
15+
// flag since it is not meant to be used in production yet.
1616
//
1717
//===----------------------------------------------------------------------===//
1818

stdlib/public/stubs/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ endif()
1616

1717
add_swift_library(swiftStdlibStubs OBJECT_LIBRARY TARGET_LIBRARY
1818
Assert.cpp
19+
CommandLine.cpp
1920
GlobalObjects.cpp
2021
LibcShims.cpp
2122
Stubs.cpp

stdlib/public/stubs/CommandLine.cpp

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
//===--- CommandLine.cpp - OS-specific command line arguments -------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2016 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// OS-specific command line argument handling is defined here.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#include <vector>
18+
#include <string>
19+
#include <cassert>
20+
#include <climits>
21+
#include <cstdarg>
22+
#include <cstdint>
23+
#include <cstdio>
24+
#include <cstdlib>
25+
#include <cstring>
26+
27+
#include "swift/Runtime/Debug.h"
28+
29+
#include "../SwiftShims/RuntimeStubs.h"
30+
#include "../SwiftShims/GlobalObjects.h"
31+
32+
// Backing storage for overrides of `Swift.Process.arguments`.
33+
static char **_swift_stdlib_ProcessOverrideUnsafeArgv = nullptr;
34+
static int _swift_stdlib_ProcessOverrideUnsafeArgc = 0;
35+
36+
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
37+
extern "C" void _swift_stdlib_overrideUnsafeArgvArgc(char **argv, int argc) {
38+
_swift_stdlib_ProcessOverrideUnsafeArgv = argv;
39+
_swift_stdlib_ProcessOverrideUnsafeArgc = argc;
40+
}
41+
42+
#if defined(__APPLE__)
43+
// NOTE: forward declare this rather than including crt_externs.h as not all
44+
// SDKs provide it
45+
extern "C" char ***_NSGetArgv(void);
46+
extern "C" int *_NSGetArgc(void);
47+
48+
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
49+
extern "C" char ** _swift_stdlib_getUnsafeArgvArgc(int *outArgLen) {
50+
assert(outArgLen != nullptr);
51+
52+
if (_swift_stdlib_ProcessOverrideUnsafeArgv) {
53+
*outArgLen = _swift_stdlib_ProcessOverrideUnsafeArgc;
54+
return _swift_stdlib_ProcessOverrideUnsafeArgv;
55+
}
56+
57+
*outArgLen = *_NSGetArgc();
58+
return *_NSGetArgv();
59+
}
60+
#elif defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__)
61+
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
62+
extern "C" char ** _swift_stdlib_getUnsafeArgvArgc(int *outArgLen) {
63+
assert(outArgLen != nullptr);
64+
65+
if (_swift_stdlib_ProcessOverrideUnsafeArgv) {
66+
*outArgLen = _swift_stdlib_ProcessOverrideUnsafeArgc;
67+
return _swift_stdlib_ProcessOverrideUnsafeArgv;
68+
}
69+
70+
FILE *cmdline = fopen("/proc/self/cmdline", "rb");
71+
if (!cmdline) {
72+
swift::fatalError(0,
73+
"fatal error: Unable to open interface to '/proc/self/cmdline'.\n");
74+
}
75+
char *arg = nullptr;
76+
size_t size = 0;
77+
std::vector<char *> argvec;
78+
while (getdelim(&arg, &size, 0, cmdline) != -1) {
79+
argvec.push_back(strdup(arg));
80+
}
81+
if (arg) {
82+
free(arg);
83+
}
84+
fclose(cmdline);
85+
*outArgLen = argvec.size();
86+
char **outBuf = (char **)calloc(argvec.size() + 1, sizeof(char *));
87+
std::copy(argvec.begin(), argvec.end(), outBuf);
88+
outBuf[argvec.size()] = nullptr;
89+
90+
return outBuf;
91+
}
92+
#elif defined (_MSC_VER)
93+
extern int *__argc;
94+
extern char **__argv;
95+
96+
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
97+
extern "C" char ** _swift_stdlib_getUnsafeArgvArgc(int *outArgLen) {
98+
assert(outArgLen != nullptr);
99+
100+
if (_swift_stdlib_ProcessOverrideUnsafeArgv) {
101+
*outArgLen = _swift_stdlib_ProcessOverrideUnsafeArgc;
102+
return _swift_stdlib_ProcessOverrideUnsafeArgv;
103+
}
104+
105+
*outArgLen = __argc;
106+
return __argv;
107+
}
108+
#else // __ANDROID__; Add your favorite arch's command line arg grabber here.
109+
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
110+
extern "C" char ** _swift_stdlib_getUnsafeArgvArgc(int *outArgLen) {
111+
if (_swift_stdlib_ProcessOverrideUnsafeArgv) {
112+
*outArgLen = _swift_stdlib_ProcessOverrideUnsafeArgc;
113+
return _swift_stdlib_ProcessOverrideUnsafeArgv;
114+
}
115+
116+
swift::fatalError(0,
117+
"fatal error: Command line arguments not supported on this platform.\n");
118+
}
119+
#endif
120+

stdlib/public/stubs/GlobalObjects.cpp

-3
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,6 @@ swift::_SwiftEmptyArrayStorage swift::_swiftEmptyArrayStorage = {
4141

4242
__swift_uint64_t swift::_swift_stdlib_HashingDetail_fixedSeedOverride = 0;
4343

44-
/// Backing storage for Swift.Process.arguments.
45-
void *swift::_swift_stdlib_ProcessArguments = nullptr;
46-
4744
namespace llvm { namespace hashing { namespace detail {
4845
// An extern variable expected by LLVM's hashing templates. We don't link any
4946
// LLVM libs into the runtime, so define this here.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include "ProcessStressTest.h"
2+
3+
int main(int argc, char **argv) {
4+
swift_process_test_getProcessArgs();
5+
return 0;
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Declared in ProcessStressTest.swift.
2+
extern void swift_process_test_getProcessArgs(void);

0 commit comments

Comments
 (0)