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
22using namespace clang::driver;
23using namespace clang;
24using namespace llvm::opt;
25
26using clang::driver::tools::AddLinkerInputs;
27
28void 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
45void 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(std::make_unique<Command>(JA, *this, Exec, CmdArgs, Inputs));
66}
67
68static 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
78void 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
87static 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(std::make_unique<Command>(JA, T, Exec, CmdArgs, Inputs));
145}
146
147static 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(std::make_unique<Command>(JA, T, Exec, CmdArgs, Inputs));
323}
324
325void 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
354toolchains::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
413Tool *toolchains::PS4CPU::buildAssembler() const {
414  return new tools::PS4cpu::Assemble(*this);
415}
416
417Tool *toolchains::PS4CPU::buildLinker() const {
418  return new tools::PS4cpu::Link(*this);
419}
420
421bool toolchains::PS4CPU::isPICDefault() const { return true; }
422
423bool toolchains::PS4CPU::HasNativeLLVMSupport() const { return true; }
424
425SanitizerMask 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