| 1 | //===---- Serenity.cpp - SerenityOS ToolChain Implementation ----*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "Serenity.h" |
| 10 | #include "clang/Config/config.h" |
| 11 | #include "clang/Driver/CommonArgs.h" |
| 12 | #include "clang/Driver/Compilation.h" |
| 13 | #include "clang/Driver/Driver.h" |
| 14 | #include "clang/Driver/SanitizerArgs.h" |
| 15 | #include "clang/Options/Options.h" |
| 16 | #include "llvm/Option/ArgList.h" |
| 17 | #include "llvm/Support/VirtualFileSystem.h" |
| 18 | #include <string> |
| 19 | |
| 20 | using namespace clang::driver; |
| 21 | using namespace clang::driver::toolchains; |
| 22 | using namespace clang; |
| 23 | using namespace llvm::opt; |
| 24 | |
| 25 | void tools::serenity::Linker::ConstructJob(Compilation &C, const JobAction &JA, |
| 26 | const InputInfo &Output, |
| 27 | const InputInfoList &Inputs, |
| 28 | const ArgList &Args, |
| 29 | const char *LinkingOutput) const { |
| 30 | const auto &TC = static_cast<Generic_ELF const &>(getToolChain()); |
| 31 | const auto &D = TC.getDriver(); |
| 32 | const bool IsShared = Args.hasArg(Ids: options::OPT_shared); |
| 33 | const bool IsStatic = |
| 34 | Args.hasArg(Ids: options::OPT_static) && !Args.hasArg(Ids: options::OPT_static_pie); |
| 35 | const bool IsStaticPIE = Args.hasArg(Ids: options::OPT_static_pie); |
| 36 | ArgStringList CmdArgs; |
| 37 | |
| 38 | if (!D.SysRoot.empty()) |
| 39 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: "--sysroot=" + D.SysRoot)); |
| 40 | |
| 41 | if (IsShared) |
| 42 | CmdArgs.push_back(Elt: "-shared" ); |
| 43 | |
| 44 | if (IsStaticPIE) { |
| 45 | CmdArgs.push_back(Elt: "-static" ); |
| 46 | CmdArgs.push_back(Elt: "-pie" ); |
| 47 | CmdArgs.push_back(Elt: "--no-dynamic-linker" ); |
| 48 | CmdArgs.push_back(Elt: "-z" ); |
| 49 | CmdArgs.push_back(Elt: "text" ); |
| 50 | } else if (IsStatic) { |
| 51 | CmdArgs.push_back(Elt: "-static" ); |
| 52 | } else if (!Args.hasArg(Ids: options::OPT_r)) { |
| 53 | if (Args.hasArg(Ids: options::OPT_rdynamic)) |
| 54 | CmdArgs.push_back(Elt: "-export-dynamic" ); |
| 55 | if (!IsShared) { |
| 56 | Arg *A = Args.getLastArg(Ids: options::OPT_pie, Ids: options::OPT_no_pie, |
| 57 | Ids: options::OPT_nopie); |
| 58 | bool IsPIE = |
| 59 | A ? A->getOption().matches(ID: options::OPT_pie) : TC.isPIEDefault(Args); |
| 60 | if (IsPIE) |
| 61 | CmdArgs.push_back(Elt: "-pie" ); |
| 62 | CmdArgs.push_back(Elt: "-dynamic-linker" ); |
| 63 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: TC.getDynamicLinker(Args))); |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | CmdArgs.push_back(Elt: "--eh-frame-hdr" ); |
| 68 | |
| 69 | assert((Output.isFilename() || Output.isNothing()) && "Invalid output." ); |
| 70 | if (Output.isFilename()) { |
| 71 | CmdArgs.push_back(Elt: "-o" ); |
| 72 | CmdArgs.push_back(Elt: Output.getFilename()); |
| 73 | } |
| 74 | |
| 75 | CmdArgs.push_back(Elt: "-z" ); |
| 76 | CmdArgs.push_back(Elt: "pack-relative-relocs" ); |
| 77 | |
| 78 | bool HasNoStdLib = Args.hasArg(Ids: options::OPT_nostdlib, Ids: options::OPT_r); |
| 79 | bool HasNoStdLibXX = Args.hasArg(Ids: options::OPT_nostdlibxx); |
| 80 | bool HasNoLibC = Args.hasArg(Ids: options::OPT_nolibc); |
| 81 | bool HasNoStartFiles = Args.hasArg(Ids: options::OPT_nostartfiles); |
| 82 | bool HasNoDefaultLibs = Args.hasArg(Ids: options::OPT_nodefaultlibs); |
| 83 | |
| 84 | bool ShouldLinkStartFiles = !HasNoStartFiles && !HasNoStdLib; |
| 85 | bool ShouldLinkCompilerRuntime = !HasNoDefaultLibs && !HasNoStdLib; |
| 86 | bool ShouldLinkLibC = !HasNoLibC && !HasNoStdLib && !HasNoDefaultLibs; |
| 87 | bool ShouldLinkLibCXX = |
| 88 | D.CCCIsCXX() && !HasNoStdLibXX && !HasNoStdLib && !HasNoDefaultLibs; |
| 89 | |
| 90 | if (ShouldLinkStartFiles) { |
| 91 | if (!IsShared) |
| 92 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: TC.GetFilePath(Name: "crt0.o" ))); |
| 93 | |
| 94 | std::string crtbegin_path; |
| 95 | if (TC.GetRuntimeLibType(Args) == ToolChain::RLT_CompilerRT) { |
| 96 | std::string crtbegin = |
| 97 | TC.getCompilerRT(Args, Component: "crtbegin" , Type: ToolChain::FT_Object); |
| 98 | if (TC.getVFS().exists(Path: crtbegin)) |
| 99 | crtbegin_path = crtbegin; |
| 100 | } |
| 101 | if (crtbegin_path.empty()) |
| 102 | crtbegin_path = TC.GetFilePath(Name: "crtbeginS.o" ); |
| 103 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: crtbegin_path)); |
| 104 | } |
| 105 | |
| 106 | Args.addAllArgs(Output&: CmdArgs, Ids: {options::OPT_L, options::OPT_u}); |
| 107 | |
| 108 | TC.AddFilePathLibArgs(Args, CmdArgs); |
| 109 | |
| 110 | if (auto LTO = TC.getLTOMode(Args); LTO != LTOK_None) |
| 111 | addLTOOptions(ToolChain: TC, Args, CmdArgs, Output, Inputs, IsThinLTO: LTO == LTOK_Thin); |
| 112 | |
| 113 | Args.addAllArgs(Output&: CmdArgs, Ids: {options::OPT_T_Group, options::OPT_s, |
| 114 | options::OPT_t, options::OPT_r}); |
| 115 | |
| 116 | addLinkerCompressDebugSectionsOption(TC, Args, CmdArgs); |
| 117 | |
| 118 | AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA); |
| 119 | |
| 120 | if (ShouldLinkCompilerRuntime) { |
| 121 | AddRunTimeLibs(TC, D, CmdArgs, Args); |
| 122 | |
| 123 | // We supply our own sanitizer runtimes that output errors to the |
| 124 | // Kernel debug log as well as stderr. |
| 125 | // FIXME: Properly port clang/gcc sanitizers and use those instead. |
| 126 | const SanitizerArgs &Sanitize = TC.getSanitizerArgs(JobArgs: Args); |
| 127 | if (Sanitize.needsUbsanRt()) |
| 128 | CmdArgs.push_back(Elt: "-lubsan" ); |
| 129 | } |
| 130 | |
| 131 | if (ShouldLinkLibCXX) { |
| 132 | bool OnlyLibstdcxxStatic = Args.hasArg(Ids: options::OPT_static_libstdcxx) && |
| 133 | !Args.hasArg(Ids: options::OPT_static); |
| 134 | CmdArgs.push_back(Elt: "--push-state" ); |
| 135 | CmdArgs.push_back(Elt: "--as-needed" ); |
| 136 | if (OnlyLibstdcxxStatic) |
| 137 | CmdArgs.push_back(Elt: "-Bstatic" ); |
| 138 | TC.AddCXXStdlibLibArgs(Args, CmdArgs); |
| 139 | if (OnlyLibstdcxxStatic) |
| 140 | CmdArgs.push_back(Elt: "-Bdynamic" ); |
| 141 | CmdArgs.push_back(Elt: "--pop-state" ); |
| 142 | } |
| 143 | |
| 144 | // Silence warnings when linking C code with a C++ '-stdlib' argument. |
| 145 | Args.ClaimAllArgs(Id0: options::OPT_stdlib_EQ); |
| 146 | |
| 147 | if (ShouldLinkLibC) |
| 148 | CmdArgs.push_back(Elt: "-lc" ); |
| 149 | |
| 150 | if (ShouldLinkStartFiles) { |
| 151 | std::string crtend_path; |
| 152 | if (TC.GetRuntimeLibType(Args) == ToolChain::RLT_CompilerRT) { |
| 153 | std::string crtend = |
| 154 | TC.getCompilerRT(Args, Component: "crtend" , Type: ToolChain::FT_Object); |
| 155 | if (TC.getVFS().exists(Path: crtend)) |
| 156 | crtend_path = crtend; |
| 157 | } |
| 158 | if (crtend_path.empty()) |
| 159 | crtend_path = TC.GetFilePath(Name: "crtendS.o" ); |
| 160 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: crtend_path)); |
| 161 | } |
| 162 | |
| 163 | const char *Exec = Args.MakeArgString(Str: TC.GetLinkerPath()); |
| 164 | C.addCommand(Cmd: std::make_unique<Command>(args: JA, args: *this, |
| 165 | args: ResponseFileSupport::AtFileCurCP(), |
| 166 | args&: Exec, args&: CmdArgs, args: Inputs, args: Output)); |
| 167 | } |
| 168 | |
| 169 | SanitizerMask |
| 170 | Serenity::getSupportedSanitizers(BoundArch BA, |
| 171 | Action::OffloadKind DeviceOffloadKind) const { |
| 172 | return ToolChain::getSupportedSanitizers(BA, DeviceOffloadKind) | |
| 173 | SanitizerKind::KernelAddress; |
| 174 | } |
| 175 | |
| 176 | Serenity::Serenity(const Driver &D, const llvm::Triple &Triple, |
| 177 | const ArgList &Args) |
| 178 | : Generic_ELF(D, Triple, Args) { |
| 179 | getFilePaths().push_back(Elt: concat(Path: getDriver().SysRoot, A: "/usr/lib" )); |
| 180 | } |
| 181 | |
| 182 | Tool *Serenity::buildLinker() const { |
| 183 | return new tools::serenity::Linker(*this); |
| 184 | } |
| 185 | |
| 186 | void Serenity::AddClangSystemIncludeArgs(const ArgList &DriverArgs, |
| 187 | ArgStringList &CC1Args) const { |
| 188 | const Driver &D = getDriver(); |
| 189 | |
| 190 | if (DriverArgs.hasArg(Ids: options::OPT_nostdinc)) |
| 191 | return; |
| 192 | |
| 193 | if (!DriverArgs.hasArg(Ids: options::OPT_nobuiltininc)) |
| 194 | addSystemInclude(DriverArgs, CC1Args, Path: concat(Path: D.ResourceDir, A: "/include" )); |
| 195 | |
| 196 | if (DriverArgs.hasArg(Ids: options::OPT_nostdlibinc)) |
| 197 | return; |
| 198 | |
| 199 | addSystemInclude(DriverArgs, CC1Args, Path: concat(Path: D.SysRoot, A: "/usr/include" )); |
| 200 | } |
| 201 | |