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