From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Bertalan <dani@danielbertalan.dev>
Date: Thu, 14 Apr 2022 10:09:50 +0200
Subject: [PATCH] [clang] Add support for Minerva
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Adds support for the `$arch-pc-minerva` target to the Clang front end.
This makes the compiler look for libraries and headers in the right
places, and enables some security mitigations like stack-smashing
protection and position-independent code by default.
Co-authored-by: kleines Filmröllchen <filmroellchen@serenityos.org>
Co-authored-by: Andrew Kaster <akaster@serenityos.org>
Co-authored-by: Daniel Bertalan <dani@danielbertalan.dev>
Co-authored-by: Dan Klishch <danilklishch@gmail.com>
---
clang/lib/Basic/Targets.cpp | 12 +
clang/lib/Basic/Targets/OSTargets.h | 17 ++
clang/lib/Driver/CMakeLists.txt | 1 +
clang/lib/Driver/Driver.cpp | 4 +
clang/lib/Driver/ToolChain.cpp | 5 +-
clang/lib/Driver/ToolChains/Minerva.cpp | 208 ++++++++++++++++++
clang/lib/Driver/ToolChains/Minerva.h | 84 +++++++
clang/lib/Lex/InitHeaderSearch.cpp | 1 +
.../usr/include/c++/v1/.keep | 0
.../include/x86_64-pc-minerva/c++/v1/.keep | 0
.../minerva_x86_64_tree/usr/lib/crt0.o | 0
.../usr/lib/crt0_shared.o | 0
.../minerva_x86_64_tree/usr/local/.keep | 0
clang/test/Driver/pic.c | 6 +
clang/test/Driver/save-stats.c | 2 +
clang/test/Driver/minerva.cpp | 199 +++++++++++++++++
16 files changed, 538 insertions(+), 1 deletion(-)
create mode 100644 clang/lib/Driver/ToolChains/Minerva.cpp
create mode 100644 clang/lib/Driver/ToolChains/Minerva.h
create mode 100644 clang/test/Driver/Inputs/minerva_x86_64_tree/usr/include/c++/v1/.keep
create mode 100644 clang/test/Driver/Inputs/minerva_x86_64_tree/usr/include/x86_64-pc-minerva/c++/v1/.keep
create mode 100644 clang/test/Driver/Inputs/minerva_x86_64_tree/usr/lib/crt0.o
create mode 100644 clang/test/Driver/Inputs/minerva_x86_64_tree/usr/lib/crt0_shared.o
create mode 100644 clang/test/Driver/Inputs/minerva_x86_64_tree/usr/local/.keep
create mode 100644 clang/test/Driver/minerva.cpp
diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp
index 281aebdb1c35d328c39dd4122355f13d57fab943..c9da76cae09be0163d0a8308b3a8e39c4fd512f1 100644
--- a/clang/lib/Basic/Targets.cpp
+++ b/clang/lib/Basic/Targets.cpp
@@ -170,6 +170,9 @@ std::unique_ptr<TargetInfo> AllocateTarget(const llvm::Triple &Triple,
case llvm::Triple::OpenBSD:
return std::make_unique<OpenBSDTargetInfo<AArch64leTargetInfo>>(Triple,
Opts);
+ case llvm::Triple::Minerva:
+ return std::make_unique<MinervaTargetInfo<AArch64leTargetInfo>>(Triple,
+ Opts);
case llvm::Triple::Win32:
switch (Triple.getEnvironment()) {
case llvm::Triple::GNU:
@@ -466,6 +469,9 @@ std::unique_ptr<TargetInfo> AllocateTarget(const llvm::Triple &Triple,
return std::make_unique<OHOSTargetInfo<RISCV64TargetInfo>>(Triple,
Opts);
}
+ case llvm::Triple::Minerva:
+ return std::make_unique<MinervaTargetInfo<RISCV64TargetInfo>>(Triple,
+ Opts);
default:
return std::make_unique<RISCV64TargetInfo>(Triple, Opts);
}
@@ -591,6 +597,9 @@ std::unique_ptr<TargetInfo> AllocateTarget(const llvm::Triple &Triple,
return std::make_unique<MCUX86_32TargetInfo>(Triple, Opts);
case llvm::Triple::Hurd:
return std::make_unique<HurdTargetInfo<X86_32TargetInfo>>(Triple, Opts);
+ case llvm::Triple::Minerva:
+ return std::make_unique<MinervaTargetInfo<X86_32TargetInfo>>(Triple,
+ Opts);
default:
return std::make_unique<X86_32TargetInfo>(Triple, Opts);
}
@@ -654,6 +663,9 @@ std::unique_ptr<TargetInfo> AllocateTarget(const llvm::Triple &Triple,
return std::make_unique<PS5OSTargetInfo<X86_64TargetInfo>>(Triple, Opts);
case llvm::Triple::Hurd:
return std::make_unique<HurdTargetInfo<X86_64TargetInfo>>(Triple, Opts);
+ case llvm::Triple::Minerva:
+ return std::make_unique<MinervaTargetInfo<X86_64TargetInfo>>(Triple,
+ Opts);
default:
return std::make_unique<X86_64TargetInfo>(Triple, Opts);
}
diff --git a/clang/lib/Basic/Targets/OSTargets.h b/clang/lib/Basic/Targets/OSTargets.h
index 991efd2bde01f4660742dd8fd427b611679571c5..0b67528245fd960ad4c3879c0c77e80911c0fdc7 100644
--- a/clang/lib/Basic/Targets/OSTargets.h
+++ b/clang/lib/Basic/Targets/OSTargets.h
@@ -1040,6 +1040,23 @@ public:
}
};
+// Minerva target
+template <typename Target>
+class LLVM_LIBRARY_VISIBILITY MinervaTargetInfo : public OSTargetInfo<Target> {
+protected:
+ void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple,
+ MacroBuilder &Builder) const override {
+ Builder.defineMacro("__minerva__");
+ DefineStd(Builder, "unix", Opts);
+ }
+
+public:
+ MinervaTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts)
+ : OSTargetInfo<Target>(Triple, Opts) {
+ this->WIntType = TargetInfo::UnsignedInt;
+ }
+};
+
} // namespace targets
} // namespace clang
#endif // LLVM_CLANG_LIB_BASIC_TARGETS_OSTARGETS_H
diff --git a/clang/lib/Driver/CMakeLists.txt b/clang/lib/Driver/CMakeLists.txt
index 5bdb6614389cffcce642f90252e605ad6e4fe9e0..0b147a4754dbead6134ad9994bc2c6fbedc78da2 100644
--- a/clang/lib/Driver/CMakeLists.txt
+++ b/clang/lib/Driver/CMakeLists.txt
@@ -75,6 +75,7 @@ add_clang_library(clangDriver
ToolChains/OpenBSD.cpp
ToolChains/PS4CPU.cpp
ToolChains/RISCVToolchain.cpp
+ ToolChains/Minerva.cpp
ToolChains/Solaris.cpp
ToolChains/SPIRV.cpp
ToolChains/SPIRVOpenMP.cpp
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 87855fdb7997105d8d425b4c8690b072d3c6cde7..2549f5940ff4f4973810831691c4c2e00f1e0507 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -45,6 +45,7 @@
#include "ToolChains/SPIRV.h"
#include "ToolChains/SPIRVOpenMP.h"
#include "ToolChains/SYCL.h"
+#include "ToolChains/Minerva.h"
#include "ToolChains/Solaris.h"
#include "ToolChains/TCE.h"
#include "ToolChains/UEFI.h"
@@ -6663,6 +6664,9 @@ const ToolChain &Driver::getToolChain(const ArgList &Args,
case llvm::Triple::Fuchsia:
TC = std::make_unique<toolchains::Fuchsia>(*this, Target, Args);
break;
+ case llvm::Triple::Minerva:
+ TC = std::make_unique<toolchains::Minerva>(*this, Target, Args);
+ break;
case llvm::Triple::Solaris:
TC = std::make_unique<toolchains::Solaris>(*this, Target, Args);
break;
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index acf9d264d631b33f048e90b7f81ea8ad771c84d9..5653b07368c4e31947f1f7e4e8acde66aeeb862a 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -702,6 +702,8 @@ StringRef ToolChain::getOSLibName() const {
return "sunos";
case llvm::Triple::AIX:
return "aix";
+ case llvm::Triple::Minerva:
+ return "minerva";
default:
return getOS();
}
@@ -1225,7 +1227,8 @@ ToolChain::UnwindLibType ToolChain::GetUnwindLibType(
else if (LibName == "platform" || LibName == "") {
ToolChain::RuntimeLibType RtLibType = GetRuntimeLibType(Args);
if (RtLibType == ToolChain::RLT_CompilerRT) {
- if (getTriple().isAndroid() || getTriple().isOSAIX())
+ if (getTriple().isAndroid() || getTriple().isOSAIX() ||
+ getTriple().isOSMinerva())
unwindLibType = ToolChain::UNW_CompilerRT;
else
unwindLibType = ToolChain::UNW_None;
diff --git a/clang/lib/Driver/ToolChains/Minerva.cpp b/clang/lib/Driver/ToolChains/Minerva.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7d95fe55a0fde623720e5974a860826753f4bf1f
--- /dev/null
+++ b/clang/lib/Driver/ToolChains/Minerva.cpp
@@ -0,0 +1,208 @@
+//===---- Minerva.cpp - Minerva ToolChain Implementation ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Minerva.h"
+#include "CommonArgs.h"
+#include "clang/Config/config.h"
+#include "clang/Driver/Compilation.h"
+#include "clang/Driver/Driver.h"
+#include "clang/Driver/Options.h"
+#include "clang/Driver/SanitizerArgs.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/VirtualFileSystem.h"
+#include <string>
+
+using namespace clang::driver;
+using namespace clang::driver::toolchains;
+using namespace clang;
+using namespace llvm::opt;
+
+void tools::minerva::Linker::ConstructJob(Compilation &C, const JobAction &JA,
+ const InputInfo &Output,
+ const InputInfoList &Inputs,
+ const ArgList &Args,
+ const char *LinkingOutput) const {
+ const auto &TC = getToolChain();
+ const auto &D = TC.getDriver();
+ const bool IsShared = Args.hasArg(options::OPT_shared);
+ const bool IsStatic =
+ Args.hasArg(options::OPT_static) && !Args.hasArg(options::OPT_static_pie);
+ const bool IsStaticPIE = Args.hasArg(options::OPT_static_pie);
+ ArgStringList CmdArgs;
+
+ if (!D.SysRoot.empty())
+ CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot));
+
+ if (IsShared)
+ CmdArgs.push_back("-shared");
+
+ if (IsStaticPIE) {
+ CmdArgs.push_back("-static");
+ CmdArgs.push_back("-pie");
+ CmdArgs.push_back("--no-dynamic-linker");
+ CmdArgs.push_back("-z");
+ CmdArgs.push_back("text");
+ } else if (IsStatic) {
+ CmdArgs.push_back("-static");
+ } else if (!Args.hasArg(options::OPT_r)) {
+ if (Args.hasArg(options::OPT_rdynamic))
+ CmdArgs.push_back("-export-dynamic");
+ if (!IsShared) {
+ Arg *A = Args.getLastArg(options::OPT_pie, options::OPT_no_pie,
+ options::OPT_nopie);
+ bool IsPIE = A ? A->getOption().matches(options::OPT_pie) : true;
+ if (IsPIE)
+ CmdArgs.push_back("-pie");
+ CmdArgs.push_back("-dynamic-linker");
+ CmdArgs.push_back("/usr/lib/Loader.so");
+ }
+ }
+
+ CmdArgs.push_back("--eh-frame-hdr");
+
+ assert((Output.isFilename() || Output.isNothing()) && "Invalid output.");
+ if (Output.isFilename()) {
+ CmdArgs.push_back("-o");
+ CmdArgs.push_back(Output.getFilename());
+ }
+
+ CmdArgs.push_back("-z");
+ CmdArgs.push_back("pack-relative-relocs");
+
+ bool HasNoStdLib = Args.hasArg(options::OPT_nostdlib, options::OPT_r);
+ bool HasNoStdLibXX = Args.hasArg(options::OPT_nostdlibxx);
+ bool HasNoLibC = Args.hasArg(options::OPT_nolibc);
+ bool HasNoStartFiles = Args.hasArg(options::OPT_nostartfiles);
+ bool HasNoDefaultLibs = Args.hasArg(options::OPT_nodefaultlibs);
+
+ bool ShouldLinkStartFiles = !HasNoStartFiles && !HasNoStdLib;
+ bool ShouldLinkCompilerRuntime = !HasNoDefaultLibs && !HasNoStdLib;
+ bool ShouldLinkLibC = !HasNoLibC && !HasNoStdLib && !HasNoDefaultLibs;
+ bool ShouldLinkLibCXX =
+ D.CCCIsCXX() && !HasNoStdLibXX && !HasNoStdLib && !HasNoDefaultLibs;
+
+ if (ShouldLinkStartFiles) {
+ if (!IsShared)
+ CmdArgs.push_back(Args.MakeArgString(TC.GetFilePath("crt0.o")));
+
+ std::string crtbegin_path;
+ if (TC.GetRuntimeLibType(Args) == ToolChain::RLT_CompilerRT) {
+ std::string crtbegin =
+ TC.getCompilerRT(Args, "crtbegin", ToolChain::FT_Object);
+ if (TC.getVFS().exists(crtbegin))
+ crtbegin_path = crtbegin;
+ }
+ if (crtbegin_path.empty())
+ crtbegin_path = TC.GetFilePath("crtbeginS.o");
+ CmdArgs.push_back(Args.MakeArgString(crtbegin_path));
+ }
+
+ Args.addAllArgs(CmdArgs, {options::OPT_L, options::OPT_u});
+
+ TC.AddFilePathLibArgs(Args, CmdArgs);
+
+ if (D.isUsingLTO()) {
+ assert(!Inputs.empty() && "Must have at least one input.");
+ // Find the first filename InputInfo object.
+ auto const *Input = llvm::find_if(
+ Inputs, [](const InputInfo &II) -> bool { return II.isFilename(); });
+ if (Input == Inputs.end())
+ // For a very rare case, all of the inputs to the linker are
+ // InputArg. If that happens, just use the first InputInfo.
+ Input = Inputs.begin();
+
+ addLTOOptions(TC, Args, CmdArgs, Output, *Input,
+ D.getLTOMode() == LTOK_Thin);
+ }
+
+ Args.addAllArgs(CmdArgs, {options::OPT_T_Group, options::OPT_s,
+ options::OPT_t, options::OPT_r});
+
+ addLinkerCompressDebugSectionsOption(TC, Args, CmdArgs);
+
+ AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA);
+
+ if (ShouldLinkCompilerRuntime) {
+ AddRunTimeLibs(TC, D, CmdArgs, Args);
+
+ // We supply our own sanitizer runtimes that output errors to the
+ // Kernel debug log as well as stderr.
+ // FIXME: Properly port clang/gcc sanitizers and use those instead.
+ const SanitizerArgs &Sanitize = TC.getSanitizerArgs(Args);
+ if (Sanitize.needsUbsanRt())
+ CmdArgs.push_back("-lubsan");
+ }
+
+ if (ShouldLinkLibCXX) {
+ bool OnlyLibstdcxxStatic = Args.hasArg(options::OPT_static_libstdcxx) &&
+ !Args.hasArg(options::OPT_static);
+ CmdArgs.push_back("--push-state");
+ CmdArgs.push_back("--as-needed");
+ if (OnlyLibstdcxxStatic)
+ CmdArgs.push_back("-Bstatic");
+ TC.AddCXXStdlibLibArgs(Args, CmdArgs);
+ if (OnlyLibstdcxxStatic)
+ CmdArgs.push_back("-Bdynamic");
+ CmdArgs.push_back("--pop-state");
+ }
+
+ CmdArgs.push_back("-L=/usr/local/lib");
+
+ // Silence warnings when linking C code with a C++ '-stdlib' argument.
+ Args.ClaimAllArgs(options::OPT_stdlib_EQ);
+
+ if (ShouldLinkLibC)
+ CmdArgs.push_back("-lc");
+
+ if (ShouldLinkStartFiles) {
+ std::string crtend_path;
+ if (TC.GetRuntimeLibType(Args) == ToolChain::RLT_CompilerRT) {
+ std::string crtend =
+ TC.getCompilerRT(Args, "crtend", ToolChain::FT_Object);
+ if (TC.getVFS().exists(crtend))
+ crtend_path = crtend;
+ }
+ if (crtend_path.empty())
+ crtend_path = TC.GetFilePath("crtendS.o");
+ CmdArgs.push_back(Args.MakeArgString(crtend_path));
+ }
+
+ const char *Exec = Args.MakeArgString(TC.GetLinkerPath());
+ C.addCommand(std::make_unique<Command>(JA, *this,
+ ResponseFileSupport::AtFileCurCP(),
+ Exec, CmdArgs, Inputs, Output));
+}
+
+Minerva::Minerva(const Driver &D, const llvm::Triple &Triple,
+ const ArgList &Args)
+ : Generic_ELF(D, Triple, Args) {
+ getFilePaths().push_back(concat(getDriver().SysRoot, "/usr/lib"));
+}
+
+Tool *Minerva::buildLinker() const {
+ return new tools::minerva::Linker(*this);
+}
+
+void Minerva::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
+ ArgStringList &CC1Args) const {
+ const Driver &D = getDriver();
+
+ if (DriverArgs.hasArg(options::OPT_nostdinc))
+ return;
+
+ if (!DriverArgs.hasArg(options::OPT_nobuiltininc))
+ addSystemInclude(DriverArgs, CC1Args, concat(D.ResourceDir, "/include"));
+
+ if (DriverArgs.hasArg(options::OPT_nostdlibinc))
+ return;
+
+ addSystemInclude(DriverArgs, CC1Args,
+ concat(D.SysRoot, "/usr/local/include"));
+
+ addSystemInclude(DriverArgs, CC1Args, concat(D.SysRoot, "/usr/include"));
+}
diff --git a/clang/lib/Driver/ToolChains/Minerva.h b/clang/lib/Driver/ToolChains/Minerva.h
new file mode 100644
index 0000000000000000000000000000000000000000..2a1f685cb6623e080a2c141dfc3790efdb09419c
--- /dev/null
+++ b/clang/lib/Driver/ToolChains/Minerva.h
@@ -0,0 +1,84 @@
+//===---- Minerva.h - Minerva ToolChain Implementation ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_MINERVA_H
+#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_MINERVA_H
+
+#include "Gnu.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Driver/Tool.h"
+#include "clang/Driver/ToolChain.h"
+
+namespace clang {
+namespace driver {
+namespace tools {
+namespace minerva {
+
+class LLVM_LIBRARY_VISIBILITY Linker final : public Tool {
+public:
+ Linker(const ToolChain &TC) : Tool("minerva::Linker", "linker", TC) {}
+
+ bool hasIntegratedCPP() const override { return false; }
+ bool isLinkJob() const override { return true; }
+
+ void ConstructJob(Compilation &C, const JobAction &JA,
+ const InputInfo &Output, const InputInfoList &Inputs,
+ const llvm::opt::ArgList &TCArgs,
+ const char *LinkingOutput) const override;
+};
+} // end namespace minerva
+} // end namespace tools
+
+namespace toolchains {
+
+class LLVM_LIBRARY_VISIBILITY Minerva final : public Generic_ELF {
+public:
+ Minerva(const Driver &D, const llvm::Triple &Triple,
+ const llvm::opt::ArgList &Args);
+
+ void
+ AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs,
+ llvm::opt::ArgStringList &CC1Args) const override;
+
+ RuntimeLibType GetDefaultRuntimeLibType() const override {
+ return ToolChain::RLT_CompilerRT;
+ }
+
+ CXXStdlibType GetDefaultCXXStdlibType() const override {
+ return ToolChain::CST_Libcxx;
+ }
+
+ const char *getDefaultLinker() const override { return "ld.lld"; }
+
+ bool HasNativeLLVMSupport() const override { return true; }
+
+ bool isPICDefault() const override { return true; }
+ bool isPIEDefault(const llvm::opt::ArgList &) const override { return true; }
+ bool isPICDefaultForced() const override { return false; }
+
+ bool IsMathErrnoDefault() const override { return false; }
+
+ UnwindTableLevel
+ getDefaultUnwindTableLevel(const llvm::opt::ArgList &Args) const override {
+ return UnwindTableLevel::Asynchronous;
+ }
+
+ LangOptions::StackProtectorMode
+ GetDefaultStackProtectorLevel(bool KernelOrKext) const override {
+ return LangOptions::SSPStrong;
+ }
+
+protected:
+ Tool *buildLinker() const override;
+};
+
+} // end namespace toolchains
+} // end namespace driver
+} // end namespace clang
+
+#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_MINERVA_H
diff --git a/clang/lib/Lex/InitHeaderSearch.cpp b/clang/lib/Lex/InitHeaderSearch.cpp
index bb2a21356fa8fe615526c4f50edd98644ac40ce0..6a892266128550d8a3bdab4364b9935ca235ac80 100644
--- a/clang/lib/Lex/InitHeaderSearch.cpp
+++ b/clang/lib/Lex/InitHeaderSearch.cpp
@@ -300,6 +300,7 @@ bool InitHeaderSearch::ShouldAddDefaultIncludePaths(
case llvm::Triple::PS4:
case llvm::Triple::PS5:
case llvm::Triple::RTEMS:
+ case llvm::Triple::Minerva:
case llvm::Triple::Solaris:
case llvm::Triple::UEFI:
case llvm::Triple::WASI:
diff --git a/clang/test/Driver/Inputs/minerva_x86_64_tree/usr/include/c++/v1/.keep b/clang/test/Driver/Inputs/minerva_x86_64_tree/usr/include/c++/v1/.keep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/clang/test/Driver/Inputs/minerva_x86_64_tree/usr/include/x86_64-pc-minerva/c++/v1/.keep b/clang/test/Driver/Inputs/minerva_x86_64_tree/usr/include/x86_64-pc-minerva/c++/v1/.keep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/clang/test/Driver/Inputs/minerva_x86_64_tree/usr/lib/crt0.o b/clang/test/Driver/Inputs/minerva_x86_64_tree/usr/lib/crt0.o
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/clang/test/Driver/Inputs/minerva_x86_64_tree/usr/lib/crt0_shared.o b/clang/test/Driver/Inputs/minerva_x86_64_tree/usr/lib/crt0_shared.o
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/clang/test/Driver/Inputs/minerva_x86_64_tree/usr/local/.keep b/clang/test/Driver/Inputs/minerva_x86_64_tree/usr/local/.keep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/clang/test/Driver/pic.c b/clang/test/Driver/pic.c
index f5d0745422790629bb5dc55812df2f28e4ae9bb1..0438ef621ef5559898ab401124ad1ac76a3c2ee4 100644
--- a/clang/test/Driver/pic.c
+++ b/clang/test/Driver/pic.c
@@ -329,3 +329,9 @@
// RUN: | FileCheck %s --check-prefix=CHECK-PIC2
// RUN: %clang -c %s --target=i586-pc-haiku -### 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-PIC2
+
+// Minerva has PIC and PIE by default
+// RUN: %clang -c %s --target=x86_64-pc-minerva -### 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-PIE2
+// RUN: %clang -c %s --target=aarch64-pc-minerva -### 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-PIE2
diff --git a/clang/test/Driver/save-stats.c b/clang/test/Driver/save-stats.c
index ad7867a9916860b6c425d4c79ba8e6db8bc8b7da..94d93dc23920cf414e1342f62251e7f89f52a60d 100644
--- a/clang/test/Driver/save-stats.c
+++ b/clang/test/Driver/save-stats.c
@@ -32,6 +32,8 @@
// RUN: %clang --target=x86_64-unknown-haiku -save-stats -flto -o obj/dir/save-stats.exe -Wl,-plugin-opt=-dummy %s -### 2>&1 | FileCheck %s -check-prefix=CHECK-LTO
// RUN: %clang --target=avr -save-stats -flto -o obj/dir/save-stats.exe %s -### 2>&1 | FileCheck %s -check-prefix=CHECK-LTO
// RUN: %clang --target=avr -save-stats -flto -o obj/dir/save-stats.exe -Wl,-plugin-opt=-dummy %s -### 2>&1 | FileCheck %s -check-prefix=CHECK-LTO
+// RUN: %clang --target=x86_64-pc-minerva -save-stats -flto -o obj/dir/save-stats.exe %s -### 2>&1 | FileCheck %s -check-prefix=CHECK-LTO
+// RUN: %clang --target=x86_64-pc-minerva -save-stats -flto -o obj/dir/save-stats.exe -Wl,-plugin-opt=-dummy %s -### 2>&1 | FileCheck %s -check-prefix=CHECK-LTO
// CHECK-LTO: "-stats-file=save-stats.stats"
// CHECK-LTO: "-o" "obj/dir{{/|\\\\}}save-stats.exe"
// CHECK-LTO: "-plugin-opt=stats-file=save-stats.stats"
diff --git a/clang/test/Driver/minerva.cpp b/clang/test/Driver/minerva.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c36370f0d22e5081b74a6224c408ac717da9a29e
--- /dev/null
+++ b/clang/test/Driver/minerva.cpp
@@ -0,0 +1,199 @@
+// UNSUPPORTED: system-windows
+
+/// Check default header and linker paths
+// RUN: %clang -### %s --target=x86_64-pc-minerva --sysroot=%S/Inputs/minerva_x86_64_tree \
+// RUN: -ccc-install-dir %S/Inputs/minerva_x86_64/usr/local/bin -resource-dir=%S/Inputs/resource_dir \
+// RUN: 2>&1 | FileCheck %s --check-prefix=PATHS_X86_64
+// PATHS_X86_64: "-resource-dir" "[[RESOURCE:[^"]+]]"
+// PATHS_X86_64: "-internal-isystem"
+// PATHS_X86_64-SAME: {{^}} "[[SYSROOT:[^"]+]]/usr/include/x86_64-pc-minerva/c++/v1"
+// PATHS_X86_64-SAME: {{^}} "-internal-isystem" "[[SYSROOT:[^"]+]]/usr/include/c++/v1"
+// PATHS_X86_64-SAME: {{^}} "-internal-isystem" "[[RESOURCE]]/include"
+// PATHS_X86_64-SAME: {{^}} "-internal-isystem" "[[SYSROOT:[^"]+]]/usr/local/include"
+// PATHS_X86_64-SAME: {{^}} "-internal-isystem" "[[SYSROOT:[^"]+]]/usr/include"
+// PATHS_X86_64: "-L
+// PATHS_X86_64-SAME: {{^}}[[SYSROOT]]/usr/lib"
+
+/// Check default linker args for each supported triple
+// RUN: %clang -### %s --target=x86_64-pc-minerva --sysroot= 2>&1 | FileCheck %s --check-prefix=MINERVA_X86_64
+// MINERVA_X86_64: "-cc1" "-triple" "x86_64-pc-minerva"
+// MINERVA_X86_64: "{{(.*[^-.0-9A-Z_a-z])?}}ld.lld"
+// MINERVA_X86_64: "-pie"
+// MINERVA_X86_64: "-dynamic-linker" "/usr/lib/Loader.so" "--eh-frame-hdr"
+// MINERVA_X86_64: "-o" "a.out"
+// MINERVA_X86_64: "-z" "pack-relative-relocs"
+// MINERVA_X86_64: "crt0.o" "crtbeginS.o"
+// MINERVA_X86_64: "-lc" "crtendS.o"
+
+// RUN: %clang -### %s --target=aarch64-pc-minerva --sysroot= 2>&1 | FileCheck %s --check-prefix=MINERVA_AARCH64
+// MINERVA_AARCH64: "-cc1" "-triple" "aarch64-pc-minerva"
+// MINERVA_AARCH64: "{{(.*[^-.0-9A-Z_a-z])?}}ld.lld"
+// MINERVA_AARCH64: "-pie"
+// MINERVA_AARCH64: "-dynamic-linker" "/usr/lib/Loader.so" "--eh-frame-hdr"
+// MINERVA_AARCH64: "-o" "a.out"
+// MINERVA_AARCH64: "-z" "pack-relative-relocs"
+// MINERVA_AARCH64: "crt0.o" "crtbeginS.o"
+// MINERVA_AARCH64: "-lc" "crtendS.o"
+
+// RUN: %clang -### %s --target=riscv64-pc-minerva --sysroot= 2>&1 | FileCheck %s --check-prefix=MINERVA_RISCV64
+// MINERVA_RISCV64: "-cc1" "-triple" "riscv64-pc-minerva"
+// MINERVA_RISCV64: "{{(.*[^-.0-9A-Z_a-z])?}}ld.lld"
+// MINERVA_RISCV64: "-pie"
+// MINERVA_RISCV64: "-dynamic-linker" "/usr/lib/Loader.so" "--eh-frame-hdr"
+// MINERVA_RISCV64: "-o" "a.out"
+// MINERVA_RISCV64: "-z" "pack-relative-relocs"
+// MINERVA_RISCV64: "crt0.o" "crtbeginS.o"
+// MINERVA_RISCV64: "-lc" "crtendS.o"
+
+/// -static-pie suppresses -dynamic-linker
+// RUN: %clang -### %s --target=x86_64-pc-minerva --sysroot= \
+// RUN: -static-pie 2>&1 | FileCheck %s --check-prefix=STATIC_PIE
+// STATIC_PIE: "-static" "-pie"
+// STATIC_PIE-NOT: "-dynamic-linker"
+// STATIC_PIE: "--no-dynamic-linker" "-z" "text"
+// STATIC_PIE: "--eh-frame-hdr"
+// STATIC_PIE: "-z" "pack-relative-relocs"
+// STATIC_PIE: "crt0.o" "crtbeginS.o"
+// STATIC_PIE: "-lc" "crtendS.o"
+
+/// -shared forces use of shared crt files
+// RUN: %clang -### %s --target=x86_64-pc-minerva --sysroot= \
+// RUN: -shared 2>&1 | FileCheck %s --check-prefix=SHARED
+// SHARED: "-shared"
+// SHARED: "--eh-frame-hdr"
+// SHARED: "-z" "pack-relative-relocs"
+// SHARED: "crtbeginS.o"
+// SHARED: "-lc" "crtendS.o"
+
+/// -static forces use of static crt files
+// RUN: %clang -### %s --target=x86_64-pc-minerva --sysroot= \
+// RUN: -static 2>&1 | FileCheck %s --check-prefix=STATIC
+// STATIC: "-static"
+// STATIC: "--eh-frame-hdr"
+// STATIC: "-z" "pack-relative-relocs"
+// STATIC: "crt0.o" "crtbeginS.o"
+// STATIC: "-lc" "crtendS.o"
+
+/// -rdynamic passes -export-dynamic
+// RUN: %clang -### %s --target=x86_64-pc-minerva --sysroot= \
+// RUN: -rdynamic 2>&1 | FileCheck %s --check-prefix=RDYNAMIC
+// RDYNAMIC: "-export-dynamic" "-pie"
+// RDYNAMIC: "-dynamic-linker" "/usr/lib/Loader.so" "--eh-frame-hdr"
+// RDYNAMIC: "-o" "a.out"
+// RDYNAMIC: "-z" "pack-relative-relocs"
+// RDYNAMIC: "crt0.o" "crtbeginS.o"
+// RDYNAMIC: "-lc" "crtendS.o"
+
+// RUN: %clang -### %s --target=x86_64-pc-minerva --sysroot= \
+// RUN: -no-pie -rdynamic 2>&1 | FileCheck %s --check-prefix=RDYNAMIC_NOPIE
+// RDYNAMIC_NOPIE: "-export-dynamic"
+// RDYNAMIC_NOPIE-NOT: "-pie"
+// RDYNAMIC_NOPIE: "-dynamic-linker" "/usr/lib/Loader.so" "--eh-frame-hdr"
+// RDYNAMIC_NOPIE: "-o" "a.out"
+// RDYNAMIC_NOPIE: "-z" "pack-relative-relocs"
+// RDYNAMIC_NOPIE: "crt0.o" "crtbeginS.o"
+// RDYNAMIC_NOPIE: "-lc" "crtendS.o"
+
+/// -nostdlib suppresses default -l and crt*.o
+// RUN: %clang -### %s --target=x86_64-pc-minerva --sysroot=%S/Inputs/minerva_x86_64_tree \
+// RUN: -ccc-install-dir %S/Inputs/minerva_x86_64_tree/usr/local/bin -resource-dir=%S/Inputs/resource_dir \
+// RUN: -nostdlib --rtlib=compiler-rt 2>&1 | FileCheck %s --check-prefix=NOSTDLIB
+// NOSTDLIB: "-internal-isystem"
+// NOSTDLIB-SAME: {{^}} "[[SYSROOT:[^"]+]]/usr/include/x86_64-pc-minerva/c++/v1"
+// NOSTDLIB-NOT: crt{{[^./]+}}.o
+// NOSTDLIB: "-L
+// NOSTDLIB-SAME: {{^}}[[SYSROOT]]/usr/lib"
+// NOSTDLIB-NOT: "-l
+// NOSTDLIB-NOT: libclang_rt.builtins-x86_64.a
+// NOSTDLIB-NOT: crt{{[^./]+}}.o
+
+// -nostartfiles suppresses crt*.o, but not default -l
+// RUN: %clang -### %s --target=x86_64-pc-minerva --sysroot=%S/Inputs/minerva_x86_64_tree \
+// RUN: -ccc-install-dir %S/Inputs/minerva_x86_64_tree/usr/local/bin -resource-dir=%S/Inputs/resource_dir \
+// RUN: -nostartfiles --rtlib=compiler-rt 2>&1 | FileCheck %s --check-prefix=NOSTARTFILES
+// NOSTARTFILES: "-internal-isystem"
+// NOSTARTFILES-SAME: {{^}} "[[SYSROOT:[^"]+]]/usr/include/x86_64-pc-minerva/c++/v1"
+// NOSTARTFILES-NOT: crt{{[^./]+}}.o
+// NOSTARTFILES: "-L
+// NOSTARTFILES-SAME: {{^}}[[SYSROOT]]/usr/lib"
+// NOSTARTFILES: "[[RESOURCE:[^"]+]]/lib/x86_64-pc-minerva/libclang_rt.builtins.a"
+// NOSTARTFILES: "-lc"
+// NOSTARTFILES-NOT: crt{{[^./]+}}.o
+
+/// -r suppresses -dynamic-linker, default -l, and crt*.o like -nostdlib.
+// RUN: %clang -### %s --target=x86_64-pc-minerva --sysroot=%S/Inputs/minerva_x86_64_tree \
+// RUN: -ccc-install-dir %S/Inputs/minerva_x86_64_tree/usr/local/bin -resource-dir=%S/Inputs/resource_dir \
+// RUN: -r --rtlib=compiler-rt 2>&1 | FileCheck %s --check-prefix=RELOCATABLE
+// RELOCATABLE-NOT: "-dynamic-linker"
+// RELOCATABLE: "-internal-isystem"
+// RELOCATABLE-SAME: {{^}} "[[SYSROOT:[^"]+]]/usr/include/x86_64-pc-minerva/c++/v1"
+// RELOCATABLE-NOT: crt{{[^./]+}}.o
+// RELOCATABLE: "-L
+// RELOCATABLE-SAME: {{^}}[[SYSROOT]]/usr/lib"
+// RELOCATABLE-NOT: "-l
+// RELOCATABLE-NOT: crt{{[^./]+}}.o
+// RELOCATABLE-NOT: libclang_rt.builtins-x86_64.a
+
+/// -nolibc suppresses -lc but not other default -l
+// RUN: %clang -### %s --target=x86_64-pc-minerva --sysroot=%S/Inputs/minerva_x86_64_tree \
+// RUN: -ccc-install-dir %S/Inputs/minerva_x86_64_tree/usr/local/bin -resource-dir=%S/Inputs/resource_dir \
+// RUN: -nolibc --rtlib=compiler-rt 2>&1 | FileCheck %s --check-prefix=NOLIBC
+// NOLIBC: "-internal-isystem"
+// NOLIBC-SAME: {{^}} "[[SYSROOT:[^"]+]]/usr/include/x86_64-pc-minerva/c++/v1"
+// NOLIBC: "[[SYSROOT:[^"]+]]/usr/lib/crt0.o" "crtbeginS.o"
+// NOLIBC: "-L
+// NOLIBC-SAME: {{^}}[[SYSROOT]]/usr/lib"
+// NOLIBC-NOT: "-lc"
+// NOLIBC: "[[RESOURCE:[^"]+]]/lib/x86_64-pc-minerva/libclang_rt.builtins.a"
+// NOLIBC: "crtendS.o"
+
+/// -fsanitize=undefined redirects to Minerva-custom UBSAN runtime
+// RUN: %clang -### %s --target=x86_64-pc-minerva --sysroot=%S/Inputs/minerva_x86_64_tree \
+// RUN: -ccc-install-dir %S/Inputs/minerva_x86_64_tree/usr/local/bin -resource-dir=%S/Inputs/resource_dir \
+// RUN: -fsanitize=undefined --rtlib=compiler-rt 2>&1 | FileCheck %s --check-prefix=UBSAN
+// UBSAN-NOT: "libclang_rt.ubsan{{[^./]+}}.a"
+// UBSAN-NOT: "libclang_rt.ubsan{{[^./]+}}.so"
+// UBSAN: "-lubsan"
+
+/// C++ stdlib behavior
+// RUN: %clangxx -### %s --target=x86_64-pc-minerva --sysroot="" \
+// RUN: 2>&1 | FileCheck %s --check-prefix=DEFAULT_LIBCXX
+// DEFAULT_LIBCXX: "-dynamic-linker" "/usr/lib/Loader.so" "--eh-frame-hdr"
+// DEFAULT_LIBCXX: "-z" "pack-relative-relocs"
+// DEFAULT_LIBCXX: "crt0.o" "crtbeginS.o"
+// DEFAULT_LIBCXX: "--push-state"
+// DEFAULT_LIBCXX: "--as-needed"
+// DEFAULT_LIBCXX: "-lc++"
+// DEFAULT_LIBCXX: "--pop-state"
+// DEFAULT_LIBCXX: "-lc" "crtendS.o"
+
+// RUN: %clangxx -### %s --target=x86_64-pc-minerva --sysroot="" \
+// RUN: -static 2>&1 | FileCheck %s --check-prefix=STATIC_LIBCXX
+// STATIC_LIBCXX: "-z" "pack-relative-relocs"
+// STATIC_LIBCXX: "crt0.o" "crtbeginS.o"
+// STATIC_LIBCXX: "--push-state"
+// STATIC_LIBCXX: "--as-needed"
+// STATIC_LIBCXX: "-lc++"
+// STATIC_LIBCXX: "--pop-state"
+// STATIC_LIBCXX: "-lc" "crtendS.o"
+
+// RUN: %clangxx -### %s --target=x86_64-pc-minerva --sysroot="" \
+// RUN: -static-libstdc++ 2>&1 | FileCheck %s --check-prefix=STATIC_LIBSTDCXX
+// STATIC_LIBSTDCXX: "-z" "pack-relative-relocs"
+// STATIC_LIBSTDCXX: "crt0.o" "crtbeginS.o"
+// STATIC_LIBSTDCXX: "--push-state"
+// STATIC_LIBSTDCXX: "--as-needed"
+// STATIC_LIBSTDCXX: "-Bstatic"
+// STATIC_LIBSTDCXX: "-lc++"
+// STATIC_LIBSTDCXX: "-Bdynamic"
+// STATIC_LIBSTDCXX: "--pop-state"
+// STATIC_LIBSTDCXX: "-lc" "crtendS.o"
+
+// RUN: %clangxx -### %s --target=x86_64-pc-minerva --sysroot="" \
+// RUN: -nostdlib++ 2>&1 | FileCheck %s --check-prefix=NO_LIBCXX
+// NO_LIBCXX: "-z" "pack-relative-relocs"
+// NO_LIBCXX: "crt0.o" "crtbeginS.o"
+// NO_LIBCXX: "--as-needed"
+// NO_LIBCXX: "-lunwind"
+// NO_LIBCXX-NOT: "-lc++"
+// NO_LIBCXX: "-lc" "crtendS.o"