1 //===--- PS4CPU.cpp - PS4CPU ToolChain Implementations ----------*- 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 "PS4CPU.h" 10 #include "FreeBSD.h" 11 #include "CommonArgs.h" 12 #include "clang/Driver/Compilation.h" 13 #include "clang/Driver/Driver.h" 14 #include "clang/Driver/DriverDiagnostic.h" 15 #include "clang/Driver/Options.h" 16 #include "clang/Driver/SanitizerArgs.h" 17 #include "llvm/Option/ArgList.h" 18 #include "llvm/Support/FileSystem.h" 19 #include "llvm/Support/Path.h" 20 #include <cstdlib> // ::getenv 21 22 using namespace clang::driver; 23 using namespace clang; 24 using namespace llvm::opt; 25 26 using clang::driver::tools::AddLinkerInputs; 27 28 void tools::PS4cpu::addProfileRTArgs(const ToolChain &TC, const ArgList &Args, 29 ArgStringList &CmdArgs) { 30 if ((Args.hasFlag(options::OPT_fprofile_arcs, options::OPT_fno_profile_arcs, 31 false) || 32 Args.hasFlag(options::OPT_fprofile_generate, 33 options::OPT_fno_profile_instr_generate, false) || 34 Args.hasFlag(options::OPT_fprofile_generate_EQ, 35 options::OPT_fno_profile_instr_generate, false) || 36 Args.hasFlag(options::OPT_fprofile_instr_generate, 37 options::OPT_fno_profile_instr_generate, false) || 38 Args.hasFlag(options::OPT_fprofile_instr_generate_EQ, 39 options::OPT_fno_profile_instr_generate, false) || 40 Args.hasArg(options::OPT_fcreate_profile) || 41 Args.hasArg(options::OPT_coverage))) 42 CmdArgs.push_back("--dependent-lib=libclang_rt.profile-x86_64.a"); 43 } 44 45 void tools::PS4cpu::Assemble::ConstructJob(Compilation &C, const JobAction &JA, 46 const InputInfo &Output, 47 const InputInfoList &Inputs, 48 const ArgList &Args, 49 const char *LinkingOutput) const { 50 claimNoWarnArgs(Args); 51 ArgStringList CmdArgs; 52 53 Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, options::OPT_Xassembler); 54 55 CmdArgs.push_back("-o"); 56 CmdArgs.push_back(Output.getFilename()); 57 58 assert(Inputs.size() == 1 && "Unexpected number of inputs."); 59 const InputInfo &Input = Inputs[0]; 60 assert(Input.isFilename() && "Invalid input."); 61 CmdArgs.push_back(Input.getFilename()); 62 63 const char *Exec = 64 Args.MakeArgString(getToolChain().GetProgramPath("orbis-as")); 65 C.addCommand(llvm::make_unique<Command>(JA, *this, Exec, CmdArgs, Inputs)); 66 } 67 68 static void AddPS4SanitizerArgs(const ToolChain &TC, ArgStringList &CmdArgs) { 69 const SanitizerArgs &SanArgs = TC.getSanitizerArgs(); 70 if (SanArgs.needsUbsanRt()) { 71 CmdArgs.push_back("-lSceDbgUBSanitizer_stub_weak"); 72 } 73 if (SanArgs.needsAsanRt()) { 74 CmdArgs.push_back("-lSceDbgAddressSanitizer_stub_weak"); 75 } 76 } 77 78 void tools::PS4cpu::addSanitizerArgs(const ToolChain &TC, 79 ArgStringList &CmdArgs) { 80 const SanitizerArgs &SanArgs = TC.getSanitizerArgs(); 81 if (SanArgs.needsUbsanRt()) 82 CmdArgs.push_back("--dependent-lib=libSceDbgUBSanitizer_stub_weak.a"); 83 if (SanArgs.needsAsanRt()) 84 CmdArgs.push_back("--dependent-lib=libSceDbgAddressSanitizer_stub_weak.a"); 85 } 86 87 static void ConstructPS4LinkJob(const Tool &T, Compilation &C, 88 const JobAction &JA, const InputInfo &Output, 89 const InputInfoList &Inputs, 90 const ArgList &Args, 91 const char *LinkingOutput) { 92 const toolchains::FreeBSD &ToolChain = 93 static_cast<const toolchains::FreeBSD &>(T.getToolChain()); 94 const Driver &D = ToolChain.getDriver(); 95 ArgStringList CmdArgs; 96 97 // Silence warning for "clang -g foo.o -o foo" 98 Args.ClaimAllArgs(options::OPT_g_Group); 99 // and "clang -emit-llvm foo.o -o foo" 100 Args.ClaimAllArgs(options::OPT_emit_llvm); 101 // and for "clang -w foo.o -o foo". Other warning options are already 102 // handled somewhere else. 103 Args.ClaimAllArgs(options::OPT_w); 104 105 if (!D.SysRoot.empty()) 106 CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot)); 107 108 if (Args.hasArg(options::OPT_pie)) 109 CmdArgs.push_back("-pie"); 110 111 if (Args.hasArg(options::OPT_rdynamic)) 112 CmdArgs.push_back("-export-dynamic"); 113 if (Args.hasArg(options::OPT_shared)) 114 CmdArgs.push_back("--oformat=so"); 115 116 if (Output.isFilename()) { 117 CmdArgs.push_back("-o"); 118 CmdArgs.push_back(Output.getFilename()); 119 } else { 120 assert(Output.isNothing() && "Invalid output."); 121 } 122 123 if(!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) 124 AddPS4SanitizerArgs(ToolChain, CmdArgs); 125 126 Args.AddAllArgs(CmdArgs, options::OPT_L); 127 Args.AddAllArgs(CmdArgs, options::OPT_T_Group); 128 Args.AddAllArgs(CmdArgs, options::OPT_e); 129 Args.AddAllArgs(CmdArgs, options::OPT_s); 130 Args.AddAllArgs(CmdArgs, options::OPT_t); 131 Args.AddAllArgs(CmdArgs, options::OPT_r); 132 133 if (Args.hasArg(options::OPT_Z_Xlinker__no_demangle)) 134 CmdArgs.push_back("--no-demangle"); 135 136 AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA); 137 138 if (Args.hasArg(options::OPT_pthread)) { 139 CmdArgs.push_back("-lpthread"); 140 } 141 142 const char *Exec = Args.MakeArgString(ToolChain.GetProgramPath("orbis-ld")); 143 144 C.addCommand(llvm::make_unique<Command>(JA, T, Exec, CmdArgs, Inputs)); 145 } 146 147 static void ConstructGoldLinkJob(const Tool &T, Compilation &C, 148 const JobAction &JA, const InputInfo &Output, 149 const InputInfoList &Inputs, 150 const ArgList &Args, 151 const char *LinkingOutput) { 152 const toolchains::FreeBSD &ToolChain = 153 static_cast<const toolchains::FreeBSD &>(T.getToolChain()); 154 const Driver &D = ToolChain.getDriver(); 155 ArgStringList CmdArgs; 156 157 // Silence warning for "clang -g foo.o -o foo" 158 Args.ClaimAllArgs(options::OPT_g_Group); 159 // and "clang -emit-llvm foo.o -o foo" 160 Args.ClaimAllArgs(options::OPT_emit_llvm); 161 // and for "clang -w foo.o -o foo". Other warning options are already 162 // handled somewhere else. 163 Args.ClaimAllArgs(options::OPT_w); 164 165 if (!D.SysRoot.empty()) 166 CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot)); 167 168 if (Args.hasArg(options::OPT_pie)) 169 CmdArgs.push_back("-pie"); 170 171 if (Args.hasArg(options::OPT_static)) { 172 CmdArgs.push_back("-Bstatic"); 173 } else { 174 if (Args.hasArg(options::OPT_rdynamic)) 175 CmdArgs.push_back("-export-dynamic"); 176 CmdArgs.push_back("--eh-frame-hdr"); 177 if (Args.hasArg(options::OPT_shared)) { 178 CmdArgs.push_back("-Bshareable"); 179 } else { 180 CmdArgs.push_back("-dynamic-linker"); 181 CmdArgs.push_back("/libexec/ld-elf.so.1"); 182 } 183 CmdArgs.push_back("--enable-new-dtags"); 184 } 185 186 if (Output.isFilename()) { 187 CmdArgs.push_back("-o"); 188 CmdArgs.push_back(Output.getFilename()); 189 } else { 190 assert(Output.isNothing() && "Invalid output."); 191 } 192 193 if(!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) 194 AddPS4SanitizerArgs(ToolChain, CmdArgs); 195 196 if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles)) { 197 const char *crt1 = nullptr; 198 if (!Args.hasArg(options::OPT_shared)) { 199 if (Args.hasArg(options::OPT_pg)) 200 crt1 = "gcrt1.o"; 201 else if (Args.hasArg(options::OPT_pie)) 202 crt1 = "Scrt1.o"; 203 else 204 crt1 = "crt1.o"; 205 } 206 if (crt1) 207 CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crt1))); 208 209 CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crti.o"))); 210 211 const char *crtbegin = nullptr; 212 if (Args.hasArg(options::OPT_static)) 213 crtbegin = "crtbeginT.o"; 214 else if (Args.hasArg(options::OPT_shared) || Args.hasArg(options::OPT_pie)) 215 crtbegin = "crtbeginS.o"; 216 else 217 crtbegin = "crtbegin.o"; 218 219 CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crtbegin))); 220 } 221 222 Args.AddAllArgs(CmdArgs, options::OPT_L); 223 ToolChain.AddFilePathLibArgs(Args, CmdArgs); 224 Args.AddAllArgs(CmdArgs, options::OPT_T_Group); 225 Args.AddAllArgs(CmdArgs, options::OPT_e); 226 Args.AddAllArgs(CmdArgs, options::OPT_s); 227 Args.AddAllArgs(CmdArgs, options::OPT_t); 228 Args.AddAllArgs(CmdArgs, options::OPT_r); 229 230 if (Args.hasArg(options::OPT_Z_Xlinker__no_demangle)) 231 CmdArgs.push_back("--no-demangle"); 232 233 AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA); 234 235 if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) { 236 // For PS4, we always want to pass libm, libstdc++ and libkernel 237 // libraries for both C and C++ compilations. 238 CmdArgs.push_back("-lkernel"); 239 if (D.CCCIsCXX()) { 240 if (ToolChain.ShouldLinkCXXStdlib(Args)) 241 ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs); 242 if (Args.hasArg(options::OPT_pg)) 243 CmdArgs.push_back("-lm_p"); 244 else 245 CmdArgs.push_back("-lm"); 246 } 247 // FIXME: For some reason GCC passes -lgcc and -lgcc_s before adding 248 // the default system libraries. Just mimic this for now. 249 if (Args.hasArg(options::OPT_pg)) 250 CmdArgs.push_back("-lgcc_p"); 251 else 252 CmdArgs.push_back("-lcompiler_rt"); 253 if (Args.hasArg(options::OPT_static)) { 254 CmdArgs.push_back("-lstdc++"); 255 } else if (Args.hasArg(options::OPT_pg)) { 256 CmdArgs.push_back("-lgcc_eh_p"); 257 } else { 258 CmdArgs.push_back("--as-needed"); 259 CmdArgs.push_back("-lstdc++"); 260 CmdArgs.push_back("--no-as-needed"); 261 } 262 263 if (Args.hasArg(options::OPT_pthread)) { 264 if (Args.hasArg(options::OPT_pg)) 265 CmdArgs.push_back("-lpthread_p"); 266 else 267 CmdArgs.push_back("-lpthread"); 268 } 269 270 if (Args.hasArg(options::OPT_pg)) { 271 if (Args.hasArg(options::OPT_shared)) 272 CmdArgs.push_back("-lc"); 273 else { 274 if (Args.hasArg(options::OPT_static)) { 275 CmdArgs.push_back("--start-group"); 276 CmdArgs.push_back("-lc_p"); 277 CmdArgs.push_back("-lpthread_p"); 278 CmdArgs.push_back("--end-group"); 279 } else { 280 CmdArgs.push_back("-lc_p"); 281 } 282 } 283 CmdArgs.push_back("-lgcc_p"); 284 } else { 285 if (Args.hasArg(options::OPT_static)) { 286 CmdArgs.push_back("--start-group"); 287 CmdArgs.push_back("-lc"); 288 CmdArgs.push_back("-lpthread"); 289 CmdArgs.push_back("--end-group"); 290 } else { 291 CmdArgs.push_back("-lc"); 292 } 293 CmdArgs.push_back("-lcompiler_rt"); 294 } 295 296 if (Args.hasArg(options::OPT_static)) { 297 CmdArgs.push_back("-lstdc++"); 298 } else if (Args.hasArg(options::OPT_pg)) { 299 CmdArgs.push_back("-lgcc_eh_p"); 300 } else { 301 CmdArgs.push_back("--as-needed"); 302 CmdArgs.push_back("-lstdc++"); 303 CmdArgs.push_back("--no-as-needed"); 304 } 305 } 306 307 if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles)) { 308 if (Args.hasArg(options::OPT_shared) || Args.hasArg(options::OPT_pie)) 309 CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtendS.o"))); 310 else 311 CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtend.o"))); 312 CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtn.o"))); 313 } 314 315 const char *Exec = 316 #ifdef _WIN32 317 Args.MakeArgString(ToolChain.GetProgramPath("orbis-ld.gold")); 318 #else 319 Args.MakeArgString(ToolChain.GetProgramPath("orbis-ld")); 320 #endif 321 322 C.addCommand(llvm::make_unique<Command>(JA, T, Exec, CmdArgs, Inputs)); 323 } 324 325 void tools::PS4cpu::Link::ConstructJob(Compilation &C, const JobAction &JA, 326 const InputInfo &Output, 327 const InputInfoList &Inputs, 328 const ArgList &Args, 329 const char *LinkingOutput) const { 330 const toolchains::FreeBSD &ToolChain = 331 static_cast<const toolchains::FreeBSD &>(getToolChain()); 332 const Driver &D = ToolChain.getDriver(); 333 bool PS4Linker; 334 StringRef LinkerOptName; 335 if (const Arg *A = Args.getLastArg(options::OPT_fuse_ld_EQ)) { 336 LinkerOptName = A->getValue(); 337 if (LinkerOptName != "ps4" && LinkerOptName != "gold") 338 D.Diag(diag::err_drv_unsupported_linker) << LinkerOptName; 339 } 340 341 if (LinkerOptName == "gold") 342 PS4Linker = false; 343 else if (LinkerOptName == "ps4") 344 PS4Linker = true; 345 else 346 PS4Linker = !Args.hasArg(options::OPT_shared); 347 348 if (PS4Linker) 349 ConstructPS4LinkJob(*this, C, JA, Output, Inputs, Args, LinkingOutput); 350 else 351 ConstructGoldLinkJob(*this, C, JA, Output, Inputs, Args, LinkingOutput); 352 } 353 354 toolchains::PS4CPU::PS4CPU(const Driver &D, const llvm::Triple &Triple, 355 const ArgList &Args) 356 : Generic_ELF(D, Triple, Args) { 357 if (Args.hasArg(clang::driver::options::OPT_static)) 358 D.Diag(clang::diag::err_drv_unsupported_opt_for_target) << "-static" 359 << "PS4"; 360 361 // Determine where to find the PS4 libraries. We use SCE_ORBIS_SDK_DIR 362 // if it exists; otherwise use the driver's installation path, which 363 // should be <SDK_DIR>/host_tools/bin. 364 365 SmallString<512> PS4SDKDir; 366 if (const char *EnvValue = getenv("SCE_ORBIS_SDK_DIR")) { 367 if (!llvm::sys::fs::exists(EnvValue)) 368 getDriver().Diag(clang::diag::warn_drv_ps4_sdk_dir) << EnvValue; 369 PS4SDKDir = EnvValue; 370 } else { 371 PS4SDKDir = getDriver().Dir; 372 llvm::sys::path::append(PS4SDKDir, "/../../"); 373 } 374 375 // By default, the driver won't report a warning if it can't find 376 // PS4's include or lib directories. This behavior could be changed if 377 // -Weverything or -Winvalid-or-nonexistent-directory options are passed. 378 // If -isysroot was passed, use that as the SDK base path. 379 std::string PrefixDir; 380 if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { 381 PrefixDir = A->getValue(); 382 if (!llvm::sys::fs::exists(PrefixDir)) 383 getDriver().Diag(clang::diag::warn_missing_sysroot) << PrefixDir; 384 } else 385 PrefixDir = PS4SDKDir.str(); 386 387 SmallString<512> PS4SDKIncludeDir(PrefixDir); 388 llvm::sys::path::append(PS4SDKIncludeDir, "target/include"); 389 if (!Args.hasArg(options::OPT_nostdinc) && 390 !Args.hasArg(options::OPT_nostdlibinc) && 391 !Args.hasArg(options::OPT_isysroot) && 392 !Args.hasArg(options::OPT__sysroot_EQ) && 393 !llvm::sys::fs::exists(PS4SDKIncludeDir)) { 394 getDriver().Diag(clang::diag::warn_drv_unable_to_find_directory_expected) 395 << "PS4 system headers" << PS4SDKIncludeDir; 396 } 397 398 SmallString<512> PS4SDKLibDir(PS4SDKDir); 399 llvm::sys::path::append(PS4SDKLibDir, "target/lib"); 400 if (!Args.hasArg(options::OPT_nostdlib) && 401 !Args.hasArg(options::OPT_nodefaultlibs) && 402 !Args.hasArg(options::OPT__sysroot_EQ) && !Args.hasArg(options::OPT_E) && 403 !Args.hasArg(options::OPT_c) && !Args.hasArg(options::OPT_S) && 404 !Args.hasArg(options::OPT_emit_ast) && 405 !llvm::sys::fs::exists(PS4SDKLibDir)) { 406 getDriver().Diag(clang::diag::warn_drv_unable_to_find_directory_expected) 407 << "PS4 system libraries" << PS4SDKLibDir; 408 return; 409 } 410 getFilePaths().push_back(PS4SDKLibDir.str()); 411 } 412 413 Tool *toolchains::PS4CPU::buildAssembler() const { 414 return new tools::PS4cpu::Assemble(*this); 415 } 416 417 Tool *toolchains::PS4CPU::buildLinker() const { 418 return new tools::PS4cpu::Link(*this); 419 } 420 421 bool toolchains::PS4CPU::isPICDefault() const { return true; } 422 423 bool toolchains::PS4CPU::HasNativeLLVMSupport() const { return true; } 424 425 SanitizerMask toolchains::PS4CPU::getSupportedSanitizers() const { 426 SanitizerMask Res = ToolChain::getSupportedSanitizers(); 427 Res |= SanitizerKind::Address; 428 Res |= SanitizerKind::PointerCompare; 429 Res |= SanitizerKind::PointerSubtract; 430 Res |= SanitizerKind::Vptr; 431 return Res; 432 } 433