//===--- Darwin.cpp - Darwin Tool and ToolChain Implementations -*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "Darwin.h" #include "Arch/AArch64.h" #include "Arch/ARM.h" #include "CommonArgs.h" #include "clang/Basic/AlignedAllocation.h" #include "clang/Basic/ObjCRuntime.h" #include "clang/Config/config.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Options.h" #include "clang/Driver/SanitizerArgs.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Option/ArgList.h" #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/Threading.h" #include "llvm/Support/VirtualFileSystem.h" #include "llvm/TargetParser/TargetParser.h" #include "llvm/TargetParser/Triple.h" #include // ::getenv using namespace clang::driver; using namespace clang::driver::tools; using namespace clang::driver::toolchains; using namespace clang; using namespace llvm::opt; static VersionTuple minimumMacCatalystDeploymentTarget() { return VersionTuple(13, 1); } llvm::Triple::ArchType darwin::getArchTypeForMachOArchName(StringRef Str) { // See arch(3) and llvm-gcc's driver-driver.c. We don't implement support for // archs which Darwin doesn't use. // The matching this routine does is fairly pointless, since it is neither the // complete architecture list, nor a reasonable subset. The problem is that // historically the driver accepts this and also ties its -march= // handling to the architecture name, so we need to be careful before removing // support for it. // This code must be kept in sync with Clang's Darwin specific argument // translation. return llvm::StringSwitch(Str) .Cases("i386", "i486", "i486SX", "i586", "i686", llvm::Triple::x86) .Cases("pentium", "pentpro", "pentIIm3", "pentIIm5", "pentium4", llvm::Triple::x86) .Cases("x86_64", "x86_64h", llvm::Triple::x86_64) // This is derived from the driver. .Cases("arm", "armv4t", "armv5", "armv6", "armv6m", llvm::Triple::arm) .Cases("armv7", "armv7em", "armv7k", "armv7m", llvm::Triple::arm) .Cases("armv7s", "xscale", llvm::Triple::arm) .Cases("arm64", "arm64e", llvm::Triple::aarch64) .Case("arm64_32", llvm::Triple::aarch64_32) .Case("r600", llvm::Triple::r600) .Case("amdgcn", llvm::Triple::amdgcn) .Case("nvptx", llvm::Triple::nvptx) .Case("nvptx64", llvm::Triple::nvptx64) .Case("amdil", llvm::Triple::amdil) .Case("spir", llvm::Triple::spir) .Default(llvm::Triple::UnknownArch); } void darwin::setTripleTypeForMachOArchName(llvm::Triple &T, StringRef Str, const ArgList &Args) { const llvm::Triple::ArchType Arch = getArchTypeForMachOArchName(Str); llvm::ARM::ArchKind ArchKind = llvm::ARM::parseArch(Str); T.setArch(Arch); if (Arch != llvm::Triple::UnknownArch) T.setArchName(Str); if (ArchKind == llvm::ARM::ArchKind::ARMV6M || ArchKind == llvm::ARM::ArchKind::ARMV7M || ArchKind == llvm::ARM::ArchKind::ARMV7EM) { // Don't reject these -version-min= if we have the appropriate triple. if (T.getOS() == llvm::Triple::IOS) for (Arg *A : Args.filtered(options::OPT_mios_version_min_EQ)) A->ignoreTargetSpecific(); if (T.getOS() == llvm::Triple::WatchOS) for (Arg *A : Args.filtered(options::OPT_mwatchos_version_min_EQ)) A->ignoreTargetSpecific(); if (T.getOS() == llvm::Triple::TvOS) for (Arg *A : Args.filtered(options::OPT_mtvos_version_min_EQ)) A->ignoreTargetSpecific(); T.setOS(llvm::Triple::UnknownOS); T.setObjectFormat(llvm::Triple::MachO); } } void darwin::Assembler::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { const llvm::Triple &T(getToolChain().getTriple()); ArgStringList CmdArgs; assert(Inputs.size() == 1 && "Unexpected number of inputs."); const InputInfo &Input = Inputs[0]; // Determine the original source input. const Action *SourceAction = &JA; while (SourceAction->getKind() != Action::InputClass) { assert(!SourceAction->getInputs().empty() && "unexpected root action!"); SourceAction = SourceAction->getInputs()[0]; } // If -fno-integrated-as is used add -Q to the darwin assembler driver to make // sure it runs its system assembler not clang's integrated assembler. // Applicable to darwin11+ and Xcode 4+. darwin<10 lacked integrated-as. // FIXME: at run-time detect assembler capabilities or rely on version // information forwarded by -target-assembler-version. if (Args.hasArg(options::OPT_fno_integrated_as)) { if (!(T.isMacOSX() && T.isMacOSXVersionLT(10, 7))) CmdArgs.push_back("-Q"); } // Forward -g, assuming we are dealing with an actual assembly file. if (SourceAction->getType() == types::TY_Asm || SourceAction->getType() == types::TY_PP_Asm) { if (Args.hasArg(options::OPT_gstabs)) CmdArgs.push_back("--gstabs"); else if (Args.hasArg(options::OPT_g_Group)) CmdArgs.push_back("-g"); } // Derived from asm spec. AddMachOArch(Args, CmdArgs); // Use -force_cpusubtype_ALL on x86 by default. if (T.isX86() || Args.hasArg(options::OPT_force__cpusubtype__ALL)) CmdArgs.push_back("-force_cpusubtype_ALL"); if (getToolChain().getArch() != llvm::Triple::x86_64 && (((Args.hasArg(options::OPT_mkernel) || Args.hasArg(options::OPT_fapple_kext)) && getMachOToolChain().isKernelStatic()) || Args.hasArg(options::OPT_static))) CmdArgs.push_back("-static"); Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, options::OPT_Xassembler); assert(Output.isFilename() && "Unexpected lipo output."); CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); assert(Input.isFilename() && "Invalid input."); CmdArgs.push_back(Input.getFilename()); // asm_final spec is empty. const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("as")); C.addCommand(std::make_unique(JA, *this, ResponseFileSupport::None(), Exec, CmdArgs, Inputs, Output)); } void darwin::MachOTool::anchor() {} void darwin::MachOTool::AddMachOArch(const ArgList &Args, ArgStringList &CmdArgs) const { StringRef ArchName = getMachOToolChain().getMachOArchName(Args); // Derived from darwin_arch spec. CmdArgs.push_back("-arch"); CmdArgs.push_back(Args.MakeArgString(ArchName)); // FIXME: Is this needed anymore? if (ArchName == "arm") CmdArgs.push_back("-force_cpusubtype_ALL"); } bool darwin::Linker::NeedsTempPath(const InputInfoList &Inputs) const { // We only need to generate a temp path for LTO if we aren't compiling object // files. When compiling source files, we run 'dsymutil' after linking. We // don't run 'dsymutil' when compiling object files. for (const auto &Input : Inputs) if (Input.getType() != types::TY_Object) return true; return false; } /// Pass -no_deduplicate to ld64 under certain conditions: /// /// - Either -O0 or -O1 is explicitly specified /// - No -O option is specified *and* this is a compile+link (implicit -O0) /// /// Also do *not* add -no_deduplicate when no -O option is specified and this /// is just a link (we can't imply -O0) static bool shouldLinkerNotDedup(bool IsLinkerOnlyAction, const ArgList &Args) { if (Arg *A = Args.getLastArg(options::OPT_O_Group)) { if (A->getOption().matches(options::OPT_O0)) return true; if (A->getOption().matches(options::OPT_O)) return llvm::StringSwitch(A->getValue()) .Case("1", true) .Default(false); return false; // OPT_Ofast & OPT_O4 } if (!IsLinkerOnlyAction) // Implicit -O0 for compile+linker only. return true; return false; } void darwin::Linker::AddLinkArgs(Compilation &C, const ArgList &Args, ArgStringList &CmdArgs, const InputInfoList &Inputs, VersionTuple Version, bool LinkerIsLLD, bool UsePlatformVersion) const { const Driver &D = getToolChain().getDriver(); const toolchains::MachO &MachOTC = getMachOToolChain(); // Newer linkers support -demangle. Pass it if supported and not disabled by // the user. if ((Version >= VersionTuple(100) || LinkerIsLLD) && !Args.hasArg(options::OPT_Z_Xlinker__no_demangle)) CmdArgs.push_back("-demangle"); if (Args.hasArg(options::OPT_rdynamic) && (Version >= VersionTuple(137) || LinkerIsLLD)) CmdArgs.push_back("-export_dynamic"); // If we are using App Extension restrictions, pass a flag to the linker // telling it that the compiled code has been audited. if (Args.hasFlag(options::OPT_fapplication_extension, options::OPT_fno_application_extension, false)) CmdArgs.push_back("-application_extension"); if (D.isUsingLTO() && (Version >= VersionTuple(116) || LinkerIsLLD) && NeedsTempPath(Inputs)) { std::string TmpPathName; if (D.getLTOMode() == LTOK_Full) { // If we are using full LTO, then automatically create a temporary file // path for the linker to use, so that it's lifetime will extend past a // possible dsymutil step. TmpPathName = D.GetTemporaryPath("cc", types::getTypeTempSuffix(types::TY_Object)); } else if (D.getLTOMode() == LTOK_Thin) // If we are using thin LTO, then create a directory instead. TmpPathName = D.GetTemporaryDirectory("thinlto"); if (!TmpPathName.empty()) { auto *TmpPath = C.getArgs().MakeArgString(TmpPathName); C.addTempFile(TmpPath); CmdArgs.push_back("-object_path_lto"); CmdArgs.push_back(TmpPath); } } // Use -lto_library option to specify the libLTO.dylib path. Try to find // it in clang installed libraries. ld64 will only look at this argument // when it actually uses LTO, so libLTO.dylib only needs to exist at link // time if ld64 decides that it needs to use LTO. // Since this is passed unconditionally, ld64 will never look for libLTO.dylib // next to it. That's ok since ld64 using a libLTO.dylib not matching the // clang version won't work anyways. // lld is built at the same revision as clang and statically links in // LLVM libraries, so it doesn't need libLTO.dylib. if (Version >= VersionTuple(133) && !LinkerIsLLD) { // Search for libLTO in /../lib/libLTO.dylib StringRef P = llvm::sys::path::parent_path(D.Dir); SmallString<128> LibLTOPath(P); llvm::sys::path::append(LibLTOPath, "lib"); llvm::sys::path::append(LibLTOPath, "libLTO.dylib"); CmdArgs.push_back("-lto_library"); CmdArgs.push_back(C.getArgs().MakeArgString(LibLTOPath)); } // ld64 version 262 and above runs the deduplicate pass by default. // FIXME: lld doesn't dedup by default. Should we pass `--icf=safe` // if `!shouldLinkerNotDedup()` if LinkerIsLLD here? if (Version >= VersionTuple(262) && shouldLinkerNotDedup(C.getJobs().empty(), Args)) CmdArgs.push_back("-no_deduplicate"); // Derived from the "link" spec. Args.AddAllArgs(CmdArgs, options::OPT_static); if (!Args.hasArg(options::OPT_static)) CmdArgs.push_back("-dynamic"); if (Args.hasArg(options::OPT_fgnu_runtime)) { // FIXME: gcc replaces -lobjc in forward args with -lobjc-gnu // here. How do we wish to handle such things? } if (!Args.hasArg(options::OPT_dynamiclib)) { AddMachOArch(Args, CmdArgs); // FIXME: Why do this only on this path? Args.AddLastArg(CmdArgs, options::OPT_force__cpusubtype__ALL); Args.AddLastArg(CmdArgs, options::OPT_bundle); Args.AddAllArgs(CmdArgs, options::OPT_bundle__loader); Args.AddAllArgs(CmdArgs, options::OPT_client__name); Arg *A; if ((A = Args.getLastArg(options::OPT_compatibility__version)) || (A = Args.getLastArg(options::OPT_current__version)) || (A = Args.getLastArg(options::OPT_install__name))) D.Diag(diag::err_drv_argument_only_allowed_with) << A->getAsString(Args) << "-dynamiclib"; Args.AddLastArg(CmdArgs, options::OPT_force__flat__namespace); Args.AddLastArg(CmdArgs, options::OPT_keep__private__externs); Args.AddLastArg(CmdArgs, options::OPT_private__bundle); } else { CmdArgs.push_back("-dylib"); Arg *A; if ((A = Args.getLastArg(options::OPT_bundle)) || (A = Args.getLastArg(options::OPT_bundle__loader)) || (A = Args.getLastArg(options::OPT_client__name)) || (A = Args.getLastArg(options::OPT_force__flat__namespace)) || (A = Args.getLastArg(options::OPT_keep__private__externs)) || (A = Args.getLastArg(options::OPT_private__bundle))) D.Diag(diag::err_drv_argument_not_allowed_with) << A->getAsString(Args) << "-dynamiclib"; Args.AddAllArgsTranslated(CmdArgs, options::OPT_compatibility__version, "-dylib_compatibility_version"); Args.AddAllArgsTranslated(CmdArgs, options::OPT_current__version, "-dylib_current_version"); AddMachOArch(Args, CmdArgs); Args.AddAllArgsTranslated(CmdArgs, options::OPT_install__name, "-dylib_install_name"); } Args.AddLastArg(CmdArgs, options::OPT_all__load); Args.AddAllArgs(CmdArgs, options::OPT_allowable__client); Args.AddLastArg(CmdArgs, options::OPT_bind__at__load); if (MachOTC.isTargetIOSBased()) Args.AddLastArg(CmdArgs, options::OPT_arch__errors__fatal); Args.AddLastArg(CmdArgs, options::OPT_dead__strip); Args.AddLastArg(CmdArgs, options::OPT_no__dead__strip__inits__and__terms); Args.AddAllArgs(CmdArgs, options::OPT_dylib__file); Args.AddLastArg(CmdArgs, options::OPT_dynamic); Args.AddAllArgs(CmdArgs, options::OPT_exported__symbols__list); Args.AddLastArg(CmdArgs, options::OPT_flat__namespace); Args.AddAllArgs(CmdArgs, options::OPT_force__load); Args.AddAllArgs(CmdArgs, options::OPT_headerpad__max__install__names); Args.AddAllArgs(CmdArgs, options::OPT_image__base); Args.AddAllArgs(CmdArgs, options::OPT_init); // Add the deployment target. if (Version >= VersionTuple(520) || LinkerIsLLD || UsePlatformVersion) MachOTC.addPlatformVersionArgs(Args, CmdArgs); else MachOTC.addMinVersionArgs(Args, CmdArgs); Args.AddLastArg(CmdArgs, options::OPT_nomultidefs); Args.AddLastArg(CmdArgs, options::OPT_multi__module); Args.AddLastArg(CmdArgs, options::OPT_single__module); Args.AddAllArgs(CmdArgs, options::OPT_multiply__defined); Args.AddAllArgs(CmdArgs, options::OPT_multiply__defined__unused); if (const Arg *A = Args.getLastArg(options::OPT_fpie, options::OPT_fPIE, options::OPT_fno_pie, options::OPT_fno_PIE)) { if (A->getOption().matches(options::OPT_fpie) || A->getOption().matches(options::OPT_fPIE)) CmdArgs.push_back("-pie"); else CmdArgs.push_back("-no_pie"); } // for embed-bitcode, use -bitcode_bundle in linker command if (C.getDriver().embedBitcodeEnabled()) { // Check if the toolchain supports bitcode build flow. if (MachOTC.SupportsEmbeddedBitcode()) { CmdArgs.push_back("-bitcode_bundle"); // FIXME: Pass this if LinkerIsLLD too, once it implements this flag. if (C.getDriver().embedBitcodeMarkerOnly() && Version >= VersionTuple(278)) { CmdArgs.push_back("-bitcode_process_mode"); CmdArgs.push_back("marker"); } } else D.Diag(diag::err_drv_bitcode_unsupported_on_toolchain); } // If GlobalISel is enabled, pass it through to LLVM. if (Arg *A = Args.getLastArg(options::OPT_fglobal_isel, options::OPT_fno_global_isel)) { if (A->getOption().matches(options::OPT_fglobal_isel)) { CmdArgs.push_back("-mllvm"); CmdArgs.push_back("-global-isel"); // Disable abort and fall back to SDAG silently. CmdArgs.push_back("-mllvm"); CmdArgs.push_back("-global-isel-abort=0"); } } if (Args.hasArg(options::OPT_mkernel) || Args.hasArg(options::OPT_fapple_kext) || Args.hasArg(options::OPT_ffreestanding)) { CmdArgs.push_back("-mllvm"); CmdArgs.push_back("-disable-atexit-based-global-dtor-lowering"); } Args.AddLastArg(CmdArgs, options::OPT_prebind); Args.AddLastArg(CmdArgs, options::OPT_noprebind); Args.AddLastArg(CmdArgs, options::OPT_nofixprebinding); Args.AddLastArg(CmdArgs, options::OPT_prebind__all__twolevel__modules); Args.AddLastArg(CmdArgs, options::OPT_read__only__relocs); Args.AddAllArgs(CmdArgs, options::OPT_sectcreate); Args.AddAllArgs(CmdArgs, options::OPT_sectorder); Args.AddAllArgs(CmdArgs, options::OPT_seg1addr); Args.AddAllArgs(CmdArgs, options::OPT_segprot); Args.AddAllArgs(CmdArgs, options::OPT_segaddr); Args.AddAllArgs(CmdArgs, options::OPT_segs__read__only__addr); Args.AddAllArgs(CmdArgs, options::OPT_segs__read__write__addr); Args.AddAllArgs(CmdArgs, options::OPT_seg__addr__table); Args.AddAllArgs(CmdArgs, options::OPT_seg__addr__table__filename); Args.AddAllArgs(CmdArgs, options::OPT_sub__library); Args.AddAllArgs(CmdArgs, options::OPT_sub__umbrella); // Give --sysroot= preference, over the Apple specific behavior to also use // --isysroot as the syslibroot. StringRef sysroot = C.getSysRoot(); if (sysroot != "") { CmdArgs.push_back("-syslibroot"); CmdArgs.push_back(C.getArgs().MakeArgString(sysroot)); } else if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { CmdArgs.push_back("-syslibroot"); CmdArgs.push_back(A->getValue()); } Args.AddLastArg(CmdArgs, options::OPT_twolevel__namespace); Args.AddLastArg(CmdArgs, options::OPT_twolevel__namespace__hints); Args.AddAllArgs(CmdArgs, options::OPT_umbrella); Args.AddAllArgs(CmdArgs, options::OPT_undefined); Args.AddAllArgs(CmdArgs, options::OPT_unexported__symbols__list); Args.AddAllArgs(CmdArgs, options::OPT_weak__reference__mismatches); Args.AddLastArg(CmdArgs, options::OPT_X_Flag); Args.AddAllArgs(CmdArgs, options::OPT_y); Args.AddLastArg(CmdArgs, options::OPT_w); Args.AddAllArgs(CmdArgs, options::OPT_pagezero__size); Args.AddAllArgs(CmdArgs, options::OPT_segs__read__); Args.AddLastArg(CmdArgs, options::OPT_seglinkedit); Args.AddLastArg(CmdArgs, options::OPT_noseglinkedit); Args.AddAllArgs(CmdArgs, options::OPT_sectalign); Args.AddAllArgs(CmdArgs, options::OPT_sectobjectsymbols); Args.AddAllArgs(CmdArgs, options::OPT_segcreate); Args.AddLastArg(CmdArgs, options::OPT_why_load); Args.AddLastArg(CmdArgs, options::OPT_whatsloaded); Args.AddAllArgs(CmdArgs, options::OPT_dylinker__install__name); Args.AddLastArg(CmdArgs, options::OPT_dylinker); Args.AddLastArg(CmdArgs, options::OPT_Mach); if (LinkerIsLLD) { if (auto *CSPGOGenerateArg = getLastCSProfileGenerateArg(Args)) { SmallString<128> Path(CSPGOGenerateArg->getNumValues() == 0 ? "" : CSPGOGenerateArg->getValue()); llvm::sys::path::append(Path, "default_%m.profraw"); CmdArgs.push_back("--cs-profile-generate"); CmdArgs.push_back(Args.MakeArgString(Twine("--cs-profile-path=") + Path)); } else if (auto *ProfileUseArg = getLastProfileUseArg(Args)) { SmallString<128> Path( ProfileUseArg->getNumValues() == 0 ? "" : ProfileUseArg->getValue()); if (Path.empty() || llvm::sys::fs::is_directory(Path)) llvm::sys::path::append(Path, "default.profdata"); CmdArgs.push_back(Args.MakeArgString(Twine("--cs-profile-path=") + Path)); } } } /// Determine whether we are linking the ObjC runtime. static bool isObjCRuntimeLinked(const ArgList &Args) { if (isObjCAutoRefCount(Args)) { Args.ClaimAllArgs(options::OPT_fobjc_link_runtime); return true; } return Args.hasArg(options::OPT_fobjc_link_runtime); } static bool checkRemarksOptions(const Driver &D, const ArgList &Args, const llvm::Triple &Triple) { // When enabling remarks, we need to error if: // * The remark file is specified but we're targeting multiple architectures, // which means more than one remark file is being generated. bool hasMultipleInvocations = Args.getAllArgValues(options::OPT_arch).size() > 1; bool hasExplicitOutputFile = Args.getLastArg(options::OPT_foptimization_record_file_EQ); if (hasMultipleInvocations && hasExplicitOutputFile) { D.Diag(diag::err_drv_invalid_output_with_multiple_archs) << "-foptimization-record-file"; return false; } return true; } static void renderRemarksOptions(const ArgList &Args, ArgStringList &CmdArgs, const llvm::Triple &Triple, const InputInfo &Output, const JobAction &JA) { StringRef Format = "yaml"; if (const Arg *A = Args.getLastArg(options::OPT_fsave_optimization_record_EQ)) Format = A->getValue(); CmdArgs.push_back("-mllvm"); CmdArgs.push_back("-lto-pass-remarks-output"); CmdArgs.push_back("-mllvm"); const Arg *A = Args.getLastArg(options::OPT_foptimization_record_file_EQ); if (A) { CmdArgs.push_back(A->getValue()); } else { assert(Output.isFilename() && "Unexpected ld output."); SmallString<128> F; F = Output.getFilename(); F += ".opt."; F += Format; CmdArgs.push_back(Args.MakeArgString(F)); } if (const Arg *A = Args.getLastArg(options::OPT_foptimization_record_passes_EQ)) { CmdArgs.push_back("-mllvm"); std::string Passes = std::string("-lto-pass-remarks-filter=") + A->getValue(); CmdArgs.push_back(Args.MakeArgString(Passes)); } if (!Format.empty()) { CmdArgs.push_back("-mllvm"); Twine FormatArg = Twine("-lto-pass-remarks-format=") + Format; CmdArgs.push_back(Args.MakeArgString(FormatArg)); } if (getLastProfileUseArg(Args)) { CmdArgs.push_back("-mllvm"); CmdArgs.push_back("-lto-pass-remarks-with-hotness"); if (const Arg *A = Args.getLastArg(options::OPT_fdiagnostics_hotness_threshold_EQ)) { CmdArgs.push_back("-mllvm"); std::string Opt = std::string("-lto-pass-remarks-hotness-threshold=") + A->getValue(); CmdArgs.push_back(Args.MakeArgString(Opt)); } } } static void AppendPlatformPrefix(SmallString<128> &Path, const llvm::Triple &T); void darwin::Linker::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { assert(Output.getType() == types::TY_Image && "Invalid linker output type."); // If the number of arguments surpasses the system limits, we will encode the // input files in a separate file, shortening the command line. To this end, // build a list of input file names that can be passed via a file with the // -filelist linker option. llvm::opt::ArgStringList InputFileList; // The logic here is derived from gcc's behavior; most of which // comes from specs (starting with link_command). Consult gcc for // more information. ArgStringList CmdArgs; /// Hack(tm) to ignore linking errors when we are doing ARC migration. if (Args.hasArg(options::OPT_ccc_arcmt_check, options::OPT_ccc_arcmt_migrate)) { for (const auto &Arg : Args) Arg->claim(); const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("touch")); CmdArgs.push_back(Output.getFilename()); C.addCommand(std::make_unique(JA, *this, ResponseFileSupport::None(), Exec, CmdArgs, std::nullopt, Output)); return; } VersionTuple Version = getMachOToolChain().getLinkerVersion(Args); bool LinkerIsLLD; const char *Exec = Args.MakeArgString(getToolChain().GetLinkerPath(&LinkerIsLLD)); // xrOS always uses -platform-version. bool UsePlatformVersion = getToolChain().getTriple().isXROS(); // I'm not sure why this particular decomposition exists in gcc, but // we follow suite for ease of comparison. AddLinkArgs(C, Args, CmdArgs, Inputs, Version, LinkerIsLLD, UsePlatformVersion); if (willEmitRemarks(Args) && checkRemarksOptions(getToolChain().getDriver(), Args, getToolChain().getTriple())) renderRemarksOptions(Args, CmdArgs, getToolChain().getTriple(), Output, JA); // Propagate the -moutline flag to the linker in LTO. if (Arg *A = Args.getLastArg(options::OPT_moutline, options::OPT_mno_outline)) { if (A->getOption().matches(options::OPT_moutline)) { if (getMachOToolChain().getMachOArchName(Args) == "arm64") { CmdArgs.push_back("-mllvm"); CmdArgs.push_back("-enable-machine-outliner"); } } else { // Disable all outlining behaviour if we have mno-outline. We need to do // this explicitly, because targets which support default outlining will // try to do work if we don't. CmdArgs.push_back("-mllvm"); CmdArgs.push_back("-enable-machine-outliner=never"); } } // Outline from linkonceodr functions by default in LTO, whenever the outliner // is enabled. Note that the target may enable the machine outliner // independently of -moutline. CmdArgs.push_back("-mllvm"); CmdArgs.push_back("-enable-linkonceodr-outlining"); // Setup statistics file output. SmallString<128> StatsFile = getStatsFileName(Args, Output, Inputs[0], getToolChain().getDriver()); if (!StatsFile.empty()) { CmdArgs.push_back("-mllvm"); CmdArgs.push_back(Args.MakeArgString("-lto-stats-file=" + StatsFile.str())); } // It seems that the 'e' option is completely ignored for dynamic executables // (the default), and with static executables, the last one wins, as expected. Args.addAllArgs(CmdArgs, {options::OPT_d_Flag, options::OPT_s, options::OPT_t, options::OPT_Z_Flag, options::OPT_u_Group, options::OPT_r}); // Forward -ObjC when either -ObjC or -ObjC++ is used, to force loading // members of static archive libraries which implement Objective-C classes or // categories. if (Args.hasArg(options::OPT_ObjC) || Args.hasArg(options::OPT_ObjCXX)) CmdArgs.push_back("-ObjC"); CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles)) getMachOToolChain().addStartObjectFileArgs(Args, CmdArgs); Args.AddAllArgs(CmdArgs, options::OPT_L); AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs, JA); // Build the input file for -filelist (list of linker input files) in case we // need it later for (const auto &II : Inputs) { if (!II.isFilename()) { // This is a linker input argument. // We cannot mix input arguments and file names in a -filelist input, thus // we prematurely stop our list (remaining files shall be passed as // arguments). if (InputFileList.size() > 0) break; continue; } InputFileList.push_back(II.getFilename()); } // Additional linker set-up and flags for Fortran. This is required in order // to generate executables. if (getToolChain().getDriver().IsFlangMode()) { addFortranRuntimeLibraryPath(getToolChain(), Args, CmdArgs); addFortranRuntimeLibs(getToolChain(), Args, CmdArgs); } if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) addOpenMPRuntime(CmdArgs, getToolChain(), Args); if (isObjCRuntimeLinked(Args) && !Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) { // We use arclite library for both ARC and subscripting support. getMachOToolChain().AddLinkARCArgs(Args, CmdArgs); CmdArgs.push_back("-framework"); CmdArgs.push_back("Foundation"); // Link libobj. CmdArgs.push_back("-lobjc"); } if (LinkingOutput) { CmdArgs.push_back("-arch_multiple"); CmdArgs.push_back("-final_output"); CmdArgs.push_back(LinkingOutput); } if (Args.hasArg(options::OPT_fnested_functions)) CmdArgs.push_back("-allow_stack_execute"); getMachOToolChain().addProfileRTLibs(Args, CmdArgs); StringRef Parallelism = getLTOParallelism(Args, getToolChain().getDriver()); if (!Parallelism.empty()) { CmdArgs.push_back("-mllvm"); unsigned NumThreads = llvm::get_threadpool_strategy(Parallelism)->compute_thread_count(); CmdArgs.push_back(Args.MakeArgString("-threads=" + Twine(NumThreads))); } if (getToolChain().ShouldLinkCXXStdlib(Args)) getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs); bool NoStdOrDefaultLibs = Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs); bool ForceLinkBuiltins = Args.hasArg(options::OPT_fapple_link_rtlib); if (!NoStdOrDefaultLibs || ForceLinkBuiltins) { // link_ssp spec is empty. // If we have both -nostdlib/nodefaultlibs and -fapple-link-rtlib then // we just want to link the builtins, not the other libs like libSystem. if (NoStdOrDefaultLibs && ForceLinkBuiltins) { getMachOToolChain().AddLinkRuntimeLib(Args, CmdArgs, "builtins"); } else { // Let the tool chain choose which runtime library to link. getMachOToolChain().AddLinkRuntimeLibArgs(Args, CmdArgs, ForceLinkBuiltins); // No need to do anything for pthreads. Claim argument to avoid warning. Args.ClaimAllArgs(options::OPT_pthread); Args.ClaimAllArgs(options::OPT_pthreads); } } if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles)) { // endfile_spec is empty. } Args.AddAllArgs(CmdArgs, options::OPT_T_Group); Args.AddAllArgs(CmdArgs, options::OPT_F); // -iframework should be forwarded as -F. for (const Arg *A : Args.filtered(options::OPT_iframework)) CmdArgs.push_back(Args.MakeArgString(std::string("-F") + A->getValue())); if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) { if (Arg *A = Args.getLastArg(options::OPT_fveclib)) { if (A->getValue() == StringRef("Accelerate")) { CmdArgs.push_back("-framework"); CmdArgs.push_back("Accelerate"); } } } // Add non-standard, platform-specific search paths, e.g., for DriverKit: // -L/System/DriverKit/usr/lib // -F/System/DriverKit/System/Library/Framework { bool NonStandardSearchPath = false; const auto &Triple = getToolChain().getTriple(); if (Triple.isDriverKit()) { // ld64 fixed the implicit -F and -L paths in ld64-605.1+. NonStandardSearchPath = Version.getMajor() < 605 || (Version.getMajor() == 605 && Version.getMinor().value_or(0) < 1); } if (NonStandardSearchPath) { if (auto *Sysroot = Args.getLastArg(options::OPT_isysroot)) { auto AddSearchPath = [&](StringRef Flag, StringRef SearchPath) { SmallString<128> P(Sysroot->getValue()); AppendPlatformPrefix(P, Triple); llvm::sys::path::append(P, SearchPath); if (getToolChain().getVFS().exists(P)) { CmdArgs.push_back(Args.MakeArgString(Flag + P)); } }; AddSearchPath("-L", "/usr/lib"); AddSearchPath("-F", "/System/Library/Frameworks"); } } } ResponseFileSupport ResponseSupport; if (Version >= VersionTuple(705) || LinkerIsLLD) { ResponseSupport = ResponseFileSupport::AtFileUTF8(); } else { // For older versions of the linker, use the legacy filelist method instead. ResponseSupport = {ResponseFileSupport::RF_FileList, llvm::sys::WEM_UTF8, "-filelist"}; } std::unique_ptr Cmd = std::make_unique( JA, *this, ResponseSupport, Exec, CmdArgs, Inputs, Output); Cmd->setInputFileList(std::move(InputFileList)); C.addCommand(std::move(Cmd)); } void darwin::StaticLibTool::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { const Driver &D = getToolChain().getDriver(); // Silence warning for "clang -g foo.o -o foo" Args.ClaimAllArgs(options::OPT_g_Group); // and "clang -emit-llvm foo.o -o foo" Args.ClaimAllArgs(options::OPT_emit_llvm); // and for "clang -w foo.o -o foo". Other warning options are already // handled somewhere else. Args.ClaimAllArgs(options::OPT_w); // Silence warnings when linking C code with a C++ '-stdlib' argument. Args.ClaimAllArgs(options::OPT_stdlib_EQ); // libtool ArgStringList CmdArgs; // Create and insert file members with a deterministic index. CmdArgs.push_back("-static"); CmdArgs.push_back("-D"); CmdArgs.push_back("-no_warning_for_no_symbols"); CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); for (const auto &II : Inputs) { if (II.isFilename()) { CmdArgs.push_back(II.getFilename()); } } // Delete old output archive file if it already exists before generating a new // archive file. const auto *OutputFileName = Output.getFilename(); if (Output.isFilename() && llvm::sys::fs::exists(OutputFileName)) { if (std::error_code EC = llvm::sys::fs::remove(OutputFileName)) { D.Diag(diag::err_drv_unable_to_remove_file) << EC.message(); return; } } const char *Exec = Args.MakeArgString(getToolChain().GetStaticLibToolPath()); C.addCommand(std::make_unique(JA, *this, ResponseFileSupport::AtFileUTF8(), Exec, CmdArgs, Inputs, Output)); } void darwin::Lipo::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { ArgStringList CmdArgs; CmdArgs.push_back("-create"); assert(Output.isFilename() && "Unexpected lipo output."); CmdArgs.push_back("-output"); CmdArgs.push_back(Output.getFilename()); for (const auto &II : Inputs) { assert(II.isFilename() && "Unexpected lipo input."); CmdArgs.push_back(II.getFilename()); } const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("lipo")); C.addCommand(std::make_unique(JA, *this, ResponseFileSupport::None(), Exec, CmdArgs, Inputs, Output)); } void darwin::Dsymutil::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { ArgStringList CmdArgs; CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); assert(Inputs.size() == 1 && "Unable to handle multiple inputs."); const InputInfo &Input = Inputs[0]; assert(Input.isFilename() && "Unexpected dsymutil input."); CmdArgs.push_back(Input.getFilename()); const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("dsymutil")); C.addCommand(std::make_unique(JA, *this, ResponseFileSupport::None(), Exec, CmdArgs, Inputs, Output)); } void darwin::VerifyDebug::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { ArgStringList CmdArgs; CmdArgs.push_back("--verify"); CmdArgs.push_back("--debug-info"); CmdArgs.push_back("--eh-frame"); CmdArgs.push_back("--quiet"); assert(Inputs.size() == 1 && "Unable to handle multiple inputs."); const InputInfo &Input = Inputs[0]; assert(Input.isFilename() && "Unexpected verify input"); // Grabbing the output of the earlier dsymutil run. CmdArgs.push_back(Input.getFilename()); const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("dwarfdump")); C.addCommand(std::make_unique(JA, *this, ResponseFileSupport::None(), Exec, CmdArgs, Inputs, Output)); } MachO::MachO(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) : ToolChain(D, Triple, Args) { // We expect 'as', 'ld', etc. to be adjacent to our install dir. getProgramPaths().push_back(getDriver().getInstalledDir()); if (getDriver().getInstalledDir() != getDriver().Dir) getProgramPaths().push_back(getDriver().Dir); } /// Darwin - Darwin tool chain for i386 and x86_64. Darwin::Darwin(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) : MachO(D, Triple, Args), TargetInitialized(false), CudaInstallation(D, Triple, Args), RocmInstallation(D, Triple, Args) {} types::ID MachO::LookupTypeForExtension(StringRef Ext) const { types::ID Ty = ToolChain::LookupTypeForExtension(Ext); // Darwin always preprocesses assembly files (unless -x is used explicitly). if (Ty == types::TY_PP_Asm) return types::TY_Asm; return Ty; } bool MachO::HasNativeLLVMSupport() const { return true; } ToolChain::CXXStdlibType Darwin::GetDefaultCXXStdlibType() const { // Always use libc++ by default return ToolChain::CST_Libcxx; } /// Darwin provides an ARC runtime starting in MacOS X 10.7 and iOS 5.0. ObjCRuntime Darwin::getDefaultObjCRuntime(bool isNonFragile) const { if (isTargetWatchOSBased()) return ObjCRuntime(ObjCRuntime::WatchOS, TargetVersion); if (isTargetIOSBased()) return ObjCRuntime(ObjCRuntime::iOS, TargetVersion); if (isTargetXROS()) { // XROS uses the iOS runtime. auto T = llvm::Triple(Twine("arm64-apple-") + llvm::Triple::getOSTypeName(llvm::Triple::XROS) + TargetVersion.getAsString()); return ObjCRuntime(ObjCRuntime::iOS, T.getiOSVersion()); } if (isNonFragile) return ObjCRuntime(ObjCRuntime::MacOSX, TargetVersion); return ObjCRuntime(ObjCRuntime::FragileMacOSX, TargetVersion); } /// Darwin provides a blocks runtime starting in MacOS X 10.6 and iOS 3.2. bool Darwin::hasBlocksRuntime() const { if (isTargetWatchOSBased() || isTargetDriverKit() || isTargetXROS()) return true; else if (isTargetIOSBased()) return !isIPhoneOSVersionLT(3, 2); else { assert(isTargetMacOSBased() && "unexpected darwin target"); return !isMacosxVersionLT(10, 6); } } void Darwin::AddCudaIncludeArgs(const ArgList &DriverArgs, ArgStringList &CC1Args) const { CudaInstallation->AddCudaIncludeArgs(DriverArgs, CC1Args); } void Darwin::AddHIPIncludeArgs(const ArgList &DriverArgs, ArgStringList &CC1Args) const { RocmInstallation->AddHIPIncludeArgs(DriverArgs, CC1Args); } // This is just a MachO name translation routine and there's no // way to join this into ARMTargetParser without breaking all // other assumptions. Maybe MachO should consider standardising // their nomenclature. static const char *ArmMachOArchName(StringRef Arch) { return llvm::StringSwitch(Arch) .Case("armv6k", "armv6") .Case("armv6m", "armv6m") .Case("armv5tej", "armv5") .Case("xscale", "xscale") .Case("armv4t", "armv4t") .Case("armv7", "armv7") .Cases("armv7a", "armv7-a", "armv7") .Cases("armv7r", "armv7-r", "armv7") .Cases("armv7em", "armv7e-m", "armv7em") .Cases("armv7k", "armv7-k", "armv7k") .Cases("armv7m", "armv7-m", "armv7m") .Cases("armv7s", "armv7-s", "armv7s") .Default(nullptr); } static const char *ArmMachOArchNameCPU(StringRef CPU) { llvm::ARM::ArchKind ArchKind = llvm::ARM::parseCPUArch(CPU); if (ArchKind == llvm::ARM::ArchKind::INVALID) return nullptr; StringRef Arch = llvm::ARM::getArchName(ArchKind); // FIXME: Make sure this MachO triple mangling is really necessary. // ARMv5* normalises to ARMv5. if (Arch.starts_with("armv5")) Arch = Arch.substr(0, 5); // ARMv6*, except ARMv6M, normalises to ARMv6. else if (Arch.starts_with("armv6") && !Arch.ends_with("6m")) Arch = Arch.substr(0, 5); // ARMv7A normalises to ARMv7. else if (Arch.ends_with("v7a")) Arch = Arch.substr(0, 5); return Arch.data(); } StringRef MachO::getMachOArchName(const ArgList &Args) const { switch (getTriple().getArch()) { default: return getDefaultUniversalArchName(); case llvm::Triple::aarch64_32: return "arm64_32"; case llvm::Triple::aarch64: { if (getTriple().isArm64e()) return "arm64e"; return "arm64"; } case llvm::Triple::thumb: case llvm::Triple::arm: if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) if (const char *Arch = ArmMachOArchName(A->getValue())) return Arch; if (const Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) if (const char *Arch = ArmMachOArchNameCPU(A->getValue())) return Arch; return "arm"; } } VersionTuple MachO::getLinkerVersion(const llvm::opt::ArgList &Args) const { if (LinkerVersion) { #ifndef NDEBUG VersionTuple NewLinkerVersion; if (Arg *A = Args.getLastArg(options::OPT_mlinker_version_EQ)) (void)NewLinkerVersion.tryParse(A->getValue()); assert(NewLinkerVersion == LinkerVersion); #endif return *LinkerVersion; } VersionTuple NewLinkerVersion; if (Arg *A = Args.getLastArg(options::OPT_mlinker_version_EQ)) if (NewLinkerVersion.tryParse(A->getValue())) getDriver().Diag(diag::err_drv_invalid_version_number) << A->getAsString(Args); LinkerVersion = NewLinkerVersion; return *LinkerVersion; } Darwin::~Darwin() {} MachO::~MachO() {} std::string Darwin::ComputeEffectiveClangTriple(const ArgList &Args, types::ID InputType) const { llvm::Triple Triple(ComputeLLVMTriple(Args, InputType)); // If the target isn't initialized (e.g., an unknown Darwin platform, return // the default triple). if (!isTargetInitialized()) return Triple.getTriple(); SmallString<16> Str; if (isTargetWatchOSBased()) Str += "watchos"; else if (isTargetTvOSBased()) Str += "tvos"; else if (isTargetDriverKit()) Str += "driverkit"; else if (isTargetIOSBased() || isTargetMacCatalyst()) Str += "ios"; else if (isTargetXROS()) Str += llvm::Triple::getOSTypeName(llvm::Triple::XROS); else Str += "macosx"; Str += getTripleTargetVersion().getAsString(); Triple.setOSName(Str); return Triple.getTriple(); } Tool *MachO::getTool(Action::ActionClass AC) const { switch (AC) { case Action::LipoJobClass: if (!Lipo) Lipo.reset(new tools::darwin::Lipo(*this)); return Lipo.get(); case Action::DsymutilJobClass: if (!Dsymutil) Dsymutil.reset(new tools::darwin::Dsymutil(*this)); return Dsymutil.get(); case Action::VerifyDebugInfoJobClass: if (!VerifyDebug) VerifyDebug.reset(new tools::darwin::VerifyDebug(*this)); return VerifyDebug.get(); default: return ToolChain::getTool(AC); } } Tool *MachO::buildLinker() const { return new tools::darwin::Linker(*this); } Tool *MachO::buildStaticLibTool() const { return new tools::darwin::StaticLibTool(*this); } Tool *MachO::buildAssembler() const { return new tools::darwin::Assembler(*this); } DarwinClang::DarwinClang(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) : Darwin(D, Triple, Args) {} void DarwinClang::addClangWarningOptions(ArgStringList &CC1Args) const { // Always error about undefined 'TARGET_OS_*' macros. CC1Args.push_back("-Wundef-prefix=TARGET_OS_"); CC1Args.push_back("-Werror=undef-prefix"); // For modern targets, promote certain warnings to errors. if (isTargetWatchOSBased() || getTriple().isArch64Bit()) { // Always enable -Wdeprecated-objc-isa-usage and promote it // to an error. CC1Args.push_back("-Wdeprecated-objc-isa-usage"); CC1Args.push_back("-Werror=deprecated-objc-isa-usage"); // For iOS and watchOS, also error about implicit function declarations, // as that can impact calling conventions. if (!isTargetMacOS()) CC1Args.push_back("-Werror=implicit-function-declaration"); } } /// Take a path that speculatively points into Xcode and return the /// `XCODE/Contents/Developer` path if it is an Xcode path, or an empty path /// otherwise. static StringRef getXcodeDeveloperPath(StringRef PathIntoXcode) { static constexpr llvm::StringLiteral XcodeAppSuffix( ".app/Contents/Developer"); size_t Index = PathIntoXcode.find(XcodeAppSuffix); if (Index == StringRef::npos) return ""; return PathIntoXcode.take_front(Index + XcodeAppSuffix.size()); } void DarwinClang::AddLinkARCArgs(const ArgList &Args, ArgStringList &CmdArgs) const { // Avoid linking compatibility stubs on i386 mac. if (isTargetMacOSBased() && getArch() == llvm::Triple::x86) return; if (isTargetAppleSiliconMac()) return; // ARC runtime is supported everywhere on arm64e. if (getTriple().isArm64e()) return; if (isTargetXROS()) return; ObjCRuntime runtime = getDefaultObjCRuntime(/*nonfragile*/ true); if ((runtime.hasNativeARC() || !isObjCAutoRefCount(Args)) && runtime.hasSubscripting()) return; SmallString<128> P(getDriver().ClangExecutable); llvm::sys::path::remove_filename(P); // 'clang' llvm::sys::path::remove_filename(P); // 'bin' llvm::sys::path::append(P, "lib", "arc"); // 'libarclite' usually lives in the same toolchain as 'clang'. However, the // Swift open source toolchains for macOS distribute Clang without libarclite. // In that case, to allow the linker to find 'libarclite', we point to the // 'libarclite' in the XcodeDefault toolchain instead. if (!getVFS().exists(P)) { auto updatePath = [&](const Arg *A) { // Try to infer the path to 'libarclite' in the toolchain from the // specified SDK path. StringRef XcodePathForSDK = getXcodeDeveloperPath(A->getValue()); if (XcodePathForSDK.empty()) return false; P = XcodePathForSDK; llvm::sys::path::append(P, "Toolchains/XcodeDefault.xctoolchain/usr", "lib", "arc"); return getVFS().exists(P); }; bool updated = false; if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) updated = updatePath(A); if (!updated) { if (const Arg *A = Args.getLastArg(options::OPT__sysroot_EQ)) updatePath(A); } } CmdArgs.push_back("-force_load"); llvm::sys::path::append(P, "libarclite_"); // Mash in the platform. if (isTargetWatchOSSimulator()) P += "watchsimulator"; else if (isTargetWatchOS()) P += "watchos"; else if (isTargetTvOSSimulator()) P += "appletvsimulator"; else if (isTargetTvOS()) P += "appletvos"; else if (isTargetIOSSimulator()) P += "iphonesimulator"; else if (isTargetIPhoneOS()) P += "iphoneos"; else P += "macosx"; P += ".a"; if (!getVFS().exists(P)) getDriver().Diag(clang::diag::err_drv_darwin_sdk_missing_arclite) << P; CmdArgs.push_back(Args.MakeArgString(P)); } unsigned DarwinClang::GetDefaultDwarfVersion() const { // Default to use DWARF 2 on OS X 10.10 / iOS 8 and lower. if ((isTargetMacOSBased() && isMacosxVersionLT(10, 11)) || (isTargetIOSBased() && isIPhoneOSVersionLT(9))) return 2; return 4; } void MachO::AddLinkRuntimeLib(const ArgList &Args, ArgStringList &CmdArgs, StringRef Component, RuntimeLinkOptions Opts, bool IsShared) const { SmallString<64> DarwinLibName = StringRef("libclang_rt."); // an Darwin the builtins compomnent is not in the library name if (Component != "builtins") { DarwinLibName += Component; if (!(Opts & RLO_IsEmbedded)) DarwinLibName += "_"; } DarwinLibName += getOSLibraryNameSuffix(); DarwinLibName += IsShared ? "_dynamic.dylib" : ".a"; SmallString<128> Dir(getDriver().ResourceDir); llvm::sys::path::append(Dir, "lib", "darwin"); if (Opts & RLO_IsEmbedded) llvm::sys::path::append(Dir, "macho_embedded"); SmallString<128> P(Dir); llvm::sys::path::append(P, DarwinLibName); // For now, allow missing resource libraries to support developers who may // not have compiler-rt checked out or integrated into their build (unless // we explicitly force linking with this library). if ((Opts & RLO_AlwaysLink) || getVFS().exists(P)) { const char *LibArg = Args.MakeArgString(P); CmdArgs.push_back(LibArg); } // Adding the rpaths might negatively interact when other rpaths are involved, // so we should make sure we add the rpaths last, after all user-specified // rpaths. This is currently true from this place, but we need to be // careful if this function is ever called before user's rpaths are emitted. if (Opts & RLO_AddRPath) { assert(DarwinLibName.ends_with(".dylib") && "must be a dynamic library"); // Add @executable_path to rpath to support having the dylib copied with // the executable. CmdArgs.push_back("-rpath"); CmdArgs.push_back("@executable_path"); // Add the path to the resource dir to rpath to support using the dylib // from the default location without copying. CmdArgs.push_back("-rpath"); CmdArgs.push_back(Args.MakeArgString(Dir)); } } StringRef Darwin::getPlatformFamily() const { switch (TargetPlatform) { case DarwinPlatformKind::MacOS: return "MacOSX"; case DarwinPlatformKind::IPhoneOS: if (TargetEnvironment == MacCatalyst) return "MacOSX"; return "iPhone"; case DarwinPlatformKind::TvOS: return "AppleTV"; case DarwinPlatformKind::WatchOS: return "Watch"; case DarwinPlatformKind::DriverKit: return "DriverKit"; case DarwinPlatformKind::XROS: return "XR"; } llvm_unreachable("Unsupported platform"); } StringRef Darwin::getSDKName(StringRef isysroot) { // Assume SDK has path: SOME_PATH/SDKs/PlatformXX.YY.sdk auto BeginSDK = llvm::sys::path::rbegin(isysroot); auto EndSDK = llvm::sys::path::rend(isysroot); for (auto IT = BeginSDK; IT != EndSDK; ++IT) { StringRef SDK = *IT; if (SDK.ends_with(".sdk")) return SDK.slice(0, SDK.size() - 4); } return ""; } StringRef Darwin::getOSLibraryNameSuffix(bool IgnoreSim) const { switch (TargetPlatform) { case DarwinPlatformKind::MacOS: return "osx"; case DarwinPlatformKind::IPhoneOS: if (TargetEnvironment == MacCatalyst) return "osx"; return TargetEnvironment == NativeEnvironment || IgnoreSim ? "ios" : "iossim"; case DarwinPlatformKind::TvOS: return TargetEnvironment == NativeEnvironment || IgnoreSim ? "tvos" : "tvossim"; case DarwinPlatformKind::WatchOS: return TargetEnvironment == NativeEnvironment || IgnoreSim ? "watchos" : "watchossim"; case DarwinPlatformKind::XROS: return TargetEnvironment == NativeEnvironment || IgnoreSim ? "xros" : "xrossim"; case DarwinPlatformKind::DriverKit: return "driverkit"; } llvm_unreachable("Unsupported platform"); } /// Check if the link command contains a symbol export directive. static bool hasExportSymbolDirective(const ArgList &Args) { for (Arg *A : Args) { if (A->getOption().matches(options::OPT_exported__symbols__list)) return true; if (!A->getOption().matches(options::OPT_Wl_COMMA) && !A->getOption().matches(options::OPT_Xlinker)) continue; if (A->containsValue("-exported_symbols_list") || A->containsValue("-exported_symbol")) return true; } return false; } /// Add an export directive for \p Symbol to the link command. static void addExportedSymbol(ArgStringList &CmdArgs, const char *Symbol) { CmdArgs.push_back("-exported_symbol"); CmdArgs.push_back(Symbol); } /// Add a sectalign directive for \p Segment and \p Section to the maximum /// expected page size for Darwin. /// /// On iPhone 6+ the max supported page size is 16K. On macOS, the max is 4K. /// Use a common alignment constant (16K) for now, and reduce the alignment on /// macOS if it proves important. static void addSectalignToPage(const ArgList &Args, ArgStringList &CmdArgs, StringRef Segment, StringRef Section) { for (const char *A : {"-sectalign", Args.MakeArgString(Segment), Args.MakeArgString(Section), "0x4000"}) CmdArgs.push_back(A); } void Darwin::addProfileRTLibs(const ArgList &Args, ArgStringList &CmdArgs) const { if (!needsProfileRT(Args) && !needsGCovInstrumentation(Args)) return; AddLinkRuntimeLib(Args, CmdArgs, "profile", RuntimeLinkOptions(RLO_AlwaysLink)); bool ForGCOV = needsGCovInstrumentation(Args); // If we have a symbol export directive and we're linking in the profile // runtime, automatically export symbols necessary to implement some of the // runtime's functionality. if (hasExportSymbolDirective(Args) && ForGCOV) { addExportedSymbol(CmdArgs, "___gcov_dump"); addExportedSymbol(CmdArgs, "___gcov_reset"); addExportedSymbol(CmdArgs, "_writeout_fn_list"); addExportedSymbol(CmdArgs, "_reset_fn_list"); } // Align __llvm_prf_{cnts,bits,data} sections to the maximum expected page // alignment. This allows profile counters to be mmap()'d to disk. Note that // it's not enough to just page-align __llvm_prf_cnts: the following section // must also be page-aligned so that its data is not clobbered by mmap(). // // The section alignment is only needed when continuous profile sync is // enabled, but this is expected to be the default in Xcode. Specifying the // extra alignment also allows the same binary to be used with/without sync // enabled. if (!ForGCOV) { for (auto IPSK : {llvm::IPSK_cnts, llvm::IPSK_bitmap, llvm::IPSK_data}) { addSectalignToPage( Args, CmdArgs, "__DATA", llvm::getInstrProfSectionName(IPSK, llvm::Triple::MachO, /*AddSegmentInfo=*/false)); } } } void DarwinClang::AddLinkSanitizerLibArgs(const ArgList &Args, ArgStringList &CmdArgs, StringRef Sanitizer, bool Shared) const { auto RLO = RuntimeLinkOptions(RLO_AlwaysLink | (Shared ? RLO_AddRPath : 0U)); AddLinkRuntimeLib(Args, CmdArgs, Sanitizer, RLO, Shared); } ToolChain::RuntimeLibType DarwinClang::GetRuntimeLibType( const ArgList &Args) const { if (Arg* A = Args.getLastArg(options::OPT_rtlib_EQ)) { StringRef Value = A->getValue(); if (Value != "compiler-rt" && Value != "platform") getDriver().Diag(clang::diag::err_drv_unsupported_rtlib_for_platform) << Value << "darwin"; } return ToolChain::RLT_CompilerRT; } void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args, ArgStringList &CmdArgs, bool ForceLinkBuiltinRT) const { // Call once to ensure diagnostic is printed if wrong value was specified GetRuntimeLibType(Args); // Darwin doesn't support real static executables, don't link any runtime // libraries with -static. if (Args.hasArg(options::OPT_static) || Args.hasArg(options::OPT_fapple_kext) || Args.hasArg(options::OPT_mkernel)) { if (ForceLinkBuiltinRT) AddLinkRuntimeLib(Args, CmdArgs, "builtins"); return; } // Reject -static-libgcc for now, we can deal with this when and if someone // cares. This is useful in situations where someone wants to statically link // something like libstdc++, and needs its runtime support routines. if (const Arg *A = Args.getLastArg(options::OPT_static_libgcc)) { getDriver().Diag(diag::err_drv_unsupported_opt) << A->getAsString(Args); return; } const SanitizerArgs &Sanitize = getSanitizerArgs(Args); if (!Sanitize.needsSharedRt()) { const char *sanitizer = nullptr; if (Sanitize.needsUbsanRt()) { sanitizer = "UndefinedBehaviorSanitizer"; } else if (Sanitize.needsAsanRt()) { sanitizer = "AddressSanitizer"; } else if (Sanitize.needsTsanRt()) { sanitizer = "ThreadSanitizer"; } if (sanitizer) { getDriver().Diag(diag::err_drv_unsupported_static_sanitizer_darwin) << sanitizer; return; } } if (Sanitize.linkRuntimes()) { if (Sanitize.needsAsanRt()) { if (Sanitize.needsStableAbi()) { AddLinkSanitizerLibArgs(Args, CmdArgs, "asan_abi", /*shared=*/false); } else { assert(Sanitize.needsSharedRt() && "Static sanitizer runtimes not supported"); AddLinkSanitizerLibArgs(Args, CmdArgs, "asan"); } } if (Sanitize.needsLsanRt()) AddLinkSanitizerLibArgs(Args, CmdArgs, "lsan"); if (Sanitize.needsUbsanRt()) { assert(Sanitize.needsSharedRt() && "Static sanitizer runtimes not supported"); AddLinkSanitizerLibArgs( Args, CmdArgs, Sanitize.requiresMinimalRuntime() ? "ubsan_minimal" : "ubsan"); } if (Sanitize.needsTsanRt()) { assert(Sanitize.needsSharedRt() && "Static sanitizer runtimes not supported"); AddLinkSanitizerLibArgs(Args, CmdArgs, "tsan"); } if (Sanitize.needsFuzzer() && !Args.hasArg(options::OPT_dynamiclib)) { AddLinkSanitizerLibArgs(Args, CmdArgs, "fuzzer", /*shared=*/false); // Libfuzzer is written in C++ and requires libcxx. AddCXXStdlibLibArgs(Args, CmdArgs); } if (Sanitize.needsStatsRt()) { AddLinkRuntimeLib(Args, CmdArgs, "stats_client", RLO_AlwaysLink); AddLinkSanitizerLibArgs(Args, CmdArgs, "stats"); } } const XRayArgs &XRay = getXRayArgs(); if (XRay.needsXRayRt()) { AddLinkRuntimeLib(Args, CmdArgs, "xray"); AddLinkRuntimeLib(Args, CmdArgs, "xray-basic"); AddLinkRuntimeLib(Args, CmdArgs, "xray-fdr"); } if (isTargetDriverKit() && !Args.hasArg(options::OPT_nodriverkitlib)) { CmdArgs.push_back("-framework"); CmdArgs.push_back("DriverKit"); } // Otherwise link libSystem, then the dynamic runtime library, and finally any // target specific static runtime library. if (!isTargetDriverKit()) CmdArgs.push_back("-lSystem"); // Select the dynamic runtime library and the target specific static library. if (isTargetIOSBased()) { // If we are compiling as iOS / simulator, don't attempt to link libgcc_s.1, // it never went into the SDK. // Linking against libgcc_s.1 isn't needed for iOS 5.0+ if (isIPhoneOSVersionLT(5, 0) && !isTargetIOSSimulator() && getTriple().getArch() != llvm::Triple::aarch64) CmdArgs.push_back("-lgcc_s.1"); } AddLinkRuntimeLib(Args, CmdArgs, "builtins"); } /// Returns the most appropriate macOS target version for the current process. /// /// If the macOS SDK version is the same or earlier than the system version, /// then the SDK version is returned. Otherwise the system version is returned. static std::string getSystemOrSDKMacOSVersion(StringRef MacOSSDKVersion) { llvm::Triple SystemTriple(llvm::sys::getProcessTriple()); if (!SystemTriple.isMacOSX()) return std::string(MacOSSDKVersion); VersionTuple SystemVersion; SystemTriple.getMacOSXVersion(SystemVersion); unsigned Major, Minor, Micro; bool HadExtra; if (!Driver::GetReleaseVersion(MacOSSDKVersion, Major, Minor, Micro, HadExtra)) return std::string(MacOSSDKVersion); VersionTuple SDKVersion(Major, Minor, Micro); if (SDKVersion > SystemVersion) return SystemVersion.getAsString(); return std::string(MacOSSDKVersion); } namespace { /// The Darwin OS that was selected or inferred from arguments / environment. struct DarwinPlatform { enum SourceKind { /// The OS was specified using the -target argument. TargetArg, /// The OS was specified using the -mtargetos= argument. MTargetOSArg, /// The OS was specified using the -m-version-min argument. OSVersionArg, /// The OS was specified using the OS_DEPLOYMENT_TARGET environment. DeploymentTargetEnv, /// The OS was inferred from the SDK. InferredFromSDK, /// The OS was inferred from the -arch. InferredFromArch }; using DarwinPlatformKind = Darwin::DarwinPlatformKind; using DarwinEnvironmentKind = Darwin::DarwinEnvironmentKind; DarwinPlatformKind getPlatform() const { return Platform; } DarwinEnvironmentKind getEnvironment() const { return Environment; } void setEnvironment(DarwinEnvironmentKind Kind) { Environment = Kind; InferSimulatorFromArch = false; } StringRef getOSVersion() const { if (Kind == OSVersionArg) return Argument->getValue(); return OSVersion; } void setOSVersion(StringRef S) { assert(Kind == TargetArg && "Unexpected kind!"); OSVersion = std::string(S); } bool hasOSVersion() const { return HasOSVersion; } VersionTuple getNativeTargetVersion() const { assert(Environment == DarwinEnvironmentKind::MacCatalyst && "native target version is specified only for Mac Catalyst"); return NativeTargetVersion; } /// Returns true if the target OS was explicitly specified. bool isExplicitlySpecified() const { return Kind <= DeploymentTargetEnv; } /// Returns true if the simulator environment can be inferred from the arch. bool canInferSimulatorFromArch() const { return InferSimulatorFromArch; } const std::optional &getTargetVariantTriple() const { return TargetVariantTriple; } /// Adds the -m-version-min argument to the compiler invocation. void addOSVersionMinArgument(DerivedArgList &Args, const OptTable &Opts) { if (Argument) return; assert(Kind != TargetArg && Kind != MTargetOSArg && Kind != OSVersionArg && "Invalid kind"); options::ID Opt; switch (Platform) { case DarwinPlatformKind::MacOS: Opt = options::OPT_mmacos_version_min_EQ; break; case DarwinPlatformKind::IPhoneOS: Opt = options::OPT_mios_version_min_EQ; break; case DarwinPlatformKind::TvOS: Opt = options::OPT_mtvos_version_min_EQ; break; case DarwinPlatformKind::WatchOS: Opt = options::OPT_mwatchos_version_min_EQ; break; case DarwinPlatformKind::XROS: // xrOS always explicitly provides a version in the triple. return; case DarwinPlatformKind::DriverKit: // DriverKit always explicitly provides a version in the triple. return; } Argument = Args.MakeJoinedArg(nullptr, Opts.getOption(Opt), OSVersion); Args.append(Argument); } /// Returns the OS version with the argument / environment variable that /// specified it. std::string getAsString(DerivedArgList &Args, const OptTable &Opts) { switch (Kind) { case TargetArg: case MTargetOSArg: case OSVersionArg: case InferredFromSDK: case InferredFromArch: assert(Argument && "OS version argument not yet inferred"); return Argument->getAsString(Args); case DeploymentTargetEnv: return (llvm::Twine(EnvVarName) + "=" + OSVersion).str(); } llvm_unreachable("Unsupported Darwin Source Kind"); } void setEnvironment(llvm::Triple::EnvironmentType EnvType, const VersionTuple &OSVersion, const std::optional &SDKInfo) { switch (EnvType) { case llvm::Triple::Simulator: Environment = DarwinEnvironmentKind::Simulator; break; case llvm::Triple::MacABI: { Environment = DarwinEnvironmentKind::MacCatalyst; // The minimum native macOS target for MacCatalyst is macOS 10.15. NativeTargetVersion = VersionTuple(10, 15); if (HasOSVersion && SDKInfo) { if (const auto *MacCatalystToMacOSMapping = SDKInfo->getVersionMapping( DarwinSDKInfo::OSEnvPair::macCatalystToMacOSPair())) { if (auto MacOSVersion = MacCatalystToMacOSMapping->map( OSVersion, NativeTargetVersion, std::nullopt)) { NativeTargetVersion = *MacOSVersion; } } } // In a zippered build, we could be building for a macOS target that's // lower than the version that's implied by the OS version. In that case // we need to use the minimum version as the native target version. if (TargetVariantTriple) { auto TargetVariantVersion = TargetVariantTriple->getOSVersion(); if (TargetVariantVersion.getMajor()) { if (TargetVariantVersion < NativeTargetVersion) NativeTargetVersion = TargetVariantVersion; } } break; } default: break; } } static DarwinPlatform createFromTarget(const llvm::Triple &TT, StringRef OSVersion, Arg *A, std::optional TargetVariantTriple, const std::optional &SDKInfo) { DarwinPlatform Result(TargetArg, getPlatformFromOS(TT.getOS()), OSVersion, A); VersionTuple OsVersion = TT.getOSVersion(); if (OsVersion.getMajor() == 0) Result.HasOSVersion = false; Result.TargetVariantTriple = TargetVariantTriple; Result.setEnvironment(TT.getEnvironment(), OsVersion, SDKInfo); return Result; } static DarwinPlatform createFromMTargetOS(llvm::Triple::OSType OS, VersionTuple OSVersion, llvm::Triple::EnvironmentType Environment, Arg *A, const std::optional &SDKInfo) { DarwinPlatform Result(MTargetOSArg, getPlatformFromOS(OS), OSVersion.getAsString(), A); Result.InferSimulatorFromArch = false; Result.setEnvironment(Environment, OSVersion, SDKInfo); return Result; } static DarwinPlatform createOSVersionArg(DarwinPlatformKind Platform, Arg *A, bool IsSimulator) { DarwinPlatform Result{OSVersionArg, Platform, A}; if (IsSimulator) Result.Environment = DarwinEnvironmentKind::Simulator; return Result; } static DarwinPlatform createDeploymentTargetEnv(DarwinPlatformKind Platform, StringRef EnvVarName, StringRef Value) { DarwinPlatform Result(DeploymentTargetEnv, Platform, Value); Result.EnvVarName = EnvVarName; return Result; } static DarwinPlatform createFromSDK(DarwinPlatformKind Platform, StringRef Value, bool IsSimulator = false) { DarwinPlatform Result(InferredFromSDK, Platform, Value); if (IsSimulator) Result.Environment = DarwinEnvironmentKind::Simulator; Result.InferSimulatorFromArch = false; return Result; } static DarwinPlatform createFromArch(llvm::Triple::OSType OS, StringRef Value) { return DarwinPlatform(InferredFromArch, getPlatformFromOS(OS), Value); } /// Constructs an inferred SDKInfo value based on the version inferred from /// the SDK path itself. Only works for values that were created by inferring /// the platform from the SDKPath. DarwinSDKInfo inferSDKInfo() { assert(Kind == InferredFromSDK && "can infer SDK info only"); llvm::VersionTuple Version; bool IsValid = !Version.tryParse(OSVersion); (void)IsValid; assert(IsValid && "invalid SDK version"); return DarwinSDKInfo( Version, /*MaximumDeploymentTarget=*/VersionTuple(Version.getMajor(), 0, 99)); } private: DarwinPlatform(SourceKind Kind, DarwinPlatformKind Platform, Arg *Argument) : Kind(Kind), Platform(Platform), Argument(Argument) {} DarwinPlatform(SourceKind Kind, DarwinPlatformKind Platform, StringRef Value, Arg *Argument = nullptr) : Kind(Kind), Platform(Platform), OSVersion(Value), Argument(Argument) {} static DarwinPlatformKind getPlatformFromOS(llvm::Triple::OSType OS) { switch (OS) { case llvm::Triple::Darwin: case llvm::Triple::MacOSX: return DarwinPlatformKind::MacOS; case llvm::Triple::IOS: return DarwinPlatformKind::IPhoneOS; case llvm::Triple::TvOS: return DarwinPlatformKind::TvOS; case llvm::Triple::WatchOS: return DarwinPlatformKind::WatchOS; case llvm::Triple::XROS: return DarwinPlatformKind::XROS; case llvm::Triple::DriverKit: return DarwinPlatformKind::DriverKit; default: llvm_unreachable("Unable to infer Darwin variant"); } } SourceKind Kind; DarwinPlatformKind Platform; DarwinEnvironmentKind Environment = DarwinEnvironmentKind::NativeEnvironment; VersionTuple NativeTargetVersion; std::string OSVersion; bool HasOSVersion = true, InferSimulatorFromArch = true; Arg *Argument; StringRef EnvVarName; std::optional TargetVariantTriple; }; /// Returns the deployment target that's specified using the -m-version-min /// argument. std::optional getDeploymentTargetFromOSVersionArg(DerivedArgList &Args, const Driver &TheDriver) { Arg *macOSVersion = Args.getLastArg(options::OPT_mmacos_version_min_EQ); Arg *iOSVersion = Args.getLastArg(options::OPT_mios_version_min_EQ, options::OPT_mios_simulator_version_min_EQ); Arg *TvOSVersion = Args.getLastArg(options::OPT_mtvos_version_min_EQ, options::OPT_mtvos_simulator_version_min_EQ); Arg *WatchOSVersion = Args.getLastArg(options::OPT_mwatchos_version_min_EQ, options::OPT_mwatchos_simulator_version_min_EQ); if (macOSVersion) { if (iOSVersion || TvOSVersion || WatchOSVersion) { TheDriver.Diag(diag::err_drv_argument_not_allowed_with) << macOSVersion->getAsString(Args) << (iOSVersion ? iOSVersion : TvOSVersion ? TvOSVersion : WatchOSVersion) ->getAsString(Args); } return DarwinPlatform::createOSVersionArg(Darwin::MacOS, macOSVersion, /*IsSimulator=*/false); } else if (iOSVersion) { if (TvOSVersion || WatchOSVersion) { TheDriver.Diag(diag::err_drv_argument_not_allowed_with) << iOSVersion->getAsString(Args) << (TvOSVersion ? TvOSVersion : WatchOSVersion)->getAsString(Args); } return DarwinPlatform::createOSVersionArg( Darwin::IPhoneOS, iOSVersion, iOSVersion->getOption().getID() == options::OPT_mios_simulator_version_min_EQ); } else if (TvOSVersion) { if (WatchOSVersion) { TheDriver.Diag(diag::err_drv_argument_not_allowed_with) << TvOSVersion->getAsString(Args) << WatchOSVersion->getAsString(Args); } return DarwinPlatform::createOSVersionArg( Darwin::TvOS, TvOSVersion, TvOSVersion->getOption().getID() == options::OPT_mtvos_simulator_version_min_EQ); } else if (WatchOSVersion) return DarwinPlatform::createOSVersionArg( Darwin::WatchOS, WatchOSVersion, WatchOSVersion->getOption().getID() == options::OPT_mwatchos_simulator_version_min_EQ); return std::nullopt; } /// Returns the deployment target that's specified using the /// OS_DEPLOYMENT_TARGET environment variable. std::optional getDeploymentTargetFromEnvironmentVariables(const Driver &TheDriver, const llvm::Triple &Triple) { std::string Targets[Darwin::LastDarwinPlatform + 1]; const char *EnvVars[] = { "MACOSX_DEPLOYMENT_TARGET", "IPHONEOS_DEPLOYMENT_TARGET", "TVOS_DEPLOYMENT_TARGET", "WATCHOS_DEPLOYMENT_TARGET", "DRIVERKIT_DEPLOYMENT_TARGET", }; static_assert(std::size(EnvVars) == Darwin::LastDarwinPlatform + 1, "Missing platform"); for (const auto &I : llvm::enumerate(llvm::ArrayRef(EnvVars))) { if (char *Env = ::getenv(I.value())) Targets[I.index()] = Env; } // Allow conflicts among OSX and iOS for historical reasons, but choose the // default platform. if (!Targets[Darwin::MacOS].empty() && (!Targets[Darwin::IPhoneOS].empty() || !Targets[Darwin::WatchOS].empty() || !Targets[Darwin::TvOS].empty())) { if (Triple.getArch() == llvm::Triple::arm || Triple.getArch() == llvm::Triple::aarch64 || Triple.getArch() == llvm::Triple::thumb) Targets[Darwin::MacOS] = ""; else Targets[Darwin::IPhoneOS] = Targets[Darwin::WatchOS] = Targets[Darwin::TvOS] = ""; } else { // Don't allow conflicts in any other platform. unsigned FirstTarget = std::size(Targets); for (unsigned I = 0; I != std::size(Targets); ++I) { if (Targets[I].empty()) continue; if (FirstTarget == std::size(Targets)) FirstTarget = I; else TheDriver.Diag(diag::err_drv_conflicting_deployment_targets) << Targets[FirstTarget] << Targets[I]; } } for (const auto &Target : llvm::enumerate(llvm::ArrayRef(Targets))) { if (!Target.value().empty()) return DarwinPlatform::createDeploymentTargetEnv( (Darwin::DarwinPlatformKind)Target.index(), EnvVars[Target.index()], Target.value()); } return std::nullopt; } /// Returns the SDK name without the optional prefix that ends with a '.' or an /// empty string otherwise. static StringRef dropSDKNamePrefix(StringRef SDKName) { size_t PrefixPos = SDKName.find('.'); if (PrefixPos == StringRef::npos) return ""; return SDKName.substr(PrefixPos + 1); } /// Tries to infer the deployment target from the SDK specified by -isysroot /// (or SDKROOT). Uses the version specified in the SDKSettings.json file if /// it's available. std::optional inferDeploymentTargetFromSDK(DerivedArgList &Args, const std::optional &SDKInfo) { const Arg *A = Args.getLastArg(options::OPT_isysroot); if (!A) return std::nullopt; StringRef isysroot = A->getValue(); StringRef SDK = Darwin::getSDKName(isysroot); if (!SDK.size()) return std::nullopt; std::string Version; if (SDKInfo) { // Get the version from the SDKSettings.json if it's available. Version = SDKInfo->getVersion().getAsString(); } else { // Slice the version number out. // Version number is between the first and the last number. size_t StartVer = SDK.find_first_of("0123456789"); size_t EndVer = SDK.find_last_of("0123456789"); if (StartVer != StringRef::npos && EndVer > StartVer) Version = std::string(SDK.slice(StartVer, EndVer + 1)); } if (Version.empty()) return std::nullopt; auto CreatePlatformFromSDKName = [&](StringRef SDK) -> std::optional { if (SDK.starts_with("iPhoneOS") || SDK.starts_with("iPhoneSimulator")) return DarwinPlatform::createFromSDK( Darwin::IPhoneOS, Version, /*IsSimulator=*/SDK.starts_with("iPhoneSimulator")); else if (SDK.starts_with("MacOSX")) return DarwinPlatform::createFromSDK(Darwin::MacOS, getSystemOrSDKMacOSVersion(Version)); else if (SDK.starts_with("WatchOS") || SDK.starts_with("WatchSimulator")) return DarwinPlatform::createFromSDK( Darwin::WatchOS, Version, /*IsSimulator=*/SDK.starts_with("WatchSimulator")); else if (SDK.starts_with("AppleTVOS") || SDK.starts_with("AppleTVSimulator")) return DarwinPlatform::createFromSDK( Darwin::TvOS, Version, /*IsSimulator=*/SDK.starts_with("AppleTVSimulator")); else if (SDK.starts_with("XR")) return DarwinPlatform::createFromSDK( Darwin::XROS, Version, /*IsSimulator=*/SDK.contains("Simulator")); else if (SDK.starts_with("DriverKit")) return DarwinPlatform::createFromSDK(Darwin::DriverKit, Version); return std::nullopt; }; if (auto Result = CreatePlatformFromSDKName(SDK)) return Result; // The SDK can be an SDK variant with a name like `.`. return CreatePlatformFromSDKName(dropSDKNamePrefix(SDK)); } std::string getOSVersion(llvm::Triple::OSType OS, const llvm::Triple &Triple, const Driver &TheDriver) { VersionTuple OsVersion; llvm::Triple SystemTriple(llvm::sys::getProcessTriple()); switch (OS) { case llvm::Triple::Darwin: case llvm::Triple::MacOSX: // If there is no version specified on triple, and both host and target are // macos, use the host triple to infer OS version. if (Triple.isMacOSX() && SystemTriple.isMacOSX() && !Triple.getOSMajorVersion()) SystemTriple.getMacOSXVersion(OsVersion); else if (!Triple.getMacOSXVersion(OsVersion)) TheDriver.Diag(diag::err_drv_invalid_darwin_version) << Triple.getOSName(); break; case llvm::Triple::IOS: if (Triple.isMacCatalystEnvironment() && !Triple.getOSMajorVersion()) { OsVersion = VersionTuple(13, 1); } else OsVersion = Triple.getiOSVersion(); break; case llvm::Triple::TvOS: OsVersion = Triple.getOSVersion(); break; case llvm::Triple::WatchOS: OsVersion = Triple.getWatchOSVersion(); break; case llvm::Triple::XROS: OsVersion = Triple.getOSVersion(); if (!OsVersion.getMajor()) OsVersion = OsVersion.withMajorReplaced(1); break; case llvm::Triple::DriverKit: OsVersion = Triple.getDriverKitVersion(); break; default: llvm_unreachable("Unexpected OS type"); break; } std::string OSVersion; llvm::raw_string_ostream(OSVersion) << OsVersion.getMajor() << '.' << OsVersion.getMinor().value_or(0) << '.' << OsVersion.getSubminor().value_or(0); return OSVersion; } /// Tries to infer the target OS from the -arch. std::optional inferDeploymentTargetFromArch(DerivedArgList &Args, const Darwin &Toolchain, const llvm::Triple &Triple, const Driver &TheDriver) { llvm::Triple::OSType OSTy = llvm::Triple::UnknownOS; StringRef MachOArchName = Toolchain.getMachOArchName(Args); if (MachOArchName == "arm64" || MachOArchName == "arm64e") OSTy = llvm::Triple::MacOSX; else if (MachOArchName == "armv7" || MachOArchName == "armv7s") OSTy = llvm::Triple::IOS; else if (MachOArchName == "armv7k" || MachOArchName == "arm64_32") OSTy = llvm::Triple::WatchOS; else if (MachOArchName != "armv6m" && MachOArchName != "armv7m" && MachOArchName != "armv7em") OSTy = llvm::Triple::MacOSX; if (OSTy == llvm::Triple::UnknownOS) return std::nullopt; return DarwinPlatform::createFromArch(OSTy, getOSVersion(OSTy, Triple, TheDriver)); } /// Returns the deployment target that's specified using the -target option. std::optional getDeploymentTargetFromTargetArg( DerivedArgList &Args, const llvm::Triple &Triple, const Driver &TheDriver, const std::optional &SDKInfo) { if (!Args.hasArg(options::OPT_target)) return std::nullopt; if (Triple.getOS() == llvm::Triple::Darwin || Triple.getOS() == llvm::Triple::UnknownOS) return std::nullopt; std::string OSVersion = getOSVersion(Triple.getOS(), Triple, TheDriver); std::optional TargetVariantTriple; for (const Arg *A : Args.filtered(options::OPT_darwin_target_variant)) { llvm::Triple TVT(A->getValue()); // Find a matching - target variant triple that can be used. if ((Triple.getArch() == llvm::Triple::aarch64 || TVT.getArchName() == Triple.getArchName()) && TVT.getArch() == Triple.getArch() && TVT.getSubArch() == Triple.getSubArch() && TVT.getVendor() == Triple.getVendor()) { if (TargetVariantTriple) continue; A->claim(); // Accept a -target-variant triple when compiling code that may run on // macOS or Mac Catalyst. if ((Triple.isMacOSX() && TVT.getOS() == llvm::Triple::IOS && TVT.isMacCatalystEnvironment()) || (TVT.isMacOSX() && Triple.getOS() == llvm::Triple::IOS && Triple.isMacCatalystEnvironment())) { TargetVariantTriple = TVT; continue; } TheDriver.Diag(diag::err_drv_target_variant_invalid) << A->getSpelling() << A->getValue(); } } return DarwinPlatform::createFromTarget(Triple, OSVersion, Args.getLastArg(options::OPT_target), TargetVariantTriple, SDKInfo); } /// Returns the deployment target that's specified using the -mtargetos option. std::optional getDeploymentTargetFromMTargetOSArg( DerivedArgList &Args, const Driver &TheDriver, const std::optional &SDKInfo) { auto *A = Args.getLastArg(options::OPT_mtargetos_EQ); if (!A) return std::nullopt; llvm::Triple TT(llvm::Twine("unknown-apple-") + A->getValue()); switch (TT.getOS()) { case llvm::Triple::MacOSX: case llvm::Triple::IOS: case llvm::Triple::TvOS: case llvm::Triple::WatchOS: case llvm::Triple::XROS: break; default: TheDriver.Diag(diag::err_drv_invalid_os_in_arg) << TT.getOSName() << A->getAsString(Args); return std::nullopt; } VersionTuple Version = TT.getOSVersion(); if (!Version.getMajor()) { TheDriver.Diag(diag::err_drv_invalid_version_number) << A->getAsString(Args); return std::nullopt; } return DarwinPlatform::createFromMTargetOS(TT.getOS(), Version, TT.getEnvironment(), A, SDKInfo); } std::optional parseSDKSettings(llvm::vfs::FileSystem &VFS, const ArgList &Args, const Driver &TheDriver) { const Arg *A = Args.getLastArg(options::OPT_isysroot); if (!A) return std::nullopt; StringRef isysroot = A->getValue(); auto SDKInfoOrErr = parseDarwinSDKInfo(VFS, isysroot); if (!SDKInfoOrErr) { llvm::consumeError(SDKInfoOrErr.takeError()); TheDriver.Diag(diag::warn_drv_darwin_sdk_invalid_settings); return std::nullopt; } return *SDKInfoOrErr; } } // namespace void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { const OptTable &Opts = getDriver().getOpts(); // Support allowing the SDKROOT environment variable used by xcrun and other // Xcode tools to define the default sysroot, by making it the default for // isysroot. if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { // Warn if the path does not exist. if (!getVFS().exists(A->getValue())) getDriver().Diag(clang::diag::warn_missing_sysroot) << A->getValue(); } else { if (char *env = ::getenv("SDKROOT")) { // We only use this value as the default if it is an absolute path, // exists, and it is not the root path. if (llvm::sys::path::is_absolute(env) && getVFS().exists(env) && StringRef(env) != "/") { Args.append(Args.MakeSeparateArg( nullptr, Opts.getOption(options::OPT_isysroot), env)); } } } // Read the SDKSettings.json file for more information, like the SDK version // that we can pass down to the compiler. SDKInfo = parseSDKSettings(getVFS(), Args, getDriver()); // The OS and the version can be specified using the -target argument. std::optional OSTarget = getDeploymentTargetFromTargetArg(Args, getTriple(), getDriver(), SDKInfo); if (OSTarget) { // Disallow mixing -target and -mtargetos=. if (const auto *MTargetOSArg = Args.getLastArg(options::OPT_mtargetos_EQ)) { std::string TargetArgStr = OSTarget->getAsString(Args, Opts); std::string MTargetOSArgStr = MTargetOSArg->getAsString(Args); getDriver().Diag(diag::err_drv_cannot_mix_options) << TargetArgStr << MTargetOSArgStr; } std::optional OSVersionArgTarget = getDeploymentTargetFromOSVersionArg(Args, getDriver()); if (OSVersionArgTarget) { unsigned TargetMajor, TargetMinor, TargetMicro; bool TargetExtra; unsigned ArgMajor, ArgMinor, ArgMicro; bool ArgExtra; if (OSTarget->getPlatform() != OSVersionArgTarget->getPlatform() || (Driver::GetReleaseVersion(OSTarget->getOSVersion(), TargetMajor, TargetMinor, TargetMicro, TargetExtra) && Driver::GetReleaseVersion(OSVersionArgTarget->getOSVersion(), ArgMajor, ArgMinor, ArgMicro, ArgExtra) && (VersionTuple(TargetMajor, TargetMinor, TargetMicro) != VersionTuple(ArgMajor, ArgMinor, ArgMicro) || TargetExtra != ArgExtra))) { // Select the OS version from the -m-version-min argument when // the -target does not include an OS version. if (OSTarget->getPlatform() == OSVersionArgTarget->getPlatform() && !OSTarget->hasOSVersion()) { OSTarget->setOSVersion(OSVersionArgTarget->getOSVersion()); } else { // Warn about -m-version-min that doesn't match the OS version // that's specified in the target. std::string OSVersionArg = OSVersionArgTarget->getAsString(Args, Opts); std::string TargetArg = OSTarget->getAsString(Args, Opts); getDriver().Diag(clang::diag::warn_drv_overriding_option) << OSVersionArg << TargetArg; } } } } else if ((OSTarget = getDeploymentTargetFromMTargetOSArg(Args, getDriver(), SDKInfo))) { // The OS target can be specified using the -mtargetos= argument. // Disallow mixing -mtargetos= and -mversion-min=. std::optional OSVersionArgTarget = getDeploymentTargetFromOSVersionArg(Args, getDriver()); if (OSVersionArgTarget) { std::string MTargetOSArgStr = OSTarget->getAsString(Args, Opts); std::string OSVersionArgStr = OSVersionArgTarget->getAsString(Args, Opts); getDriver().Diag(diag::err_drv_cannot_mix_options) << MTargetOSArgStr << OSVersionArgStr; } } else { // The OS target can be specified using the -mversion-min argument. OSTarget = getDeploymentTargetFromOSVersionArg(Args, getDriver()); // If no deployment target was specified on the command line, check for // environment defines. if (!OSTarget) { OSTarget = getDeploymentTargetFromEnvironmentVariables(getDriver(), getTriple()); if (OSTarget) { // Don't infer simulator from the arch when the SDK is also specified. std::optional SDKTarget = inferDeploymentTargetFromSDK(Args, SDKInfo); if (SDKTarget) OSTarget->setEnvironment(SDKTarget->getEnvironment()); } } // If there is no command-line argument to specify the Target version and // no environment variable defined, see if we can set the default based // on -isysroot using SDKSettings.json if it exists. if (!OSTarget) { OSTarget = inferDeploymentTargetFromSDK(Args, SDKInfo); /// If the target was successfully constructed from the SDK path, try to /// infer the SDK info if the SDK doesn't have it. if (OSTarget && !SDKInfo) SDKInfo = OSTarget->inferSDKInfo(); } // If no OS targets have been specified, try to guess platform from -target // or arch name and compute the version from the triple. if (!OSTarget) OSTarget = inferDeploymentTargetFromArch(Args, *this, getTriple(), getDriver()); } assert(OSTarget && "Unable to infer Darwin variant"); OSTarget->addOSVersionMinArgument(Args, Opts); DarwinPlatformKind Platform = OSTarget->getPlatform(); unsigned Major, Minor, Micro; bool HadExtra; // The major version should not be over this number. const unsigned MajorVersionLimit = 1000; // Set the tool chain target information. if (Platform == MacOS) { if (!Driver::GetReleaseVersion(OSTarget->getOSVersion(), Major, Minor, Micro, HadExtra) || HadExtra || Major < 10 || Major >= MajorVersionLimit || Minor >= 100 || Micro >= 100) getDriver().Diag(diag::err_drv_invalid_version_number) << OSTarget->getAsString(Args, Opts); } else if (Platform == IPhoneOS) { if (!Driver::GetReleaseVersion(OSTarget->getOSVersion(), Major, Minor, Micro, HadExtra) || HadExtra || Major >= MajorVersionLimit || Minor >= 100 || Micro >= 100) getDriver().Diag(diag::err_drv_invalid_version_number) << OSTarget->getAsString(Args, Opts); ; if (OSTarget->getEnvironment() == MacCatalyst && (Major < 13 || (Major == 13 && Minor < 1))) { getDriver().Diag(diag::err_drv_invalid_version_number) << OSTarget->getAsString(Args, Opts); Major = 13; Minor = 1; Micro = 0; } // For 32-bit targets, the deployment target for iOS has to be earlier than // iOS 11. if (getTriple().isArch32Bit() && Major >= 11) { // If the deployment target is explicitly specified, print a diagnostic. if (OSTarget->isExplicitlySpecified()) { if (OSTarget->getEnvironment() == MacCatalyst) getDriver().Diag(diag::err_invalid_macos_32bit_deployment_target); else getDriver().Diag(diag::warn_invalid_ios_deployment_target) << OSTarget->getAsString(Args, Opts); // Otherwise, set it to 10.99.99. } else { Major = 10; Minor = 99; Micro = 99; } } } else if (Platform == TvOS) { if (!Driver::GetReleaseVersion(OSTarget->getOSVersion(), Major, Minor, Micro, HadExtra) || HadExtra || Major >= MajorVersionLimit || Minor >= 100 || Micro >= 100) getDriver().Diag(diag::err_drv_invalid_version_number) << OSTarget->getAsString(Args, Opts); } else if (Platform == WatchOS) { if (!Driver::GetReleaseVersion(OSTarget->getOSVersion(), Major, Minor, Micro, HadExtra) || HadExtra || Major >= MajorVersionLimit || Minor >= 100 || Micro >= 100) getDriver().Diag(diag::err_drv_invalid_version_number) << OSTarget->getAsString(Args, Opts); } else if (Platform == DriverKit) { if (!Driver::GetReleaseVersion(OSTarget->getOSVersion(), Major, Minor, Micro, HadExtra) || HadExtra || Major < 19 || Major >= MajorVersionLimit || Minor >= 100 || Micro >= 100) getDriver().Diag(diag::err_drv_invalid_version_number) << OSTarget->getAsString(Args, Opts); } else if (Platform == XROS) { if (!Driver::GetReleaseVersion(OSTarget->getOSVersion(), Major, Minor, Micro, HadExtra) || HadExtra || Major < 1 || Major >= MajorVersionLimit || Minor >= 100 || Micro >= 100) getDriver().Diag(diag::err_drv_invalid_version_number) << OSTarget->getAsString(Args, Opts); } else llvm_unreachable("unknown kind of Darwin platform"); DarwinEnvironmentKind Environment = OSTarget->getEnvironment(); // Recognize iOS targets with an x86 architecture as the iOS simulator. if (Environment == NativeEnvironment && Platform != MacOS && Platform != DriverKit && OSTarget->canInferSimulatorFromArch() && getTriple().isX86()) Environment = Simulator; VersionTuple NativeTargetVersion; if (Environment == MacCatalyst) NativeTargetVersion = OSTarget->getNativeTargetVersion(); setTarget(Platform, Environment, Major, Minor, Micro, NativeTargetVersion); TargetVariantTriple = OSTarget->getTargetVariantTriple(); if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { StringRef SDK = getSDKName(A->getValue()); if (SDK.size() > 0) { size_t StartVer = SDK.find_first_of("0123456789"); StringRef SDKName = SDK.slice(0, StartVer); if (!SDKName.starts_with(getPlatformFamily()) && !dropSDKNamePrefix(SDKName).starts_with(getPlatformFamily())) getDriver().Diag(diag::warn_incompatible_sysroot) << SDKName << getPlatformFamily(); } } } // For certain platforms/environments almost all resources (e.g., headers) are // located in sub-directories, e.g., for DriverKit they live in // /System/DriverKit/usr/include (instead of /usr/include). static void AppendPlatformPrefix(SmallString<128> &Path, const llvm::Triple &T) { if (T.isDriverKit()) { llvm::sys::path::append(Path, "System", "DriverKit"); } } // Returns the effective sysroot from either -isysroot or --sysroot, plus the // platform prefix (if any). llvm::SmallString<128> DarwinClang::GetEffectiveSysroot(const llvm::opt::ArgList &DriverArgs) const { llvm::SmallString<128> Path("/"); if (DriverArgs.hasArg(options::OPT_isysroot)) Path = DriverArgs.getLastArgValue(options::OPT_isysroot); else if (!getDriver().SysRoot.empty()) Path = getDriver().SysRoot; if (hasEffectiveTriple()) { AppendPlatformPrefix(Path, getEffectiveTriple()); } return Path; } void DarwinClang::AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args) const { const Driver &D = getDriver(); llvm::SmallString<128> Sysroot = GetEffectiveSysroot(DriverArgs); bool NoStdInc = DriverArgs.hasArg(options::OPT_nostdinc); bool NoStdlibInc = DriverArgs.hasArg(options::OPT_nostdlibinc); bool NoBuiltinInc = DriverArgs.hasFlag( options::OPT_nobuiltininc, options::OPT_ibuiltininc, /*Default=*/false); bool ForceBuiltinInc = DriverArgs.hasFlag( options::OPT_ibuiltininc, options::OPT_nobuiltininc, /*Default=*/false); // Add /usr/local/include if (!NoStdInc && !NoStdlibInc) { SmallString<128> P(Sysroot); llvm::sys::path::append(P, "usr", "local", "include"); addSystemInclude(DriverArgs, CC1Args, P); } // Add the Clang builtin headers (/include) if (!(NoStdInc && !ForceBuiltinInc) && !NoBuiltinInc) { SmallString<128> P(D.ResourceDir); llvm::sys::path::append(P, "include"); addSystemInclude(DriverArgs, CC1Args, P); } if (NoStdInc || NoStdlibInc) return; // Check for configure-time C include directories. llvm::StringRef CIncludeDirs(C_INCLUDE_DIRS); if (!CIncludeDirs.empty()) { llvm::SmallVector dirs; CIncludeDirs.split(dirs, ":"); for (llvm::StringRef dir : dirs) { llvm::StringRef Prefix = llvm::sys::path::is_absolute(dir) ? "" : llvm::StringRef(Sysroot); addExternCSystemInclude(DriverArgs, CC1Args, Prefix + dir); } } else { // Otherwise, add /usr/include. SmallString<128> P(Sysroot); llvm::sys::path::append(P, "usr", "include"); addExternCSystemInclude(DriverArgs, CC1Args, P.str()); } } bool DarwinClang::AddGnuCPlusPlusIncludePaths(const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, llvm::SmallString<128> Base, llvm::StringRef Version, llvm::StringRef ArchDir, llvm::StringRef BitDir) const { llvm::sys::path::append(Base, Version); // Add the base dir addSystemInclude(DriverArgs, CC1Args, Base); // Add the multilib dirs { llvm::SmallString<128> P = Base; if (!ArchDir.empty()) llvm::sys::path::append(P, ArchDir); if (!BitDir.empty()) llvm::sys::path::append(P, BitDir); addSystemInclude(DriverArgs, CC1Args, P); } // Add the backward dir { llvm::SmallString<128> P = Base; llvm::sys::path::append(P, "backward"); addSystemInclude(DriverArgs, CC1Args, P); } return getVFS().exists(Base); } void DarwinClang::AddClangCXXStdlibIncludeArgs( const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args) const { // The implementation from a base class will pass through the -stdlib to // CC1Args. // FIXME: this should not be necessary, remove usages in the frontend // (e.g. HeaderSearchOptions::UseLibcxx) and don't pipe -stdlib. // Also check whether this is used for setting library search paths. ToolChain::AddClangCXXStdlibIncludeArgs(DriverArgs, CC1Args); if (DriverArgs.hasArg(options::OPT_nostdinc, options::OPT_nostdlibinc, options::OPT_nostdincxx)) return; llvm::SmallString<128> Sysroot = GetEffectiveSysroot(DriverArgs); switch (GetCXXStdlibType(DriverArgs)) { case ToolChain::CST_Libcxx: { // On Darwin, libc++ can be installed in one of the following places: // 1. Alongside the compiler in /include/c++/v1 // 2. Alongside the compiler in /../include/c++/v1 // 3. In a SDK (or a custom sysroot) in /usr/include/c++/v1 // // The precedence of paths is as listed above, i.e. we take the first path // that exists. Note that we never include libc++ twice -- we take the first // path that exists and don't send the other paths to CC1 (otherwise // include_next could break). // // Also note that in most cases, (1) and (2) are exactly the same path. // Those two paths will differ only when the `clang` program being run // is actually a symlink to the real executable. // Check for (1) // Get from '/bin' to '/include/c++/v1'. // Note that InstallBin can be relative, so we use '..' instead of // parent_path. llvm::SmallString<128> InstallBin = llvm::StringRef(getDriver().getInstalledDir()); // /bin llvm::sys::path::append(InstallBin, "..", "include", "c++", "v1"); if (getVFS().exists(InstallBin)) { addSystemInclude(DriverArgs, CC1Args, InstallBin); return; } else if (DriverArgs.hasArg(options::OPT_v)) { llvm::errs() << "ignoring nonexistent directory \"" << InstallBin << "\"\n"; } // (2) Check for the folder where the executable is located, if different. if (getDriver().getInstalledDir() != getDriver().Dir) { InstallBin = llvm::StringRef(getDriver().Dir); llvm::sys::path::append(InstallBin, "..", "include", "c++", "v1"); if (getVFS().exists(InstallBin)) { addSystemInclude(DriverArgs, CC1Args, InstallBin); return; } else if (DriverArgs.hasArg(options::OPT_v)) { llvm::errs() << "ignoring nonexistent directory \"" << InstallBin << "\"\n"; } } // Otherwise, check for (3) llvm::SmallString<128> SysrootUsr = Sysroot; llvm::sys::path::append(SysrootUsr, "usr", "include", "c++", "v1"); if (getVFS().exists(SysrootUsr)) { addSystemInclude(DriverArgs, CC1Args, SysrootUsr); return; } else if (DriverArgs.hasArg(options::OPT_v)) { llvm::errs() << "ignoring nonexistent directory \"" << SysrootUsr << "\"\n"; } // Otherwise, don't add any path. break; } case ToolChain::CST_Libstdcxx: llvm::SmallString<128> UsrIncludeCxx = Sysroot; llvm::sys::path::append(UsrIncludeCxx, "usr", "include", "c++"); llvm::Triple::ArchType arch = getTriple().getArch(); bool IsBaseFound = true; switch (arch) { default: break; case llvm::Triple::x86: case llvm::Triple::x86_64: IsBaseFound = AddGnuCPlusPlusIncludePaths(DriverArgs, CC1Args, UsrIncludeCxx, "4.2.1", "i686-apple-darwin10", arch == llvm::Triple::x86_64 ? "x86_64" : ""); IsBaseFound |= AddGnuCPlusPlusIncludePaths(DriverArgs, CC1Args, UsrIncludeCxx, "4.0.0", "i686-apple-darwin8", ""); break; case llvm::Triple::arm: case llvm::Triple::thumb: IsBaseFound = AddGnuCPlusPlusIncludePaths(DriverArgs, CC1Args, UsrIncludeCxx, "4.2.1", "arm-apple-darwin10", "v7"); IsBaseFound |= AddGnuCPlusPlusIncludePaths(DriverArgs, CC1Args, UsrIncludeCxx, "4.2.1", "arm-apple-darwin10", "v6"); break; case llvm::Triple::aarch64: IsBaseFound = AddGnuCPlusPlusIncludePaths(DriverArgs, CC1Args, UsrIncludeCxx, "4.2.1", "arm64-apple-darwin10", ""); break; } if (!IsBaseFound) { getDriver().Diag(diag::warn_drv_libstdcxx_not_found); } break; } } void DarwinClang::AddCXXStdlibLibArgs(const ArgList &Args, ArgStringList &CmdArgs) const { CXXStdlibType Type = GetCXXStdlibType(Args); switch (Type) { case ToolChain::CST_Libcxx: CmdArgs.push_back("-lc++"); if (Args.hasArg(options::OPT_fexperimental_library)) CmdArgs.push_back("-lc++experimental"); break; case ToolChain::CST_Libstdcxx: // Unfortunately, -lstdc++ doesn't always exist in the standard search path; // it was previously found in the gcc lib dir. However, for all the Darwin // platforms we care about it was -lstdc++.6, so we search for that // explicitly if we can't see an obvious -lstdc++ candidate. // Check in the sysroot first. if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { SmallString<128> P(A->getValue()); llvm::sys::path::append(P, "usr", "lib", "libstdc++.dylib"); if (!getVFS().exists(P)) { llvm::sys::path::remove_filename(P); llvm::sys::path::append(P, "libstdc++.6.dylib"); if (getVFS().exists(P)) { CmdArgs.push_back(Args.MakeArgString(P)); return; } } } // Otherwise, look in the root. // FIXME: This should be removed someday when we don't have to care about // 10.6 and earlier, where /usr/lib/libstdc++.dylib does not exist. if (!getVFS().exists("/usr/lib/libstdc++.dylib") && getVFS().exists("/usr/lib/libstdc++.6.dylib")) { CmdArgs.push_back("/usr/lib/libstdc++.6.dylib"); return; } // Otherwise, let the linker search. CmdArgs.push_back("-lstdc++"); break; } } void DarwinClang::AddCCKextLibArgs(const ArgList &Args, ArgStringList &CmdArgs) const { // For Darwin platforms, use the compiler-rt-based support library // instead of the gcc-provided one (which is also incidentally // only present in the gcc lib dir, which makes it hard to find). SmallString<128> P(getDriver().ResourceDir); llvm::sys::path::append(P, "lib", "darwin"); // Use the newer cc_kext for iOS ARM after 6.0. if (isTargetWatchOS()) { llvm::sys::path::append(P, "libclang_rt.cc_kext_watchos.a"); } else if (isTargetTvOS()) { llvm::sys::path::append(P, "libclang_rt.cc_kext_tvos.a"); } else if (isTargetIPhoneOS()) { llvm::sys::path::append(P, "libclang_rt.cc_kext_ios.a"); } else if (isTargetDriverKit()) { // DriverKit doesn't want extra runtime support. } else if (isTargetXROSDevice()) { llvm::sys::path::append( P, llvm::Twine("libclang_rt.cc_kext_") + llvm::Triple::getOSTypeName(llvm::Triple::XROS) + ".a"); } else { llvm::sys::path::append(P, "libclang_rt.cc_kext.a"); } // For now, allow missing resource libraries to support developers who may // not have compiler-rt checked out or integrated into their build. if (getVFS().exists(P)) CmdArgs.push_back(Args.MakeArgString(P)); } DerivedArgList *MachO::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch, Action::OffloadKind) const { DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs()); const OptTable &Opts = getDriver().getOpts(); // FIXME: We really want to get out of the tool chain level argument // translation business, as it makes the driver functionality much // more opaque. For now, we follow gcc closely solely for the // purpose of easily achieving feature parity & testability. Once we // have something that works, we should reevaluate each translation // and try to push it down into tool specific logic. for (Arg *A : Args) { if (A->getOption().matches(options::OPT_Xarch__)) { // Skip this argument unless the architecture matches either the toolchain // triple arch, or the arch being bound. StringRef XarchArch = A->getValue(0); if (!(XarchArch == getArchName() || (!BoundArch.empty() && XarchArch == BoundArch))) continue; Arg *OriginalArg = A; TranslateXarchArgs(Args, A, DAL); // Linker input arguments require custom handling. The problem is that we // have already constructed the phase actions, so we can not treat them as // "input arguments". if (A->getOption().hasFlag(options::LinkerInput)) { // Convert the argument into individual Zlinker_input_args. for (const char *Value : A->getValues()) { DAL->AddSeparateArg( OriginalArg, Opts.getOption(options::OPT_Zlinker_input), Value); } continue; } } // Sob. These is strictly gcc compatible for the time being. Apple // gcc translates options twice, which means that self-expanding // options add duplicates. switch ((options::ID)A->getOption().getID()) { default: DAL->append(A); break; case options::OPT_mkernel: case options::OPT_fapple_kext: DAL->append(A); DAL->AddFlagArg(A, Opts.getOption(options::OPT_static)); break; case options::OPT_dependency_file: DAL->AddSeparateArg(A, Opts.getOption(options::OPT_MF), A->getValue()); break; case options::OPT_gfull: DAL->AddFlagArg(A, Opts.getOption(options::OPT_g_Flag)); DAL->AddFlagArg( A, Opts.getOption(options::OPT_fno_eliminate_unused_debug_symbols)); break; case options::OPT_gused: DAL->AddFlagArg(A, Opts.getOption(options::OPT_g_Flag)); DAL->AddFlagArg( A, Opts.getOption(options::OPT_feliminate_unused_debug_symbols)); break; case options::OPT_shared: DAL->AddFlagArg(A, Opts.getOption(options::OPT_dynamiclib)); break; case options::OPT_fconstant_cfstrings: DAL->AddFlagArg(A, Opts.getOption(options::OPT_mconstant_cfstrings)); break; case options::OPT_fno_constant_cfstrings: DAL->AddFlagArg(A, Opts.getOption(options::OPT_mno_constant_cfstrings)); break; case options::OPT_Wnonportable_cfstrings: DAL->AddFlagArg(A, Opts.getOption(options::OPT_mwarn_nonportable_cfstrings)); break; case options::OPT_Wno_nonportable_cfstrings: DAL->AddFlagArg( A, Opts.getOption(options::OPT_mno_warn_nonportable_cfstrings)); break; } } // Add the arch options based on the particular spelling of -arch, to match // how the driver works. if (!BoundArch.empty()) { StringRef Name = BoundArch; const Option MCpu = Opts.getOption(options::OPT_mcpu_EQ); const Option MArch = Opts.getOption(clang::driver::options::OPT_march_EQ); // This code must be kept in sync with LLVM's getArchTypeForDarwinArch, // which defines the list of which architectures we accept. if (Name == "ppc") ; else if (Name == "ppc601") DAL->AddJoinedArg(nullptr, MCpu, "601"); else if (Name == "ppc603") DAL->AddJoinedArg(nullptr, MCpu, "603"); else if (Name == "ppc604") DAL->AddJoinedArg(nullptr, MCpu, "604"); else if (Name == "ppc604e") DAL->AddJoinedArg(nullptr, MCpu, "604e"); else if (Name == "ppc750") DAL->AddJoinedArg(nullptr, MCpu, "750"); else if (Name == "ppc7400") DAL->AddJoinedArg(nullptr, MCpu, "7400"); else if (Name == "ppc7450") DAL->AddJoinedArg(nullptr, MCpu, "7450"); else if (Name == "ppc970") DAL->AddJoinedArg(nullptr, MCpu, "970"); else if (Name == "ppc64" || Name == "ppc64le") DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_m64)); else if (Name == "i386") ; else if (Name == "i486") DAL->AddJoinedArg(nullptr, MArch, "i486"); else if (Name == "i586") DAL->AddJoinedArg(nullptr, MArch, "i586"); else if (Name == "i686") DAL->AddJoinedArg(nullptr, MArch, "i686"); else if (Name == "pentium") DAL->AddJoinedArg(nullptr, MArch, "pentium"); else if (Name == "pentium2") DAL->AddJoinedArg(nullptr, MArch, "pentium2"); else if (Name == "pentpro") DAL->AddJoinedArg(nullptr, MArch, "pentiumpro"); else if (Name == "pentIIm3") DAL->AddJoinedArg(nullptr, MArch, "pentium2"); else if (Name == "x86_64" || Name == "x86_64h") DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_m64)); else if (Name == "arm") DAL->AddJoinedArg(nullptr, MArch, "armv4t"); else if (Name == "armv4t") DAL->AddJoinedArg(nullptr, MArch, "armv4t"); else if (Name == "armv5") DAL->AddJoinedArg(nullptr, MArch, "armv5tej"); else if (Name == "xscale") DAL->AddJoinedArg(nullptr, MArch, "xscale"); else if (Name == "armv6") DAL->AddJoinedArg(nullptr, MArch, "armv6k"); else if (Name == "armv6m") DAL->AddJoinedArg(nullptr, MArch, "armv6m"); else if (Name == "armv7") DAL->AddJoinedArg(nullptr, MArch, "armv7a"); else if (Name == "armv7em") DAL->AddJoinedArg(nullptr, MArch, "armv7em"); else if (Name == "armv7k") DAL->AddJoinedArg(nullptr, MArch, "armv7k"); else if (Name == "armv7m") DAL->AddJoinedArg(nullptr, MArch, "armv7m"); else if (Name == "armv7s") DAL->AddJoinedArg(nullptr, MArch, "armv7s"); } return DAL; } void MachO::AddLinkRuntimeLibArgs(const ArgList &Args, ArgStringList &CmdArgs, bool ForceLinkBuiltinRT) const { // Embedded targets are simple at the moment, not supporting sanitizers and // with different libraries for each member of the product { static, PIC } x // { hard-float, soft-float } llvm::SmallString<32> CompilerRT = StringRef(""); CompilerRT += (tools::arm::getARMFloatABI(*this, Args) == tools::arm::FloatABI::Hard) ? "hard" : "soft"; CompilerRT += Args.hasArg(options::OPT_fPIC) ? "_pic" : "_static"; AddLinkRuntimeLib(Args, CmdArgs, CompilerRT, RLO_IsEmbedded); } bool Darwin::isAlignedAllocationUnavailable() const { llvm::Triple::OSType OS; if (isTargetMacCatalyst()) return TargetVersion < alignedAllocMinVersion(llvm::Triple::MacOSX); switch (TargetPlatform) { case MacOS: // Earlier than 10.13. OS = llvm::Triple::MacOSX; break; case IPhoneOS: OS = llvm::Triple::IOS; break; case TvOS: // Earlier than 11.0. OS = llvm::Triple::TvOS; break; case WatchOS: // Earlier than 4.0. OS = llvm::Triple::WatchOS; break; case XROS: // Always available. return false; case DriverKit: // Always available. return false; } return TargetVersion < alignedAllocMinVersion(OS); } static bool sdkSupportsBuiltinModules(const Darwin::DarwinPlatformKind &TargetPlatform, const std::optional &SDKInfo) { if (!SDKInfo) return false; VersionTuple SDKVersion = SDKInfo->getVersion(); switch (TargetPlatform) { case Darwin::MacOS: return SDKVersion >= VersionTuple(99U); case Darwin::IPhoneOS: return SDKVersion >= VersionTuple(99U); case Darwin::TvOS: return SDKVersion >= VersionTuple(99U); case Darwin::WatchOS: return SDKVersion >= VersionTuple(99U); case Darwin::XROS: return SDKVersion >= VersionTuple(99U); default: return true; } } void Darwin::addClangTargetOptions(const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, Action::OffloadKind DeviceOffloadKind) const { // Pass "-faligned-alloc-unavailable" only when the user hasn't manually // enabled or disabled aligned allocations. if (!DriverArgs.hasArgNoClaim(options::OPT_faligned_allocation, options::OPT_fno_aligned_allocation) && isAlignedAllocationUnavailable()) CC1Args.push_back("-faligned-alloc-unavailable"); addClangCC1ASTargetOptions(DriverArgs, CC1Args); // Enable compatibility mode for NSItemProviderCompletionHandler in // Foundation/NSItemProvider.h. CC1Args.push_back("-fcompatibility-qualified-id-block-type-checking"); // Give static local variables in inline functions hidden visibility when // -fvisibility-inlines-hidden is enabled. if (!DriverArgs.getLastArgNoClaim( options::OPT_fvisibility_inlines_hidden_static_local_var, options::OPT_fno_visibility_inlines_hidden_static_local_var)) CC1Args.push_back("-fvisibility-inlines-hidden-static-local-var"); // Earlier versions of the darwin SDK have the C standard library headers // all together in the Darwin module. That leads to module cycles with // the _Builtin_ modules. e.g. on darwin includes . // The builtin include-nexts . When both of those // darwin headers are in the Darwin module, there's a module cycle Darwin -> // _Builtin_stdint -> Darwin (i.e. inttypes.h (darwin) -> stdint.h (builtin) -> // stdint.h (darwin)). This is fixed in later versions of the darwin SDK, // but until then, the builtin headers need to join the system modules. // i.e. when the builtin stdint.h is in the Darwin module too, the cycle // goes away. Note that -fbuiltin-headers-in-system-modules does nothing // to fix the same problem with C++ headers, and is generally fragile. if (!sdkSupportsBuiltinModules(TargetPlatform, SDKInfo)) CC1Args.push_back("-fbuiltin-headers-in-system-modules"); if (!DriverArgs.hasArgNoClaim(options::OPT_fdefine_target_os_macros, options::OPT_fno_define_target_os_macros)) CC1Args.push_back("-fdefine-target-os-macros"); } void Darwin::addClangCC1ASTargetOptions( const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CC1ASArgs) const { if (TargetVariantTriple) { CC1ASArgs.push_back("-darwin-target-variant-triple"); CC1ASArgs.push_back(Args.MakeArgString(TargetVariantTriple->getTriple())); } if (SDKInfo) { /// Pass the SDK version to the compiler when the SDK information is /// available. auto EmitTargetSDKVersionArg = [&](const VersionTuple &V) { std::string Arg; llvm::raw_string_ostream OS(Arg); OS << "-target-sdk-version=" << V; CC1ASArgs.push_back(Args.MakeArgString(OS.str())); }; if (isTargetMacCatalyst()) { if (const auto *MacOStoMacCatalystMapping = SDKInfo->getVersionMapping( DarwinSDKInfo::OSEnvPair::macOStoMacCatalystPair())) { std::optional SDKVersion = MacOStoMacCatalystMapping->map( SDKInfo->getVersion(), minimumMacCatalystDeploymentTarget(), std::nullopt); EmitTargetSDKVersionArg( SDKVersion ? *SDKVersion : minimumMacCatalystDeploymentTarget()); } } else { EmitTargetSDKVersionArg(SDKInfo->getVersion()); } /// Pass the target variant SDK version to the compiler when the SDK /// information is available and is required for target variant. if (TargetVariantTriple) { if (isTargetMacCatalyst()) { std::string Arg; llvm::raw_string_ostream OS(Arg); OS << "-darwin-target-variant-sdk-version=" << SDKInfo->getVersion(); CC1ASArgs.push_back(Args.MakeArgString(OS.str())); } else if (const auto *MacOStoMacCatalystMapping = SDKInfo->getVersionMapping( DarwinSDKInfo::OSEnvPair::macOStoMacCatalystPair())) { if (std::optional SDKVersion = MacOStoMacCatalystMapping->map( SDKInfo->getVersion(), minimumMacCatalystDeploymentTarget(), std::nullopt)) { std::string Arg; llvm::raw_string_ostream OS(Arg); OS << "-darwin-target-variant-sdk-version=" << *SDKVersion; CC1ASArgs.push_back(Args.MakeArgString(OS.str())); } } } } } DerivedArgList * Darwin::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch, Action::OffloadKind DeviceOffloadKind) const { // First get the generic Apple args, before moving onto Darwin-specific ones. DerivedArgList *DAL = MachO::TranslateArgs(Args, BoundArch, DeviceOffloadKind); // If no architecture is bound, none of the translations here are relevant. if (BoundArch.empty()) return DAL; // Add an explicit version min argument for the deployment target. We do this // after argument translation because -Xarch_ arguments may add a version min // argument. AddDeploymentTarget(*DAL); // For iOS 6, undo the translation to add -static for -mkernel/-fapple-kext. // FIXME: It would be far better to avoid inserting those -static arguments, // but we can't check the deployment target in the translation code until // it is set here. if (isTargetWatchOSBased() || isTargetDriverKit() || isTargetXROS() || (isTargetIOSBased() && !isIPhoneOSVersionLT(6, 0))) { for (ArgList::iterator it = DAL->begin(), ie = DAL->end(); it != ie; ) { Arg *A = *it; ++it; if (A->getOption().getID() != options::OPT_mkernel && A->getOption().getID() != options::OPT_fapple_kext) continue; assert(it != ie && "unexpected argument translation"); A = *it; assert(A->getOption().getID() == options::OPT_static && "missing expected -static argument"); *it = nullptr; ++it; } } auto Arch = tools::darwin::getArchTypeForMachOArchName(BoundArch); if ((Arch == llvm::Triple::arm || Arch == llvm::Triple::thumb)) { if (Args.hasFlag(options::OPT_fomit_frame_pointer, options::OPT_fno_omit_frame_pointer, false)) getDriver().Diag(clang::diag::warn_drv_unsupported_opt_for_target) << "-fomit-frame-pointer" << BoundArch; } return DAL; } ToolChain::UnwindTableLevel MachO::getDefaultUnwindTableLevel(const ArgList &Args) const { // Unwind tables are not emitted if -fno-exceptions is supplied (except when // targeting x86_64). if (getArch() == llvm::Triple::x86_64 || (GetExceptionModel(Args) != llvm::ExceptionHandling::SjLj && Args.hasFlag(options::OPT_fexceptions, options::OPT_fno_exceptions, true))) return (getArch() == llvm::Triple::aarch64 || getArch() == llvm::Triple::aarch64_32) ? UnwindTableLevel::Synchronous : UnwindTableLevel::Asynchronous; return UnwindTableLevel::None; } bool MachO::UseDwarfDebugFlags() const { if (const char *S = ::getenv("RC_DEBUG_OPTIONS")) return S[0] != '\0'; return false; } std::string MachO::GetGlobalDebugPathRemapping() const { if (const char *S = ::getenv("RC_DEBUG_PREFIX_MAP")) return S; return {}; } llvm::ExceptionHandling Darwin::GetExceptionModel(const ArgList &Args) const { // Darwin uses SjLj exceptions on ARM. if (getTriple().getArch() != llvm::Triple::arm && getTriple().getArch() != llvm::Triple::thumb) return llvm::ExceptionHandling::None; // Only watchOS uses the new DWARF/Compact unwinding method. llvm::Triple Triple(ComputeLLVMTriple(Args)); if (Triple.isWatchABI()) return llvm::ExceptionHandling::DwarfCFI; return llvm::ExceptionHandling::SjLj; } bool Darwin::SupportsEmbeddedBitcode() const { assert(TargetInitialized && "Target not initialized!"); if (isTargetIPhoneOS() && isIPhoneOSVersionLT(6, 0)) return false; return true; } bool MachO::isPICDefault() const { return true; } bool MachO::isPIEDefault(const llvm::opt::ArgList &Args) const { return false; } bool MachO::isPICDefaultForced() const { return (getArch() == llvm::Triple::x86_64 || getArch() == llvm::Triple::aarch64); } bool MachO::SupportsProfiling() const { // Profiling instrumentation is only supported on x86. return getTriple().isX86(); } void Darwin::addMinVersionArgs(const ArgList &Args, ArgStringList &CmdArgs) const { VersionTuple TargetVersion = getTripleTargetVersion(); assert(!isTargetXROS() && "xrOS always uses -platform-version"); if (isTargetWatchOS()) CmdArgs.push_back("-watchos_version_min"); else if (isTargetWatchOSSimulator()) CmdArgs.push_back("-watchos_simulator_version_min"); else if (isTargetTvOS()) CmdArgs.push_back("-tvos_version_min"); else if (isTargetTvOSSimulator()) CmdArgs.push_back("-tvos_simulator_version_min"); else if (isTargetDriverKit()) CmdArgs.push_back("-driverkit_version_min"); else if (isTargetIOSSimulator()) CmdArgs.push_back("-ios_simulator_version_min"); else if (isTargetIOSBased()) CmdArgs.push_back("-iphoneos_version_min"); else if (isTargetMacCatalyst()) CmdArgs.push_back("-maccatalyst_version_min"); else { assert(isTargetMacOS() && "unexpected target"); CmdArgs.push_back("-macosx_version_min"); } VersionTuple MinTgtVers = getEffectiveTriple().getMinimumSupportedOSVersion(); if (!MinTgtVers.empty() && MinTgtVers > TargetVersion) TargetVersion = MinTgtVers; CmdArgs.push_back(Args.MakeArgString(TargetVersion.getAsString())); if (TargetVariantTriple) { assert(isTargetMacOSBased() && "unexpected target"); VersionTuple VariantTargetVersion; if (TargetVariantTriple->isMacOSX()) { CmdArgs.push_back("-macosx_version_min"); TargetVariantTriple->getMacOSXVersion(VariantTargetVersion); } else { assert(TargetVariantTriple->isiOS() && TargetVariantTriple->isMacCatalystEnvironment() && "unexpected target variant triple"); CmdArgs.push_back("-maccatalyst_version_min"); VariantTargetVersion = TargetVariantTriple->getiOSVersion(); } VersionTuple MinTgtVers = TargetVariantTriple->getMinimumSupportedOSVersion(); if (MinTgtVers.getMajor() && MinTgtVers > VariantTargetVersion) VariantTargetVersion = MinTgtVers; CmdArgs.push_back(Args.MakeArgString(VariantTargetVersion.getAsString())); } } static const char *getPlatformName(Darwin::DarwinPlatformKind Platform, Darwin::DarwinEnvironmentKind Environment) { switch (Platform) { case Darwin::MacOS: return "macos"; case Darwin::IPhoneOS: if (Environment == Darwin::MacCatalyst) return "mac catalyst"; return "ios"; case Darwin::TvOS: return "tvos"; case Darwin::WatchOS: return "watchos"; case Darwin::XROS: return "xros"; case Darwin::DriverKit: return "driverkit"; } llvm_unreachable("invalid platform"); } void Darwin::addPlatformVersionArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const { auto EmitPlatformVersionArg = [&](const VersionTuple &TV, Darwin::DarwinPlatformKind TargetPlatform, Darwin::DarwinEnvironmentKind TargetEnvironment, const llvm::Triple &TT) { // -platform_version // Both the target and SDK version support only up to 3 components. CmdArgs.push_back("-platform_version"); std::string PlatformName = getPlatformName(TargetPlatform, TargetEnvironment); if (TargetEnvironment == Darwin::Simulator) PlatformName += "-simulator"; CmdArgs.push_back(Args.MakeArgString(PlatformName)); VersionTuple TargetVersion = TV.withoutBuild(); if ((TargetPlatform == Darwin::IPhoneOS || TargetPlatform == Darwin::TvOS) && getTriple().getArchName() == "arm64e" && TargetVersion.getMajor() < 14) { // arm64e slice is supported on iOS/tvOS 14+ only. TargetVersion = VersionTuple(14, 0); } VersionTuple MinTgtVers = TT.getMinimumSupportedOSVersion(); if (!MinTgtVers.empty() && MinTgtVers > TargetVersion) TargetVersion = MinTgtVers; CmdArgs.push_back(Args.MakeArgString(TargetVersion.getAsString())); if (TargetPlatform == IPhoneOS && TargetEnvironment == MacCatalyst) { // Mac Catalyst programs must use the appropriate iOS SDK version // that corresponds to the macOS SDK version used for the compilation. std::optional iOSSDKVersion; if (SDKInfo) { if (const auto *MacOStoMacCatalystMapping = SDKInfo->getVersionMapping( DarwinSDKInfo::OSEnvPair::macOStoMacCatalystPair())) { iOSSDKVersion = MacOStoMacCatalystMapping->map( SDKInfo->getVersion().withoutBuild(), minimumMacCatalystDeploymentTarget(), std::nullopt); } } CmdArgs.push_back(Args.MakeArgString( (iOSSDKVersion ? *iOSSDKVersion : minimumMacCatalystDeploymentTarget()) .getAsString())); return; } if (SDKInfo) { VersionTuple SDKVersion = SDKInfo->getVersion().withoutBuild(); if (!SDKVersion.getMinor()) SDKVersion = VersionTuple(SDKVersion.getMajor(), 0); CmdArgs.push_back(Args.MakeArgString(SDKVersion.getAsString())); } else { // Use an SDK version that's matching the deployment target if the SDK // version is missing. This is preferred over an empty SDK version // (0.0.0) as the system's runtime might expect the linked binary to // contain a valid SDK version in order for the binary to work // correctly. It's reasonable to use the deployment target version as // a proxy for the SDK version because older SDKs don't guarantee // support for deployment targets newer than the SDK versions, so that // rules out using some predetermined older SDK version, which leaves // the deployment target version as the only reasonable choice. CmdArgs.push_back(Args.MakeArgString(TargetVersion.getAsString())); } }; EmitPlatformVersionArg(getTripleTargetVersion(), TargetPlatform, TargetEnvironment, getEffectiveTriple()); if (!TargetVariantTriple) return; Darwin::DarwinPlatformKind Platform; Darwin::DarwinEnvironmentKind Environment; VersionTuple TargetVariantVersion; if (TargetVariantTriple->isMacOSX()) { TargetVariantTriple->getMacOSXVersion(TargetVariantVersion); Platform = Darwin::MacOS; Environment = Darwin::NativeEnvironment; } else { assert(TargetVariantTriple->isiOS() && TargetVariantTriple->isMacCatalystEnvironment() && "unexpected target variant triple"); TargetVariantVersion = TargetVariantTriple->getiOSVersion(); Platform = Darwin::IPhoneOS; Environment = Darwin::MacCatalyst; } EmitPlatformVersionArg(TargetVariantVersion, Platform, Environment, *TargetVariantTriple); } // Add additional link args for the -dynamiclib option. static void addDynamicLibLinkArgs(const Darwin &D, const ArgList &Args, ArgStringList &CmdArgs) { // Derived from darwin_dylib1 spec. if (D.isTargetIPhoneOS()) { if (D.isIPhoneOSVersionLT(3, 1)) CmdArgs.push_back("-ldylib1.o"); return; } if (!D.isTargetMacOS()) return; if (D.isMacosxVersionLT(10, 5)) CmdArgs.push_back("-ldylib1.o"); else if (D.isMacosxVersionLT(10, 6)) CmdArgs.push_back("-ldylib1.10.5.o"); } // Add additional link args for the -bundle option. static void addBundleLinkArgs(const Darwin &D, const ArgList &Args, ArgStringList &CmdArgs) { if (Args.hasArg(options::OPT_static)) return; // Derived from darwin_bundle1 spec. if ((D.isTargetIPhoneOS() && D.isIPhoneOSVersionLT(3, 1)) || (D.isTargetMacOS() && D.isMacosxVersionLT(10, 6))) CmdArgs.push_back("-lbundle1.o"); } // Add additional link args for the -pg option. static void addPgProfilingLinkArgs(const Darwin &D, const ArgList &Args, ArgStringList &CmdArgs) { if (D.isTargetMacOS() && D.isMacosxVersionLT(10, 9)) { if (Args.hasArg(options::OPT_static) || Args.hasArg(options::OPT_object) || Args.hasArg(options::OPT_preload)) { CmdArgs.push_back("-lgcrt0.o"); } else { CmdArgs.push_back("-lgcrt1.o"); // darwin_crt2 spec is empty. } // By default on OS X 10.8 and later, we don't link with a crt1.o // file and the linker knows to use _main as the entry point. But, // when compiling with -pg, we need to link with the gcrt1.o file, // so pass the -no_new_main option to tell the linker to use the // "start" symbol as the entry point. if (!D.isMacosxVersionLT(10, 8)) CmdArgs.push_back("-no_new_main"); } else { D.getDriver().Diag(diag::err_drv_clang_unsupported_opt_pg_darwin) << D.isTargetMacOSBased(); } } static void addDefaultCRTLinkArgs(const Darwin &D, const ArgList &Args, ArgStringList &CmdArgs) { // Derived from darwin_crt1 spec. if (D.isTargetIPhoneOS()) { if (D.getArch() == llvm::Triple::aarch64) ; // iOS does not need any crt1 files for arm64 else if (D.isIPhoneOSVersionLT(3, 1)) CmdArgs.push_back("-lcrt1.o"); else if (D.isIPhoneOSVersionLT(6, 0)) CmdArgs.push_back("-lcrt1.3.1.o"); return; } if (!D.isTargetMacOS()) return; if (D.isMacosxVersionLT(10, 5)) CmdArgs.push_back("-lcrt1.o"); else if (D.isMacosxVersionLT(10, 6)) CmdArgs.push_back("-lcrt1.10.5.o"); else if (D.isMacosxVersionLT(10, 8)) CmdArgs.push_back("-lcrt1.10.6.o"); // darwin_crt2 spec is empty. } void Darwin::addStartObjectFileArgs(const ArgList &Args, ArgStringList &CmdArgs) const { // Derived from startfile spec. if (Args.hasArg(options::OPT_dynamiclib)) addDynamicLibLinkArgs(*this, Args, CmdArgs); else if (Args.hasArg(options::OPT_bundle)) addBundleLinkArgs(*this, Args, CmdArgs); else if (Args.hasArg(options::OPT_pg) && SupportsProfiling()) addPgProfilingLinkArgs(*this, Args, CmdArgs); else if (Args.hasArg(options::OPT_static) || Args.hasArg(options::OPT_object) || Args.hasArg(options::OPT_preload)) CmdArgs.push_back("-lcrt0.o"); else addDefaultCRTLinkArgs(*this, Args, CmdArgs); if (isTargetMacOS() && Args.hasArg(options::OPT_shared_libgcc) && isMacosxVersionLT(10, 5)) { const char *Str = Args.MakeArgString(GetFilePath("crt3.o")); CmdArgs.push_back(Str); } } void Darwin::CheckObjCARC() const { if (isTargetIOSBased() || isTargetWatchOSBased() || isTargetXROS() || (isTargetMacOSBased() && !isMacosxVersionLT(10, 6))) return; getDriver().Diag(diag::err_arc_unsupported_on_toolchain); } SanitizerMask Darwin::getSupportedSanitizers() const { const bool IsX86_64 = getTriple().getArch() == llvm::Triple::x86_64; const bool IsAArch64 = getTriple().getArch() == llvm::Triple::aarch64; SanitizerMask Res = ToolChain::getSupportedSanitizers(); Res |= SanitizerKind::Address; Res |= SanitizerKind::PointerCompare; Res |= SanitizerKind::PointerSubtract; Res |= SanitizerKind::Leak; Res |= SanitizerKind::Fuzzer; Res |= SanitizerKind::FuzzerNoLink; Res |= SanitizerKind::ObjCCast; // Prior to 10.9, macOS shipped a version of the C++ standard library without // C++11 support. The same is true of iOS prior to version 5. These OS'es are // incompatible with -fsanitize=vptr. if (!(isTargetMacOSBased() && isMacosxVersionLT(10, 9)) && !(isTargetIPhoneOS() && isIPhoneOSVersionLT(5, 0))) Res |= SanitizerKind::Vptr; if ((IsX86_64 || IsAArch64) && (isTargetMacOSBased() || isTargetIOSSimulator() || isTargetTvOSSimulator() || isTargetWatchOSSimulator())) { Res |= SanitizerKind::Thread; } return Res; } void Darwin::printVerboseInfo(raw_ostream &OS) const { CudaInstallation->print(OS); RocmInstallation->print(OS); }