1 //===--- ZOS.cpp - z/OS 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 "ZOS.h" 10 #include "CommonArgs.h" 11 #include "clang/Driver/Compilation.h" 12 #include "clang/Driver/Options.h" 13 #include "llvm/Option/ArgList.h" 14 #include "llvm/Support/FileSystem.h" 15 #include "llvm/Support/VirtualFileSystem.h" 16 #include "llvm/Support/WithColor.h" 17 18 using namespace clang; 19 using namespace clang::driver; 20 using namespace clang::driver::tools; 21 using namespace clang::driver::toolchains; 22 using namespace llvm; 23 using namespace llvm::opt; 24 using namespace llvm::sys; 25 26 ZOS::ZOS(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) 27 : ToolChain(D, Triple, Args) {} 28 29 ZOS::~ZOS() {} 30 31 void ZOS::addClangTargetOptions(const ArgList &DriverArgs, 32 ArgStringList &CC1Args, 33 Action::OffloadKind DeviceOffloadKind) const { 34 // Pass "-faligned-alloc-unavailable" only when the user hasn't manually 35 // enabled or disabled aligned allocations. 36 if (!DriverArgs.hasArgNoClaim(options::OPT_faligned_allocation, 37 options::OPT_fno_aligned_allocation)) 38 CC1Args.push_back("-faligned-alloc-unavailable"); 39 } 40 41 void zos::Assembler::ConstructJob(Compilation &C, const JobAction &JA, 42 const InputInfo &Output, 43 const InputInfoList &Inputs, 44 const ArgList &Args, 45 const char *LinkingOutput) const { 46 ArgStringList CmdArgs; 47 48 Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, options::OPT_Xassembler); 49 50 // Specify assembler output file. 51 assert((Output.isFilename() || Output.isNothing()) && "Invalid output."); 52 if (Output.isFilename()) { 53 CmdArgs.push_back("-o"); 54 CmdArgs.push_back(Output.getFilename()); 55 } 56 57 // Specify assembler input file. 58 // The system assembler on z/OS takes exactly one input file. The driver is 59 // expected to invoke as(1) separately for each assembler source input file. 60 if (Inputs.size() != 1) 61 llvm_unreachable("Invalid number of input files."); 62 const InputInfo &II = Inputs[0]; 63 assert((II.isFilename() || II.isNothing()) && "Invalid input."); 64 if (II.isFilename()) 65 CmdArgs.push_back(II.getFilename()); 66 67 const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("as")); 68 C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(), 69 Exec, CmdArgs, Inputs)); 70 } 71 72 static std::string getLEHLQ(const ArgList &Args) { 73 if (Args.hasArg(options::OPT_mzos_hlq_le_EQ)) { 74 Arg *LEHLQArg = Args.getLastArg(options::OPT_mzos_hlq_le_EQ); 75 StringRef HLQ = LEHLQArg->getValue(); 76 if (!HLQ.empty()) 77 return HLQ.str(); 78 } 79 return "CEE"; 80 } 81 82 static std::string getClangHLQ(const ArgList &Args) { 83 if (Args.hasArg(options::OPT_mzos_hlq_clang_EQ)) { 84 Arg *ClangHLQArg = Args.getLastArg(options::OPT_mzos_hlq_clang_EQ); 85 StringRef HLQ = ClangHLQArg->getValue(); 86 if (!HLQ.empty()) 87 return HLQ.str(); 88 } 89 return getLEHLQ(Args); 90 } 91 92 static std::string getCSSHLQ(const ArgList &Args) { 93 if (Args.hasArg(options::OPT_mzos_hlq_csslib_EQ)) { 94 Arg *CsslibHLQArg = Args.getLastArg(options::OPT_mzos_hlq_csslib_EQ); 95 StringRef HLQ = CsslibHLQArg->getValue(); 96 if (!HLQ.empty()) 97 return HLQ.str(); 98 } 99 return "SYS1"; 100 } 101 102 void zos::Linker::ConstructJob(Compilation &C, const JobAction &JA, 103 const InputInfo &Output, 104 const InputInfoList &Inputs, const ArgList &Args, 105 const char *LinkingOutput) const { 106 const ZOS &ToolChain = static_cast<const ZOS &>(getToolChain()); 107 ArgStringList CmdArgs; 108 109 const bool IsSharedLib = 110 Args.hasFlag(options::OPT_shared, options::OPT_static, false); 111 112 assert((Output.isFilename() || Output.isNothing()) && "Invalid output."); 113 if (Output.isFilename()) { 114 CmdArgs.push_back("-o"); 115 CmdArgs.push_back(Output.getFilename()); 116 } 117 118 SmallString<128> LinkerOptions; 119 LinkerOptions = "AMODE="; 120 LinkerOptions += "64"; 121 LinkerOptions += ",LIST"; 122 LinkerOptions += ",DYNAM=DLL"; 123 LinkerOptions += ",MSGLEVEL=4"; 124 LinkerOptions += ",CASE=MIXED"; 125 LinkerOptions += ",REUS=RENT"; 126 127 CmdArgs.push_back("-b"); 128 CmdArgs.push_back(Args.MakeArgString(LinkerOptions)); 129 130 if (!IsSharedLib) { 131 CmdArgs.push_back("-e"); 132 CmdArgs.push_back("CELQSTRT"); 133 134 CmdArgs.push_back("-O"); 135 CmdArgs.push_back("CELQSTRT"); 136 137 CmdArgs.push_back("-u"); 138 CmdArgs.push_back("CELQMAIN"); 139 } 140 141 // Generate side file if -shared option is present. 142 if (IsSharedLib) { 143 StringRef OutputName = Output.getFilename(); 144 // Strip away the last file suffix in presence from output name and add 145 // a new .x suffix. 146 size_t Suffix = OutputName.find_last_of("."); 147 const char *SideDeckName = 148 Args.MakeArgString(OutputName.substr(0, Suffix) + ".x"); 149 CmdArgs.push_back("-x"); 150 CmdArgs.push_back(SideDeckName); 151 } else { 152 // We need to direct side file to /dev/null to suppress linker warning when 153 // the object file contains exported symbols, and -shared or 154 // -Wl,-x<sidedeck>.x is not specified. 155 CmdArgs.push_back("-x"); 156 CmdArgs.push_back("/dev/null"); 157 } 158 159 Args.AddAllArgs(CmdArgs, options::OPT_u); 160 161 // Add archive library search paths. 162 Args.AddAllArgs(CmdArgs, options::OPT_L); 163 ToolChain.AddFilePathLibArgs(Args, CmdArgs); 164 165 // Specify linker input file(s) 166 AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA); 167 168 // z/OS tool chain depends on LE data sets and the CSSLIB data set. 169 // These data sets can have different high level qualifiers (HLQs) 170 // as each installation can define them differently. 171 172 std::string LEHLQ = getLEHLQ(Args); 173 std::string CsslibHLQ = getCSSHLQ(Args); 174 175 StringRef ld_env_var = StringRef(getenv("_LD_SYSLIB")).trim(); 176 if (ld_env_var.empty()) { 177 CmdArgs.push_back("-S"); 178 CmdArgs.push_back(Args.MakeArgString("//'" + LEHLQ + ".SCEEBND2'")); 179 CmdArgs.push_back("-S"); 180 CmdArgs.push_back(Args.MakeArgString("//'" + CsslibHLQ + ".CSSLIB'")); 181 } 182 183 if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) { 184 ld_env_var = StringRef(getenv("_LD_SIDE_DECKS")).trim(); 185 if (ld_env_var.empty()) { 186 CmdArgs.push_back( 187 Args.MakeArgString("//'" + LEHLQ + ".SCEELIB(CELQS001)'")); 188 CmdArgs.push_back( 189 Args.MakeArgString("//'" + LEHLQ + ".SCEELIB(CELQS003)'")); 190 } else { 191 char *ld_side_deck = strdup(ld_env_var.str().c_str()); 192 ld_side_deck = strtok(ld_side_deck, ":"); 193 while (ld_side_deck != nullptr) { 194 CmdArgs.push_back(ld_side_deck); 195 ld_side_deck = strtok(nullptr, ":"); 196 } 197 } 198 } 199 // Link libc++ library 200 if (ToolChain.ShouldLinkCXXStdlib(Args)) { 201 ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs); 202 } 203 204 // Specify compiler-rt library path for linker 205 if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) 206 AddRunTimeLibs(ToolChain, ToolChain.getDriver(), CmdArgs, Args); 207 208 const char *Exec = Args.MakeArgString(ToolChain.GetLinkerPath()); 209 C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(), 210 Exec, CmdArgs, Inputs)); 211 } 212 213 ToolChain::RuntimeLibType ZOS::GetDefaultRuntimeLibType() const { 214 return ToolChain::RLT_CompilerRT; 215 } 216 217 ToolChain::CXXStdlibType ZOS::GetDefaultCXXStdlibType() const { 218 return ToolChain::CST_Libcxx; 219 } 220 221 void ZOS::AddCXXStdlibLibArgs(const llvm::opt::ArgList &Args, 222 llvm::opt::ArgStringList &CmdArgs) const { 223 switch (GetCXXStdlibType(Args)) { 224 case ToolChain::CST_Libstdcxx: 225 llvm::report_fatal_error("linking libstdc++ is unimplemented on z/OS"); 226 break; 227 case ToolChain::CST_Libcxx: { 228 std::string ClangHLQ = getClangHLQ(Args); 229 CmdArgs.push_back( 230 Args.MakeArgString("//'" + ClangHLQ + ".SCEELIB(CRTDQCXE)'")); 231 CmdArgs.push_back( 232 Args.MakeArgString("//'" + ClangHLQ + ".SCEELIB(CRTDQCXS)'")); 233 CmdArgs.push_back( 234 Args.MakeArgString("//'" + ClangHLQ + ".SCEELIB(CRTDQCXP)'")); 235 CmdArgs.push_back( 236 Args.MakeArgString("//'" + ClangHLQ + ".SCEELIB(CRTDQCXA)'")); 237 CmdArgs.push_back( 238 Args.MakeArgString("//'" + ClangHLQ + ".SCEELIB(CRTDQXLA)'")); 239 CmdArgs.push_back( 240 Args.MakeArgString("//'" + ClangHLQ + ".SCEELIB(CRTDQUNW)'")); 241 } break; 242 } 243 } 244 245 auto ZOS::buildAssembler() const -> Tool * { return new zos::Assembler(*this); } 246 247 auto ZOS::buildLinker() const -> Tool * { return new zos::Linker(*this); } 248 249 void ZOS::AddClangSystemIncludeArgs(const ArgList &DriverArgs, 250 ArgStringList &CC1Args) const { 251 if (DriverArgs.hasArg(options::OPT_nostdinc)) 252 return; 253 254 const Driver &D = getDriver(); 255 256 // resolve ResourceDir 257 std::string ResourceDir(D.ResourceDir); 258 259 // zos_wrappers must take highest precedence 260 261 // - <clang>/lib/clang/<ver>/include/zos_wrappers 262 if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) { 263 SmallString<128> P(ResourceDir); 264 path::append(P, "include", "zos_wrappers"); 265 addSystemInclude(DriverArgs, CC1Args, P.str()); 266 267 // - <clang>/lib/clang/<ver>/include 268 SmallString<128> P2(ResourceDir); 269 path::append(P2, "include"); 270 addSystemInclude(DriverArgs, CC1Args, P2.str()); 271 } 272 273 // - /usr/include 274 if (Arg *SysIncludeArg = 275 DriverArgs.getLastArg(options::OPT_mzos_sys_include_EQ)) { 276 StringRef SysInclude = SysIncludeArg->getValue(); 277 278 // fall back to the default include path 279 if (!SysInclude.empty()) { 280 281 // -mzos-sys-include opton can have colon separated 282 // list of paths, so we need to parse the value. 283 StringRef PathLE(SysInclude); 284 size_t Colon = PathLE.find(':'); 285 if (Colon == StringRef::npos) { 286 addSystemInclude(DriverArgs, CC1Args, PathLE.str()); 287 return; 288 } 289 290 while (Colon != StringRef::npos) { 291 SmallString<128> P = PathLE.substr(0, Colon); 292 addSystemInclude(DriverArgs, CC1Args, P.str()); 293 PathLE = PathLE.substr(Colon + 1); 294 Colon = PathLE.find(':'); 295 } 296 if (PathLE.size()) 297 addSystemInclude(DriverArgs, CC1Args, PathLE.str()); 298 299 return; 300 } 301 } 302 303 addSystemInclude(DriverArgs, CC1Args, "/usr/include"); 304 } 305 306 void ZOS::TryAddIncludeFromPath(llvm::SmallString<128> Path, 307 const llvm::opt::ArgList &DriverArgs, 308 llvm::opt::ArgStringList &CC1Args) const { 309 if (!getVFS().exists(Path)) { 310 if (DriverArgs.hasArg(options::OPT_v)) 311 WithColor::warning(errs(), "Clang") 312 << "ignoring nonexistent directory \"" << Path << "\"\n"; 313 if (!DriverArgs.hasArg(options::OPT__HASH_HASH_HASH)) 314 return; 315 } 316 addSystemInclude(DriverArgs, CC1Args, Path); 317 } 318 319 void ZOS::AddClangCXXStdlibIncludeArgs( 320 const llvm::opt::ArgList &DriverArgs, 321 llvm::opt::ArgStringList &CC1Args) const { 322 if (DriverArgs.hasArg(options::OPT_nostdinc) || 323 DriverArgs.hasArg(options::OPT_nostdincxx) || 324 DriverArgs.hasArg(options::OPT_nostdlibinc)) 325 return; 326 327 switch (GetCXXStdlibType(DriverArgs)) { 328 case ToolChain::CST_Libcxx: { 329 // <install>/bin/../include/c++/v1 330 llvm::SmallString<128> InstallBin = 331 llvm::StringRef(getDriver().getInstalledDir()); 332 llvm::sys::path::append(InstallBin, "..", "include", "c++", "v1"); 333 TryAddIncludeFromPath(InstallBin, DriverArgs, CC1Args); 334 break; 335 } 336 case ToolChain::CST_Libstdcxx: 337 llvm::report_fatal_error( 338 "picking up libstdc++ headers is unimplemented on z/OS"); 339 break; 340 } 341 } 342