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