From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Daniel Bertalan Date: Thu, 14 Apr 2022 10:09:50 +0200 Subject: [PATCH] [Driver] Add support for SerenityOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds support for the `$arch-pc-serenity` 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 --- clang/lib/Basic/Targets.cpp | 8 + clang/lib/Basic/Targets/OSTargets.h | 18 ++ clang/lib/Driver/CMakeLists.txt | 1 + clang/lib/Driver/Driver.cpp | 4 + clang/lib/Driver/ToolChain.cpp | 2 + clang/lib/Driver/ToolChains/Serenity.cpp | 336 +++++++++++++++++++++++ clang/lib/Driver/ToolChains/Serenity.h | 100 +++++++ 7 files changed, 469 insertions(+) create mode 100644 clang/lib/Driver/ToolChains/Serenity.cpp create mode 100644 clang/lib/Driver/ToolChains/Serenity.h diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp index 8400774db..f82618dc8 100644 --- a/clang/lib/Basic/Targets.cpp +++ b/clang/lib/Basic/Targets.cpp @@ -153,6 +153,8 @@ TargetInfo *AllocateTarget(const llvm::Triple &Triple, return new NetBSDTargetInfo(Triple, Opts); case llvm::Triple::OpenBSD: return new OpenBSDTargetInfo(Triple, Opts); + case llvm::Triple::Serenity: + return new SerenityTargetInfo(Triple, Opts); case llvm::Triple::Win32: switch (Triple.getEnvironment()) { case llvm::Triple::GNU: @@ -424,6 +426,8 @@ TargetInfo *AllocateTarget(const llvm::Triple &Triple, return new FuchsiaTargetInfo(Triple, Opts); case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); + case llvm::Triple::Serenity: + return new SerenityTargetInfo(Triple, Opts); default: return new RISCV64TargetInfo(Triple, Opts); } @@ -542,6 +546,8 @@ TargetInfo *AllocateTarget(const llvm::Triple &Triple, return new MCUX86_32TargetInfo(Triple, Opts); case llvm::Triple::Hurd: return new HurdTargetInfo(Triple, Opts); + case llvm::Triple::Serenity: + return new SerenityTargetInfo(Triple, Opts); default: return new X86_32TargetInfo(Triple, Opts); } @@ -596,6 +602,8 @@ TargetInfo *AllocateTarget(const llvm::Triple &Triple, return new PS4OSTargetInfo(Triple, Opts); case llvm::Triple::PS5: return new PS5OSTargetInfo(Triple, Opts); + case llvm::Triple::Serenity: + return new SerenityTargetInfo(Triple, Opts); default: return new X86_64TargetInfo(Triple, Opts); } diff --git a/clang/lib/Basic/Targets/OSTargets.h b/clang/lib/Basic/Targets/OSTargets.h index fd372cb12..27a3f9797 100644 --- a/clang/lib/Basic/Targets/OSTargets.h +++ b/clang/lib/Basic/Targets/OSTargets.h @@ -1005,6 +1005,24 @@ public: } }; +// SerenityOS target +template +class LLVM_LIBRARY_VISIBILITY SerenityTargetInfo : public OSTargetInfo { +protected: + void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple, + MacroBuilder &Builder) const override { + Builder.defineMacro("__serenity__"); + DefineStd(Builder, "unix", Opts); + Builder.defineMacro("__ELF__"); + } + +public: + SerenityTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts) + : OSTargetInfo(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 ba56a9323..4d0ffe88c 100644 --- a/clang/lib/Driver/CMakeLists.txt +++ b/clang/lib/Driver/CMakeLists.txt @@ -78,6 +78,7 @@ add_clang_library(clangDriver ToolChains/OpenBSD.cpp ToolChains/PS4CPU.cpp ToolChains/RISCVToolchain.cpp + ToolChains/Serenity.cpp ToolChains/Solaris.cpp ToolChains/SPIRV.cpp ToolChains/TCE.cpp diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index a268f2fa8..df54be3de 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -45,6 +45,7 @@ #include "ToolChains/PPCLinux.h" #include "ToolChains/PS4CPU.h" #include "ToolChains/RISCVToolchain.h" +#include "ToolChains/Serenity.h" #include "ToolChains/SPIRV.h" #include "ToolChains/Solaris.h" #include "ToolChains/TCE.h" @@ -6041,6 +6042,9 @@ const ToolChain &Driver::getToolChain(const ArgList &Args, case llvm::Triple::Fuchsia: TC = std::make_unique(*this, Target, Args); break; + case llvm::Triple::Serenity: + TC = std::make_unique(*this, Target, Args); + break; case llvm::Triple::Solaris: TC = std::make_unique(*this, Target, Args); break; diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index bc70205a6..3d5c856ab 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -482,6 +482,8 @@ StringRef ToolChain::getOSLibName() const { return "sunos"; case llvm::Triple::AIX: return "aix"; + case llvm::Triple::Serenity: + return "serenity"; default: return getOS(); } diff --git a/clang/lib/Driver/ToolChains/Serenity.cpp b/clang/lib/Driver/ToolChains/Serenity.cpp new file mode 100644 index 000000000..4fdf45a19 --- /dev/null +++ b/clang/lib/Driver/ToolChains/Serenity.cpp @@ -0,0 +1,340 @@ +//===---- Serenity.cpp - SerenityOS 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 "Serenity.h" +#include "CommonArgs.h" +#include "clang/Config/config.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "clang/Driver/SanitizerArgs.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/VirtualFileSystem.h" +#include + +using namespace clang::driver; +using namespace clang::driver::toolchains; +using namespace clang; +using namespace llvm::opt; + +static bool getPIE(const ArgList &Args, const ToolChain &TC) { + if (Args.hasArg(options::OPT_static, options::OPT_shared, + options::OPT_static_pie)) + return false; + Arg *Last = Args.getLastArg(options::OPT_pie, options::OPT_no_pie, + options::OPT_nopie); + return Last ? Last->getOption().matches(options::OPT_pie) : true; +} + +void tools::serenity::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 IsRdynamic = Args.hasArg(options::OPT_rdynamic); + const bool IsStaticPIE = Args.hasArg(options::OPT_static_pie); + const bool IsPIE = getPIE(Args, TC); + ArgStringList CmdArgs; + + if (!D.SysRoot.empty()) + CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot)); + + if (IsPIE || IsStaticPIE) + CmdArgs.push_back("-pie"); + + if (IsShared) + CmdArgs.push_back("-shared"); + + if (IsStatic || IsStaticPIE) + CmdArgs.push_back("-static"); + + if (IsStaticPIE) { + CmdArgs.push_back("--no-dynamic-linker"); + CmdArgs.push_back("-z"); + CmdArgs.push_back("text"); + } + + if (!IsStatic && !IsStaticPIE) { + if (IsRdynamic) + CmdArgs.push_back("-export-dynamic"); + CmdArgs.push_back("-dynamic-linker"); + CmdArgs.push_back("/usr/lib/Loader.so"); + } + + if (!IsStatic || IsStaticPIE) + CmdArgs.push_back("--eh-frame-hdr"); + + if (Output.isFilename()) { + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + } + + const char *Exec = Args.MakeArgString(TC.GetLinkerPath()); + auto linkerIs = [Exec](const char* name) { + return llvm::sys::path::filename(Exec).equals_insensitive(name) || + llvm::sys::path::stem(Exec).equals_insensitive(name); + }; + + if (linkerIs("ld.lld") || linkerIs("ld.mold")) { + CmdArgs.push_back("--pack-dyn-relocs=relr"); + } else { + CmdArgs.push_back("-z"); + CmdArgs.push_back("pack-relative-relocs"); + } + + if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles)) { + CmdArgs.push_back(Args.MakeArgString( + TC.GetFilePath((IsShared) ? "crt0_shared.o" : "crt0.o"))); + CmdArgs.push_back(Args.MakeArgString(TC.GetFilePath("crti.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()) { + const char *crtbegin = (IsShared || IsPIE) ? "crtbeginS.o" : "crtbegin.o"; + crtbegin_path = TC.GetFilePath(crtbegin); + } + CmdArgs.push_back(Args.MakeArgString(crtbegin_path)); + } + + Args.AddAllArgs(CmdArgs, options::OPT_L); + Args.AddAllArgs(CmdArgs, options::OPT_u); + + TC.AddFilePathLibArgs(Args, CmdArgs); + + if (D.isUsingLTO()) { + assert(!Inputs.empty() && "Must have at least one input."); + addLTOOptions(TC, Args, CmdArgs, Output, Inputs[0], + D.getLTOMode() == LTOK_Thin); + } + + Args.AddAllArgs(CmdArgs, options::OPT_T_Group); + Args.AddAllArgs(CmdArgs, options::OPT_e); + Args.AddAllArgs(CmdArgs, options::OPT_s); + Args.AddAllArgs(CmdArgs, options::OPT_t); + Args.AddAllArgs(CmdArgs, options::OPT_Z_Flag); + Args.AddAllArgs(CmdArgs, options::OPT_r); + + addLinkerCompressDebugSectionsOption(TC, Args, CmdArgs); + + AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA); + + if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) { + AddRunTimeLibs(TC, D, CmdArgs, Args); + + // We supply our own sanitizer runtimes + // FIXME: What if we want to use Clang-supplied ones as well? + const SanitizerArgs &Sanitize = TC.getSanitizerArgs(Args); + if (Sanitize.needsUbsanRt()) + CmdArgs.push_back("-lubsan"); + } + + if (D.CCCIsCXX() && TC.ShouldLinkCXXStdlib(Args)) { + 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"); + } + + if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) { + if (!Args.hasArg(options::OPT_nolibc)) + CmdArgs.push_back("-lc"); + } + + if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles)) { + 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()) { + const char *crtend = (IsShared || IsPIE) ? "crtendS.o" : "crtend.o"; + crtend_path = TC.GetFilePath(crtend); + } + CmdArgs.push_back(Args.MakeArgString(crtend_path)); + + CmdArgs.push_back(Args.MakeArgString(TC.GetFilePath("crtn.o"))); + } + + Args.AddAllArgs(CmdArgs, options::OPT_T); + + C.addCommand(std::make_unique(JA, *this, + ResponseFileSupport::AtFileCurCP(), + Exec, CmdArgs, Inputs, Output)); +} + +Serenity::Serenity(const Driver &D, const llvm::Triple &Triple, + const ArgList &Args) + : ToolChain(D, Triple, Args) { + getProgramPaths().push_back(getDriver().getInstalledDir()); + if (getDriver().getInstalledDir() != getDriver().Dir) + getProgramPaths().push_back(getDriver().Dir); + getFilePaths().push_back(getDriver().SysRoot + "/usr/local/lib"); + getFilePaths().push_back(getDriver().SysRoot + "/usr/lib"); +} + +Tool *Serenity::buildLinker() const { + return new tools::serenity::Linker(*this); +} + +void Serenity::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, D.ResourceDir + "/include"); + } + + if (DriverArgs.hasArg(options::OPT_nostdlibinc)) + return; + + addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/usr/local/include"); + addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/usr/include"); +} + +static std::string getLibStdCXXVersion(const Driver &D, StringRef IncludePath) { + SmallString<128> Path(IncludePath); + llvm::sys::path::append(Path, "c++"); + + std::error_code EC; + std::tuple Newest{0, 0, 0}; + std::string Result; + + for (llvm::vfs::directory_iterator LI = D.getVFS().dir_begin(Path, EC), LE; + !EC && LI != LE; LI = LI.increment(EC)) { + StringRef VersionText = llvm::sys::path::filename(LI->path()); + + // This is libc++ + if (VersionText[0] == 'v') + continue; + + llvm::SmallVector Parts; + VersionText.split(Parts, '.'); + + // SerenityOS always builds GCC with .. versions + if (Parts.size() < 3) + continue; + + std::tuple Current; + if (!llvm::to_integer(Parts[0], std::get<0>(Current))) + continue; + if (!llvm::to_integer(Parts[1], std::get<1>(Current))) + continue; + if (!llvm::to_integer(Parts[2], std::get<2>(Current))) + continue; + + if (Current > Newest) { + Newest = Current; + Result = VersionText.str(); + } + } + return Result; +} + +void Serenity::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, + ArgStringList &CC1Args) const { + if (DriverArgs.hasArg(options::OPT_nostdinc, options::OPT_nostdincxx, + options::OPT_nostdlibinc)) + return; + + const auto StdLib = GetCXXStdlibType(DriverArgs); + + const Driver &D = getDriver(); + std::string SysRoot = computeSysRoot(); + std::string Target = getTripleString(); + + auto AddIncludePath = [&](std::string Path) { + std::string Version = StdLib == CXXStdlibType::CST_Libstdcxx + ? getLibStdCXXVersion(D, Path) + : detectLibcxxVersion(Path); + if (Version.empty()) + return false; + + std::string TargetDir; + if (StdLib == CXXStdlibType::CST_Libstdcxx) { + TargetDir = Path + "/c++/" + Version + "/" + Target; + } else { + TargetDir = Path + "/" + Target + "/c++/" + Version; + } + + if (D.getVFS().exists(TargetDir)) + addSystemInclude(DriverArgs, CC1Args, TargetDir); + + addSystemInclude(DriverArgs, CC1Args, Path + "/c++/" + Version); + return true; + }; + + if (AddIncludePath(getDriver().Dir + "/../include")) + return; + if (AddIncludePath(SysRoot + "/usr/local/include")) + return; + if (AddIncludePath(SysRoot + "/usr/include")) + return; +} + +void Serenity::addClangTargetOptions( + const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, + Action::OffloadKind DeviceOffloadKind) const { + if (!DriverArgs.hasFlag(options::OPT_fuse_init_array, + options::OPT_fno_use_init_array, true)) + CC1Args.push_back("-fno-use-init-array"); +} + +ToolChain::UnwindLibType +Serenity::GetUnwindLibType(const llvm::opt::ArgList &Args) const { + + const Arg *A = Args.getLastArg(options::OPT_unwindlib_EQ); + StringRef LibName = A ? A->getValue() : CLANG_DEFAULT_UNWINDLIB; + + if (LibName == "none") { + return ToolChain::UNW_None; + } else if (LibName == "platform" || LibName == "") { + ToolChain::RuntimeLibType RtLibType = GetRuntimeLibType(Args); + if (RtLibType == ToolChain::RLT_CompilerRT) { + return ToolChain::UNW_CompilerRT; + } else if (RtLibType == ToolChain::RLT_Libgcc) + return ToolChain::UNW_Libgcc; + } else if (LibName == "libunwind") { + if (GetRuntimeLibType(Args) == RLT_Libgcc) + getDriver().Diag(diag::err_drv_incompatible_unwindlib); + return ToolChain::UNW_CompilerRT; + } else if (LibName == "libgcc") { + return ToolChain::UNW_Libgcc; + } + + if (A) + getDriver().Diag(diag::err_drv_invalid_unwindlib_name) + << A->getAsString(Args); + + return ToolChain::UNW_None; +} + +SanitizerMask Serenity::getSupportedSanitizers() const { + return ToolChain::getSupportedSanitizers() | SanitizerKind::KernelAddress; +} diff --git a/clang/lib/Driver/ToolChains/Serenity.h b/clang/lib/Driver/ToolChains/Serenity.h new file mode 100644 index 000000000..feb31a0d6 --- /dev/null +++ b/clang/lib/Driver/ToolChains/Serenity.h @@ -0,0 +1,102 @@ +//===---- Serenity.h - SerenityOS 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_SERENITY_H +#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_SERENITY_H + +#include "clang/Basic/LangOptions.h" +#include "clang/Driver/Tool.h" +#include "clang/Driver/ToolChain.h" + +namespace clang { +namespace driver { +namespace tools { +namespace serenity { + +class LLVM_LIBRARY_VISIBILITY Linker : public Tool { +public: + Linker(const ToolChain &TC) : Tool("serenity::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 serenity +} // end namespace tools + +namespace toolchains { + +class LLVM_LIBRARY_VISIBILITY Serenity : public ToolChain { +public: + Serenity(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; + + void AddClangCXXStdlibIncludeArgs( + 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; + } + + UnwindLibType GetUnwindLibType(const llvm::opt::ArgList &Args) const override; + + void + addClangTargetOptions(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args, + Action::OffloadKind DeviceOffloadKind) const override; + + const char *getDefaultLinker() const override { return "ld.lld"; } + + bool HasNativeLLVMSupport() const override { return true; } + + bool IsIntegratedAssemblerDefault() const override { return true; } + + bool isPICDefault() const override { return true; } + bool isPIEDefault(const llvm::opt::ArgList&) const override { return false; } + bool isPICDefaultForced() const override { return false; } + + SanitizerMask getSupportedSanitizers() const override; + + bool IsMathErrnoDefault() const override { return false; } + + UnwindTableLevel + getDefaultUnwindTableLevel(const llvm::opt::ArgList &Args) const override { + return UnwindTableLevel::Asynchronous; + } + + bool useRelaxRelocations() const override { return true; } + + LangOptions::StackProtectorMode + GetDefaultStackProtectorLevel(bool KernelOrKext) const override { + return LangOptions::SSPStrong; + } + + unsigned GetDefaultDwarfVersion() const override { return 5; } + +protected: + Tool *buildLinker() const override; +}; + +} // end namespace toolchains +} // end namespace driver +} // end namespace clang + +#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_SERENITY_H