//===--- CommandLine.cpp - OS-specific command line arguments -------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://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 #include "swift/Runtime/Debug.h" #include "swift/Runtime/Win32.h" #include "swift/shims/GlobalObjects.h" #include "swift/shims/RuntimeStubs.h" #include "swift/shims/Visibility.h" #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #include #include #endif // Backing storage for overrides of `Swift.CommandLine.arguments`. static char **_swift_stdlib_ProcessOverrideUnsafeArgv = nullptr; static int _swift_stdlib_ProcessOverrideUnsafeArgc = 0; // This needs to findable by dlopen() for JIT purposes (see Immediate.cpp). SWIFT_CC(swift) extern "C" SWIFT_ATTRIBUTE_FOR_EXPORTS void _swift_stdlib_overrideUnsafeArgvArgc(char **argv, int argc) { _swift_stdlib_ProcessOverrideUnsafeArgv = argv; _swift_stdlib_ProcessOverrideUnsafeArgc = argc; } namespace swift { /// A platform-specific implementation of @c _swift_stdlib_getUnsafeArgvArgc. /// /// This function should return @c argc and @c argv cheaply (ideally in /// constant time and without needing to allocate.) If it cannot do so, /// it should return @c nullptr, at which point the caller can call /// @c enumerateUnsafeArgv() in order to reconstruct @c argv locally. /// /// The result of this function is @em not owned by the caller and should /// persist for the lifetime of the process. static char **getUnsafeArgvArgc(int *outArgLen); /// A platform-specific function that enumerates the contents of @c argv /// one argument at a time. /// /// @a body is a function that takes two arguments: /// /// - The first argument is the value of @c argc if it can be readily /// computed, or @c -1 otherwise. /// - The second argument is the element of @c argv being enumerated. The /// caller makes a copy of this string. The implementation should not /// enumerate the trailing @c nullptr required by the C standard. /// /// Callers should call @c getUnsafeArgvArgc() before calling this function /// in case a fast path is available. If that function is implemented on this /// platform, then this function does not need to be implemented. template static void enumerateUnsafeArgv(const F& body); } SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL char **_swift_stdlib_getUnsafeArgvArgc(int *outArgLen) { assert(outArgLen != nullptr); // Check the override before doing any platform-specific work. if (SWIFT_UNLIKELY(_swift_stdlib_ProcessOverrideUnsafeArgv)) { *outArgLen = _swift_stdlib_ProcessOverrideUnsafeArgc; return _swift_stdlib_ProcessOverrideUnsafeArgv; } // Try the platform-specific fast path that avoids heap (re)allocation. Not // all platforms implement this function. if (auto argv = swift::getUnsafeArgvArgc(outArgLen)) { return argv; } // Take a slower path wherein we construct the argv array one element at a // time. If the enumeration function can provide us with a hint for argc, we // can avoid calling realloc() more than once. int maxArgc = 0; int argc = 0; char **argv = nullptr; swift::enumerateUnsafeArgv([&] (int maxArgcHint, const char *arg) { if (argc >= maxArgc) { if (maxArgcHint > maxArgc) { // The platform was able to cheaply get argc, so use the // provided value. maxArgc = maxArgcHint; } else { // The platform could not tell us argc, so grow the argument array by // small amounts. We assume here that the command line doesn't usually // have many arguments, so a small/linear growth factor should be safe. // // If we ask for space for 15 arguments on the first allocation, then // when we call realloc (asking for additional space for the trailing // nullptr), we'll avoid wasting memory due to malloc()'s granularity. maxArgc = (maxArgc == 0) ? 15 : (maxArgc + 16); } // Reallocate (or initially allocate) the argv buffer. Overallocate by // one element to allow for a trailing nullptr. // // NOTE: It is intentional that we do not simply use std::vector here. // STL collections may call operator new() which can be overridden by // client code and that client code could call back into Swift. size_t argvSize = sizeof(char *) * (maxArgc + 1); argv = reinterpret_cast(realloc(argv, argvSize)); if (!argv) { swift::fatalError(0, "Fatal error: Could not allocate space for %d commandline " " arguments: %d\n", argc, errno); } } argv[argc] = strdup(arg); argc += 1; }); if (argv) { // Ensure the arguments array has a trailing nullptr, per the C standard. argv[argc] = nullptr; } else { // We didn't get any arguments and never ended up allocating an array. // Return an empty array (save for the trailing nullptr) instead. static char *emptyArgv[] = { nullptr }; argc = 0; argv = emptyArgv; } *outArgLen = argc; return argv; } #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); static char **swift::getUnsafeArgvArgc(int *outArgLen) { *outArgLen = *_NSGetArgc(); return *_NSGetArgv(); } template static void swift::enumerateUnsafeArgv(const F& body) { } #elif defined(__linux__) // On Linux, there is no easy way to get the argument vector pointer outside // of the main() function. However, the ABI specifications dictate the layout // of the process's initial stack, which looks something like: // // stack top ----> ┌────────────────────────┐ // │ Unspecified │ // ┊ ┊ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤ // │ Information block │ // │ (argument strings, │ // │ environment strings, │ // │ auxiliary information) │ // ┊ ┊ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤ // │ Unspecified │ // ┊ ┊ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤ // │ NULL │ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤ // │ Auxiliary Vector │ // ┊ ┊ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤ // │ NULL │ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤ // │ Environment Pointers │ // ┊ ┊ // environ ------> ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤ // │ NULL │ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤ // │ Argument Pointers │ // ┊ ┊ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤ // │ Argument Count │ // ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤ // ┊ ┊ // // See https://gitlab.com/x86-psABIs/x86-64-ABI, // https://gitlab.com/x86-psABIs/i386-ABI // // The upshot is that if we can get hold of `environ` before anything has // had a chance to change it, we can find the `argv` array and also the // argument count, `argc`, by walking back up the stack. // // (Note that Linux uses this same layout for all platforms, not just x86-based // ones. It also has a fixed layout for the data at the top of the stack, but // we don't need to take advantage of that here and can stick to things that // are defined in the ABI specs.) #include #define DEBUG_ARGVGRABBER 0 #if DEBUG_ARGVGRABBER #define ARGVDEBUG(...) fprintf(stderr, __VA_ARGS__) #else #define ARGVDEBUG(...) #endif namespace { struct ArgvGrabber { char **argv; int argc; ArgvGrabber(); private: struct stack { void *base; void *top; stack() : base(nullptr), top(nullptr) {} stack(void *b, void *t) : base(b), top(t) {} }; stack findStack(); void findArgv(stack s); }; // Find the stack by looking at /proc/self/maps ArgvGrabber::stack ArgvGrabber::findStack(void) { FILE *maps = fopen("/proc/self/maps", "r"); if (!maps) { ARGVDEBUG("unable to open maps - %d\n", errno); return stack(); } char line[256]; void *base = NULL, *top = NULL; bool found = false; while (fgets(line, sizeof(line), maps)) { // line is on the stack, so we know we're looking at the right // region if line is between base and top. // // Note that we can't look for [stack], because Rosetta and qemu // set up a separate stack for the emulated code. // // We also need to glom on extra VM ranges after the first one // we find, because *sometimes* we end up with an extra range. void *lo, *hi; if (sscanf(line, "%p-%p", &lo, &hi) == 2) { if ((void *)line >= lo && (void *)line < hi) { base = lo; top = hi; found = true; } else if (found && top == lo) { top = hi; } } } fclose(maps); if (!found) { ARGVDEBUG("stack not found in maps\n"); return stack(); } return stack(base, top); } #if DEBUG_ARGVGRABBER void printMaps() { FILE *maps = fopen("/proc/self/maps", "r"); if (!maps) { fprintf(stderr, "unable to open maps - %d\n", errno); return; } char line[256]; while (fgets(line, sizeof(line), maps)) { fputs(line, stderr); } fclose(maps); } #endif // Find argv by walking backwards from environ void ArgvGrabber::findArgv(ArgvGrabber::stack stack) { if (!stack.base) { ARGVDEBUG("no stack\n"); return; } // Check that environ points to the stack char **envp = environ; if ((void *)envp < stack.base || (void *)envp >= stack.top) { ARGVDEBUG("envp = %p, stack is from %p to %p\n", envp, stack.base, stack.top); #if DEBUG_ARGVGRABBER printMaps(); #endif return; } char **ptr = envp - 1; // We're now pointing at the NULL that terminates argv. Keep going back // while we're seeing pointers (values greater than envp). while ((void *)(ptr - 1) > stack.base) { --ptr; // The first thing less than envp must be the argc value if ((void *)*ptr < (void *)envp) { argc = (int)(intptr_t)*ptr++; argv = ptr; return; } } ARGVDEBUG("didn't find argc\n"); } ArgvGrabber::ArgvGrabber() : argv(nullptr), argc(0) { ARGVDEBUG("***GRABBING ARGV for %d***\n", getpid()); findArgv(findStack()); #if DEBUG_ARGVGRABBER fprintf(stderr, "ARGV is at %p with count %d\n", argv, argc); for (int i = 0; i < argc; ++i) { fprintf(stderr, " argv[%d] = \"%s\"\n", i, argv[i]); } fprintf(stderr, "***ARGV GRABBED***\n"); #endif } ArgvGrabber argvGrabber; } // namespace static char **swift::getUnsafeArgvArgc(int *outArgLen) { *outArgLen = argvGrabber.argc; return argvGrabber.argv; } template static void swift::enumerateUnsafeArgv(const F& body) { } #elif defined(__CYGWIN__) static char **swift::getUnsafeArgvArgc(int *outArgLen) { return nullptr; } template static void swift::enumerateUnsafeArgv(const F& body) { FILE *cmdline = fopen("/proc/self/cmdline", "rb"); if (!cmdline) { swift::fatalError(0, "Fatal error: Unable to open interface to '/proc/self/cmdline': %d.\n", errno); } char *arg = nullptr; size_t size = 0; while (getdelim(&arg, &size, '\0', cmdline) != -1) { body(-1, arg); } if (arg) { free(arg); } fclose(cmdline); } #elif defined(_WIN32) #include static char **swift::getUnsafeArgvArgc(int *outArgLen) { return nullptr; } template static void swift::enumerateUnsafeArgv(const F& body) { int argc = 0; if (LPWSTR *wargv = CommandLineToArgvW(GetCommandLineW(), &argc)) { std::for_each(wargv, wargv + argc, [=] (wchar_t *warg) { if (char *arg = _swift_win32_copyUTF8FromWide(warg)) { body(argc, arg); free(arg); } else { // Note that GetLastError() and errno may not be so useful here, // as in the error case we may have called free(), which might reset // either or both of them. swift::fatalError(0, "Fatal error: Unable to convert argument '%ls' to " "UTF-8: %lx, %d.\n", warg, ::GetLastError(), errno); } }); LocalFree(wargv); } } #elif defined(__FreeBSD__) #include #include #include #include #include static char **swift::getUnsafeArgvArgc(int *outArgLen) { return nullptr; } template static void swift::enumerateUnsafeArgv(const F& body) { char *argPtr = nullptr; // or use ARG_MAX? 8192 is used in LLDB though.. int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ARGS, getpid()}; size_t argPtrSize = 0; for (int i = 0; i < 3 && !argPtr; ++i) { // give up after 3 tries if (sysctl(mib, 4, nullptr, &argPtrSize, nullptr, 0) != -1) { argPtr = static_cast(malloc(argPtrSize)); if (sysctl(mib, 4, argPtr, &argPtrSize, nullptr, 0) == -1) { free(argPtr); argPtr = nullptr; if (errno != ENOMEM) break; } } else { break; } } if (!argPtr) swift::fatalError(0, "Fatal error: Could not retrieve commandline " "arguments: sysctl: %s.\n", strerror(errno)); char *curPtr = argPtr; char *endPtr = argPtr + argPtrSize; for (; curPtr < endPtr; curPtr += strlen(curPtr) + 1) { body(-1, curPtr); } free(argPtr); } #elif defined(__wasi__) #include #include #include static char **swift::getUnsafeArgvArgc(int *outArgLen) { __wasi_errno_t err; size_t argv_buf_size = 0; size_t argc = 0; err = __wasi_args_sizes_get(&argc, &argv_buf_size); if (err != __WASI_ERRNO_SUCCESS) { swift::fatalError(0, "Fatal error: Could not retrieve commandline " "arguments: %d.\n", static_cast(err)); return nullptr; } char *argv_buf = static_cast(malloc(argv_buf_size)); char **argv = static_cast(calloc(argc + 1, sizeof(char *))); err = __wasi_args_get((uint8_t **)argv, (uint8_t *)argv_buf); if (err != __WASI_ERRNO_SUCCESS) { swift::fatalError(0, "Fatal error: Could not retrieve commandline " "arguments: %d.\n", static_cast(err)); return nullptr; } *outArgLen = static_cast(argc); return argv; } template static void swift::enumerateUnsafeArgv(const F& body) { } #elif defined(__OpenBSD__) #include #include #include static char **swift::getUnsafeArgvArgc(int *outArgLen) { int mib[2] = {CTL_VM, VM_PSSTRINGS}; struct _ps_strings _ps; size_t len = sizeof(_ps); if (sysctl(mib, 2, &_ps, &len, NULL, 0) == -1) { return nullptr; } struct ps_strings *ps = static_cast(_ps.val); *outArgLen = ps->ps_nargvstr; return ps->ps_argvstr; } template static void swift::enumerateUnsafeArgv(const F& body) { } #else // Add your favorite OS's command line arg grabber here. static char **swift::getUnsafeArgvArgc(int *outArgLen) { swift::fatalError( 0, "Fatal error: Command line arguments not supported on this platform.\n"); } template static void swift::enumerateUnsafeArgv(const F& body) { } #endif