hamzasood updated this revision to Diff 92019.
hamzasood added a comment.
- Added an assertion in Command::Execute to ensure the environment vector is
null-terminated before using it.
- Split Command::setEnvironment so that there's an overload to specifically
handle the case where the input vector is to be copied in. While this adds a
small amount of extra code complexity, it allows us to reduce the worst case
from two copies (copy into the function, re-allocate/copy when appending the
null-terminator) to just one. Considering the non-trivial size of environment
blocks (and that we're already quite inefficient by going from UTF16 -> UTF8 ->
UTF16), I think it's worth the few extra lines of code. However it's simple to
revert if anyone disagrees.
https://reviews.llvm.org/D30991
Files:
include/clang/Driver/Job.h
lib/Driver/Job.cpp
lib/Driver/ToolChains/MSVC.cpp
Index: lib/Driver/ToolChains/MSVC.cpp
===================================================================
--- lib/Driver/ToolChains/MSVC.cpp
+++ lib/Driver/ToolChains/MSVC.cpp
@@ -34,7 +34,7 @@
// Include the necessary headers to interface with the Windows registry and
// environment.
#if defined(LLVM_ON_WIN32)
-#define USE_WIN32
+ #define USE_WIN32
#endif
#ifdef USE_WIN32
@@ -47,20 +47,20 @@
#endif
#ifdef _MSC_VER
-// Don't support SetupApi on MinGW.
-#define USE_MSVC_SETUP_API
+ // Don't support SetupApi on MinGW.
+ #define USE_MSVC_SETUP_API
-// Make sure this comes before MSVCSetupApi.h
-#include <comdef.h>
+ // Make sure this comes before MSVCSetupApi.h
+ #include <comdef.h>
-#include "MSVCSetupApi.h"
-#include "llvm/Support/COM.h"
-_COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration));
-_COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2));
-_COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper));
-_COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances));
-_COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance));
-_COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2));
+ #include "MSVCSetupApi.h"
+ #include "llvm/Support/COM.h"
+ _COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration));
+ _COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2));
+ _COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper));
+ _COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances));
+ _COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance));
+ _COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2));
#endif
using namespace clang::driver;
@@ -446,6 +446,8 @@
TC.addProfileRTLibs(Args, CmdArgs);
+ std::vector<const char *> Environment;
+
// We need to special case some linker paths. In the case of lld, we need to
// translate 'lld' into 'lld-link', and in the case of the regular msvc
// linker, we need to use a special search algorithm.
@@ -459,6 +461,68 @@
// from the program PATH, because other environments like GnuWin32 install
// their own link.exe which may come first.
linkPath = FindVisualStudioExecutable(TC, "link.exe");
+
+#ifdef USE_WIN32
+ // When cross-compiling with VS2017 or newer, link.exe expects to have
+ // its containing bin directory at the top of PATH, followed by the
+ // native target bin directory.
+ // e.g. when compiling for x86 on an x64 host, PATH should start with:
+ // /bin/HostX64/x86;/bin/HostX64/x64
+ if (TC.getIsVS2017OrNewer() &&
+ llvm::Triple(llvm::sys::getProcessTriple()).getArch() != TC.getArch()) {
+ auto HostArch = llvm::Triple(llvm::sys::getProcessTriple()).getArch();
+
+ auto EnvBlockWide = std::unique_ptr<wchar_t[],
+ decltype(&FreeEnvironmentStringsW)>(
+ GetEnvironmentStringsW(), FreeEnvironmentStringsW);
+ if (!EnvBlockWide)
+ goto SkipSettingEnvironment;
+
+ size_t EnvCount = 0;
+ size_t EnvBlockLen = 0;
+ while (EnvBlockWide[EnvBlockLen] != L'\0') {
+ ++EnvCount;
+ EnvBlockLen += std::wcslen(&EnvBlockWide[EnvBlockLen])
+ + 1/*string null-terminator*/;
+ }
+ ++EnvBlockLen; // add the block null-terminator
+
+ std::string EnvBlock;
+ if (!llvm::convertUTF16ToUTF8String(
+ llvm::ArrayRef<char>(reinterpret_cast<char *>(EnvBlockWide.get()),
+ EnvBlockLen * sizeof(EnvBlockWide[0])),
+ EnvBlock))
+ goto SkipSettingEnvironment;
+
+ Environment.reserve(EnvCount + 1);
+
+ // Now loop over each string in the block and copy them into the
+ // environment vector, adjusting the PATH variable as needed when we
+ // find it.
+ for (const char *Cursor = EnvBlock.data(); *Cursor != '\0';) {
+ llvm::StringRef EnvVar(Cursor);
+ if (EnvVar.startswith_lower("path=")) {
+ using SubDirectoryType = toolchains::MSVCToolChain::SubDirectoryType;
+ constexpr size_t PrefixLen = 5; // strlen("path=")
+ Environment.push_back(Args.MakeArgString(
+ EnvVar.substr(0, PrefixLen)
+ + TC.getSubDirectoryPath(SubDirectoryType::Bin)
+ + llvm::Twine(llvm::sys::EnvPathSeparator)
+ + TC.getSubDirectoryPath(SubDirectoryType::Bin, HostArch)
+ + (EnvVar.size() > PrefixLen
+ ? llvm::Twine(llvm::sys::EnvPathSeparator)
+ + EnvVar.substr(PrefixLen)
+ : "")
+ ));
+ } else {
+ Environment.push_back(Args.MakeArgString(EnvVar));
+ }
+ Cursor += EnvVar.size() + 1/*null-terminator*/;
+ }
+ }
+ SkipSettingEnvironment:
+ ;
+#endif
} else {
linkPath = Linker;
llvm::sys::path::replace_extension(linkPath, "exe");
@@ -465,8 +529,11 @@
linkPath = TC.GetProgramPath(linkPath.c_str());
}
- const char *Exec = Args.MakeArgString(linkPath);
- C.addCommand(llvm::make_unique<Command>(JA, *this, Exec, CmdArgs, Inputs));
+ auto LinkCmd = llvm::make_unique<Command>(
+ JA, *this, Args.MakeArgString(linkPath), CmdArgs, Inputs);
+ if (!Environment.empty())
+ LinkCmd->setEnvironment(std::move(Environment));
+ C.addCommand(std::move(LinkCmd));
}
void visualstudio::Compiler::ConstructJob(Compilation &C, const JobAction &JA,
Index: lib/Driver/Job.cpp
===================================================================
--- lib/Driver/Job.cpp
+++ lib/Driver/Job.cpp
@@ -301,16 +301,39 @@
ResponseFileFlag += FileName;
}
+void Command::setEnvironment(std::vector<const char *> &&NewEnvironment) {
+ Environment = std::move(NewEnvironment);
+ Environment.push_back(nullptr);
+}
+
+void Command::setEnvironment(const std::vector<const char *> &NewEnvironment) {
+ // Reserve space for the null-terminator before copying the input vector.
+ // This removes the possibility of needing a second copy when appending
+ // the null-terminator.
+ Environment.reserve(NewEnvironment.size() + 1);
+ Environment.assign(NewEnvironment.begin(), NewEnvironment.end());
+ Environment.push_back(nullptr);
+}
+
int Command::Execute(const StringRef **Redirects, std::string *ErrMsg,
bool *ExecutionFailed) const {
SmallVector<const char*, 128> Argv;
+ const char **Envp;
+ if (Environment.empty()) {
+ Envp = nullptr;
+ } else {
+ assert(Environment.back() == nullptr
+ && "Environment vector should be null-terminated by now");
+ Envp = const_cast<const char **>(Environment.data());
+ }
+
if (ResponseFile == nullptr) {
Argv.push_back(Executable);
Argv.append(Arguments.begin(), Arguments.end());
Argv.push_back(nullptr);
- return llvm::sys::ExecuteAndWait(Executable, Argv.data(), /*env*/ nullptr,
+ return llvm::sys::ExecuteAndWait(Executable, Argv.data(), Envp,
Redirects, /*secondsToWait*/ 0,
/*memoryLimit*/ 0, ErrMsg,
ExecutionFailed);
@@ -337,7 +360,7 @@
return -1;
}
- return llvm::sys::ExecuteAndWait(Executable, Argv.data(), /*env*/ nullptr,
+ return llvm::sys::ExecuteAndWait(Executable, Argv.data(), Envp,
Redirects, /*secondsToWait*/ 0,
/*memoryLimit*/ 0, ErrMsg, ExecutionFailed);
}
Index: include/clang/Driver/Job.h
===================================================================
--- include/clang/Driver/Job.h
+++ include/clang/Driver/Job.h
@@ -69,6 +69,9 @@
/// file
std::string ResponseFileFlag;
+ /// See Command::setEnvironment
+ std::vector<const char *> Environment;
+
/// When a response file is needed, we try to put most arguments in an
/// exclusive file, while others remains as regular command line arguments.
/// This functions fills a vector with the regular command line arguments,
@@ -111,6 +114,20 @@
InputFileList = std::move(List);
}
+ /// \brief Sets the environment to be used by the new process.
+ /// \param NewEnvironment A vector of environment variables to be
+ /// moved into place. The vector will have a null-terminator
+ /// appended to it; a potential copy can be avoided by ensuring
+ /// the vector has space for it before calling this function.
+ /// \remark If the environment remains unset, then the environment
+ /// from this process will be used.
+ /// \sa Command::setEnvironment(const std::vector<const char *> &)
+ void setEnvironment(std::vector<const char *> &&NewEnvironment);
+
+ /// An overload of the above function. It optimises the case where
+ /// the given vector is to be copied in as opposed to moved.
+ void setEnvironment(const std::vector<const char *> &NewEnvironment);
+
const char *getExecutable() const { return Executable; }
const llvm::opt::ArgStringList &getArguments() const { return Arguments; }
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits