Skip to content

Commit 65226de

Browse files
authored
Merge pull request #3108 from CodaFi/argumental
[Stdlib] Fix entry-point-based process args
2 parents 2682ab2 + dc88e51 commit 65226de

File tree

13 files changed

+258
-57
lines changed

13 files changed

+258
-57
lines changed

lib/Immediate/Immediate.cpp

Lines changed: 31 additions & 9 deletions
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

Lines changed: 3 additions & 1 deletion
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

Lines changed: 0 additions & 3 deletions
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

Lines changed: 8 additions & 0 deletions
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

Lines changed: 21 additions & 40 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 120 additions & 0 deletions
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

Lines changed: 0 additions & 3 deletions
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.
Lines changed: 6 additions & 0 deletions
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+
}
Lines changed: 2 additions & 0 deletions
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)