1 //===-- BareMetal.cpp - Bare Metal ToolChain --------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "BareMetal.h" 10 11 #include "CommonArgs.h" 12 #include "Gnu.h" 13 #include "clang/Driver/InputInfo.h" 14 15 #include "Arch/ARM.h" 16 #include "Arch/RISCV.h" 17 #include "clang/Driver/Compilation.h" 18 #include "clang/Driver/Driver.h" 19 #include "clang/Driver/DriverDiagnostic.h" 20 #include "clang/Driver/MultilibBuilder.h" 21 #include "clang/Driver/Options.h" 22 #include "llvm/ADT/StringExtras.h" 23 #include "llvm/Option/ArgList.h" 24 #include "llvm/Support/Path.h" 25 #include "llvm/Support/VirtualFileSystem.h" 26 #include "llvm/Support/raw_ostream.h" 27 28 #include <sstream> 29 30 using namespace llvm::opt; 31 using namespace clang; 32 using namespace clang::driver; 33 using namespace clang::driver::tools; 34 using namespace clang::driver::toolchains; 35 36 static bool findRISCVMultilibs(const Driver &D, 37 const llvm::Triple &TargetTriple, 38 const ArgList &Args, DetectedMultilibs &Result) { 39 Multilib::flags_list Flags; 40 std::string Arch = riscv::getRISCVArch(Args, TargetTriple); 41 StringRef Abi = tools::riscv::getRISCVABI(Args, TargetTriple); 42 43 if (TargetTriple.isRISCV64()) { 44 MultilibBuilder Imac = 45 MultilibBuilder().flag("-march=rv64imac").flag("-mabi=lp64"); 46 MultilibBuilder Imafdc = MultilibBuilder("/rv64imafdc/lp64d") 47 .flag("-march=rv64imafdc") 48 .flag("-mabi=lp64d"); 49 50 // Multilib reuse 51 bool UseImafdc = 52 (Arch == "rv64imafdc") || (Arch == "rv64gc"); // gc => imafdc 53 54 addMultilibFlag((Arch == "rv64imac"), "-march=rv64imac", Flags); 55 addMultilibFlag(UseImafdc, "-march=rv64imafdc", Flags); 56 addMultilibFlag(Abi == "lp64", "-mabi=lp64", Flags); 57 addMultilibFlag(Abi == "lp64d", "-mabi=lp64d", Flags); 58 59 Result.Multilibs = 60 MultilibSetBuilder().Either(Imac, Imafdc).makeMultilibSet(); 61 return Result.Multilibs.select(Flags, Result.SelectedMultilibs); 62 } 63 if (TargetTriple.isRISCV32()) { 64 MultilibBuilder Imac = 65 MultilibBuilder().flag("-march=rv32imac").flag("-mabi=ilp32"); 66 MultilibBuilder I = MultilibBuilder("/rv32i/ilp32") 67 .flag("-march=rv32i") 68 .flag("-mabi=ilp32"); 69 MultilibBuilder Im = MultilibBuilder("/rv32im/ilp32") 70 .flag("-march=rv32im") 71 .flag("-mabi=ilp32"); 72 MultilibBuilder Iac = MultilibBuilder("/rv32iac/ilp32") 73 .flag("-march=rv32iac") 74 .flag("-mabi=ilp32"); 75 MultilibBuilder Imafc = MultilibBuilder("/rv32imafc/ilp32f") 76 .flag("-march=rv32imafc") 77 .flag("-mabi=ilp32f"); 78 79 // Multilib reuse 80 bool UseI = (Arch == "rv32i") || (Arch == "rv32ic"); // ic => i 81 bool UseIm = (Arch == "rv32im") || (Arch == "rv32imc"); // imc => im 82 bool UseImafc = (Arch == "rv32imafc") || (Arch == "rv32imafdc") || 83 (Arch == "rv32gc"); // imafdc,gc => imafc 84 85 addMultilibFlag(UseI, "-march=rv32i", Flags); 86 addMultilibFlag(UseIm, "-march=rv32im", Flags); 87 addMultilibFlag((Arch == "rv32iac"), "-march=rv32iac", Flags); 88 addMultilibFlag((Arch == "rv32imac"), "-march=rv32imac", Flags); 89 addMultilibFlag(UseImafc, "-march=rv32imafc", Flags); 90 addMultilibFlag(Abi == "ilp32", "-mabi=ilp32", Flags); 91 addMultilibFlag(Abi == "ilp32f", "-mabi=ilp32f", Flags); 92 93 Result.Multilibs = 94 MultilibSetBuilder().Either(I, Im, Iac, Imac, Imafc).makeMultilibSet(); 95 return Result.Multilibs.select(Flags, Result.SelectedMultilibs); 96 } 97 return false; 98 } 99 100 BareMetal::BareMetal(const Driver &D, const llvm::Triple &Triple, 101 const ArgList &Args) 102 : ToolChain(D, Triple, Args) { 103 getProgramPaths().push_back(getDriver().Dir); 104 105 findMultilibs(D, Triple, Args); 106 SmallString<128> SysRoot(computeSysRoot()); 107 if (!SysRoot.empty()) { 108 for (const Multilib &M : getOrderedMultilibs()) { 109 SmallString<128> Dir(SysRoot); 110 llvm::sys::path::append(Dir, M.osSuffix(), "lib"); 111 getFilePaths().push_back(std::string(Dir)); 112 getLibraryPaths().push_back(std::string(Dir)); 113 } 114 } 115 } 116 117 /// Is the triple {arm,armeb,thumb,thumbeb}-none-none-{eabi,eabihf} ? 118 static bool isARMBareMetal(const llvm::Triple &Triple) { 119 if (Triple.getArch() != llvm::Triple::arm && 120 Triple.getArch() != llvm::Triple::thumb && 121 Triple.getArch() != llvm::Triple::armeb && 122 Triple.getArch() != llvm::Triple::thumbeb) 123 return false; 124 125 if (Triple.getVendor() != llvm::Triple::UnknownVendor) 126 return false; 127 128 if (Triple.getOS() != llvm::Triple::UnknownOS) 129 return false; 130 131 if (Triple.getEnvironment() != llvm::Triple::EABI && 132 Triple.getEnvironment() != llvm::Triple::EABIHF) 133 return false; 134 135 return true; 136 } 137 138 /// Is the triple {aarch64.aarch64_be}-none-elf? 139 static bool isAArch64BareMetal(const llvm::Triple &Triple) { 140 if (Triple.getArch() != llvm::Triple::aarch64 && 141 Triple.getArch() != llvm::Triple::aarch64_be) 142 return false; 143 144 if (Triple.getVendor() != llvm::Triple::UnknownVendor) 145 return false; 146 147 if (Triple.getOS() != llvm::Triple::UnknownOS) 148 return false; 149 150 return Triple.getEnvironmentName() == "elf"; 151 } 152 153 static bool isRISCVBareMetal(const llvm::Triple &Triple) { 154 if (!Triple.isRISCV()) 155 return false; 156 157 if (Triple.getVendor() != llvm::Triple::UnknownVendor) 158 return false; 159 160 if (Triple.getOS() != llvm::Triple::UnknownOS) 161 return false; 162 163 return Triple.getEnvironmentName() == "elf"; 164 } 165 166 /// Is the triple powerpc[64][le]-*-none-eabi? 167 static bool isPPCBareMetal(const llvm::Triple &Triple) { 168 return Triple.isPPC() && Triple.getOS() == llvm::Triple::UnknownOS && 169 Triple.getEnvironment() == llvm::Triple::EABI; 170 } 171 172 static void findMultilibsFromYAML(const ToolChain &TC, const Driver &D, 173 StringRef MultilibPath, const ArgList &Args, 174 DetectedMultilibs &Result) { 175 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB = 176 D.getVFS().getBufferForFile(MultilibPath); 177 if (!MB) 178 return; 179 Multilib::flags_list Flags = TC.getMultilibFlags(Args); 180 llvm::ErrorOr<MultilibSet> ErrorOrMultilibSet = 181 MultilibSet::parseYaml(*MB.get()); 182 if (ErrorOrMultilibSet.getError()) 183 return; 184 Result.Multilibs = ErrorOrMultilibSet.get(); 185 if (Result.Multilibs.select(Flags, Result.SelectedMultilibs)) 186 return; 187 D.Diag(clang::diag::warn_drv_missing_multilib) << llvm::join(Flags, " "); 188 std::stringstream ss; 189 for (const Multilib &Multilib : Result.Multilibs) 190 ss << "\n" << llvm::join(Multilib.flags(), " "); 191 D.Diag(clang::diag::note_drv_available_multilibs) << ss.str(); 192 } 193 194 static constexpr llvm::StringLiteral MultilibFilename = "multilib.yaml"; 195 196 // Get the sysroot, before multilib takes effect. 197 static std::string computeBaseSysRoot(const Driver &D, 198 const llvm::Triple &Triple) { 199 if (!D.SysRoot.empty()) 200 return D.SysRoot; 201 202 SmallString<128> SysRootDir(D.Dir); 203 llvm::sys::path::append(SysRootDir, "..", "lib", "clang-runtimes"); 204 205 SmallString<128> MultilibPath(SysRootDir); 206 llvm::sys::path::append(MultilibPath, MultilibFilename); 207 208 // New behaviour: if multilib.yaml is found then use clang-runtimes as the 209 // sysroot. 210 if (D.getVFS().exists(MultilibPath)) 211 return std::string(SysRootDir); 212 213 // Otherwise fall back to the old behaviour of appending the target triple. 214 llvm::sys::path::append(SysRootDir, D.getTargetTriple()); 215 return std::string(SysRootDir); 216 } 217 218 void BareMetal::findMultilibs(const Driver &D, const llvm::Triple &Triple, 219 const ArgList &Args) { 220 DetectedMultilibs Result; 221 if (isRISCVBareMetal(Triple)) { 222 if (findRISCVMultilibs(D, Triple, Args, Result)) { 223 SelectedMultilibs = Result.SelectedMultilibs; 224 Multilibs = Result.Multilibs; 225 } 226 } else { 227 llvm::SmallString<128> MultilibPath(computeBaseSysRoot(D, Triple)); 228 llvm::sys::path::append(MultilibPath, MultilibFilename); 229 findMultilibsFromYAML(*this, D, MultilibPath, Args, Result); 230 SelectedMultilibs = Result.SelectedMultilibs; 231 Multilibs = Result.Multilibs; 232 } 233 } 234 235 bool BareMetal::handlesTarget(const llvm::Triple &Triple) { 236 return isARMBareMetal(Triple) || isAArch64BareMetal(Triple) || 237 isRISCVBareMetal(Triple) || isPPCBareMetal(Triple); 238 } 239 240 Tool *BareMetal::buildLinker() const { 241 return new tools::baremetal::Linker(*this); 242 } 243 244 Tool *BareMetal::buildStaticLibTool() const { 245 return new tools::baremetal::StaticLibTool(*this); 246 } 247 248 std::string BareMetal::computeSysRoot() const { 249 return computeBaseSysRoot(getDriver(), getTriple()); 250 } 251 252 BareMetal::OrderedMultilibs BareMetal::getOrderedMultilibs() const { 253 // Get multilibs in reverse order because they're ordered most-specific last. 254 if (!SelectedMultilibs.empty()) 255 return llvm::reverse(SelectedMultilibs); 256 257 // No multilibs selected so return a single default multilib. 258 static const llvm::SmallVector<Multilib> Default = {Multilib()}; 259 return llvm::reverse(Default); 260 } 261 262 void BareMetal::AddClangSystemIncludeArgs(const ArgList &DriverArgs, 263 ArgStringList &CC1Args) const { 264 if (DriverArgs.hasArg(options::OPT_nostdinc)) 265 return; 266 267 if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) { 268 SmallString<128> Dir(getDriver().ResourceDir); 269 llvm::sys::path::append(Dir, "include"); 270 addSystemInclude(DriverArgs, CC1Args, Dir.str()); 271 } 272 273 if (DriverArgs.hasArg(options::OPT_nostdlibinc)) 274 return; 275 276 if (std::optional<std::string> Path = getStdlibIncludePath()) 277 addSystemInclude(DriverArgs, CC1Args, *Path); 278 279 const SmallString<128> SysRoot(computeSysRoot()); 280 if (!SysRoot.empty()) { 281 for (const Multilib &M : getOrderedMultilibs()) { 282 SmallString<128> Dir(SysRoot); 283 llvm::sys::path::append(Dir, M.includeSuffix()); 284 llvm::sys::path::append(Dir, "include"); 285 addSystemInclude(DriverArgs, CC1Args, Dir.str()); 286 } 287 } 288 } 289 290 void BareMetal::addClangTargetOptions(const ArgList &DriverArgs, 291 ArgStringList &CC1Args, 292 Action::OffloadKind) const { 293 CC1Args.push_back("-nostdsysteminc"); 294 } 295 296 void BareMetal::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, 297 ArgStringList &CC1Args) const { 298 if (DriverArgs.hasArg(options::OPT_nostdinc, options::OPT_nostdlibinc, 299 options::OPT_nostdincxx)) 300 return; 301 302 const Driver &D = getDriver(); 303 std::string Target = getTripleString(); 304 305 auto AddCXXIncludePath = [&](StringRef Path) { 306 std::string Version = detectLibcxxVersion(Path); 307 if (Version.empty()) 308 return; 309 310 { 311 // First the per-target include dir: include/<target>/c++/v1. 312 SmallString<128> TargetDir(Path); 313 llvm::sys::path::append(TargetDir, Target, "c++", Version); 314 addSystemInclude(DriverArgs, CC1Args, TargetDir); 315 } 316 317 { 318 // Then the generic dir: include/c++/v1. 319 SmallString<128> Dir(Path); 320 llvm::sys::path::append(Dir, "c++", Version); 321 addSystemInclude(DriverArgs, CC1Args, Dir); 322 } 323 }; 324 325 switch (GetCXXStdlibType(DriverArgs)) { 326 case ToolChain::CST_Libcxx: { 327 SmallString<128> P(D.Dir); 328 llvm::sys::path::append(P, "..", "include"); 329 AddCXXIncludePath(P); 330 break; 331 } 332 case ToolChain::CST_Libstdcxx: 333 // We only support libc++ toolchain installation. 334 break; 335 } 336 337 std::string SysRoot(computeSysRoot()); 338 if (SysRoot.empty()) 339 return; 340 341 for (const Multilib &M : getOrderedMultilibs()) { 342 SmallString<128> Dir(SysRoot); 343 llvm::sys::path::append(Dir, M.gccSuffix()); 344 switch (GetCXXStdlibType(DriverArgs)) { 345 case ToolChain::CST_Libcxx: { 346 // First check sysroot/usr/include/c++/v1 if it exists. 347 SmallString<128> TargetDir(Dir); 348 llvm::sys::path::append(TargetDir, "usr", "include", "c++", "v1"); 349 if (D.getVFS().exists(TargetDir)) { 350 addSystemInclude(DriverArgs, CC1Args, TargetDir.str()); 351 break; 352 } 353 // Add generic path if nothing else succeeded so far. 354 llvm::sys::path::append(Dir, "include", "c++", "v1"); 355 addSystemInclude(DriverArgs, CC1Args, Dir.str()); 356 break; 357 } 358 case ToolChain::CST_Libstdcxx: { 359 llvm::sys::path::append(Dir, "include", "c++"); 360 std::error_code EC; 361 Generic_GCC::GCCVersion Version = {"", -1, -1, -1, "", "", ""}; 362 // Walk the subdirs, and find the one with the newest gcc version: 363 for (llvm::vfs::directory_iterator 364 LI = D.getVFS().dir_begin(Dir.str(), EC), 365 LE; 366 !EC && LI != LE; LI = LI.increment(EC)) { 367 StringRef VersionText = llvm::sys::path::filename(LI->path()); 368 auto CandidateVersion = Generic_GCC::GCCVersion::Parse(VersionText); 369 if (CandidateVersion.Major == -1) 370 continue; 371 if (CandidateVersion <= Version) 372 continue; 373 Version = CandidateVersion; 374 } 375 if (Version.Major != -1) { 376 llvm::sys::path::append(Dir, Version.Text); 377 addSystemInclude(DriverArgs, CC1Args, Dir.str()); 378 } 379 break; 380 } 381 } 382 } 383 } 384 385 void BareMetal::AddCXXStdlibLibArgs(const ArgList &Args, 386 ArgStringList &CmdArgs) const { 387 switch (GetCXXStdlibType(Args)) { 388 case ToolChain::CST_Libcxx: 389 CmdArgs.push_back("-lc++"); 390 if (Args.hasArg(options::OPT_fexperimental_library)) 391 CmdArgs.push_back("-lc++experimental"); 392 CmdArgs.push_back("-lc++abi"); 393 break; 394 case ToolChain::CST_Libstdcxx: 395 CmdArgs.push_back("-lstdc++"); 396 CmdArgs.push_back("-lsupc++"); 397 break; 398 } 399 CmdArgs.push_back("-lunwind"); 400 } 401 402 void BareMetal::AddLinkRuntimeLib(const ArgList &Args, 403 ArgStringList &CmdArgs) const { 404 ToolChain::RuntimeLibType RLT = GetRuntimeLibType(Args); 405 switch (RLT) { 406 case ToolChain::RLT_CompilerRT: { 407 CmdArgs.push_back(getCompilerRTArgString(Args, "builtins")); 408 return; 409 } 410 case ToolChain::RLT_Libgcc: 411 CmdArgs.push_back("-lgcc"); 412 return; 413 } 414 llvm_unreachable("Unhandled RuntimeLibType."); 415 } 416 417 void baremetal::StaticLibTool::ConstructJob(Compilation &C, const JobAction &JA, 418 const InputInfo &Output, 419 const InputInfoList &Inputs, 420 const ArgList &Args, 421 const char *LinkingOutput) const { 422 const Driver &D = getToolChain().getDriver(); 423 424 // Silence warning for "clang -g foo.o -o foo" 425 Args.ClaimAllArgs(options::OPT_g_Group); 426 // and "clang -emit-llvm foo.o -o foo" 427 Args.ClaimAllArgs(options::OPT_emit_llvm); 428 // and for "clang -w foo.o -o foo". Other warning options are already 429 // handled somewhere else. 430 Args.ClaimAllArgs(options::OPT_w); 431 // Silence warnings when linking C code with a C++ '-stdlib' argument. 432 Args.ClaimAllArgs(options::OPT_stdlib_EQ); 433 434 // ar tool command "llvm-ar <options> <output_file> <input_files>". 435 ArgStringList CmdArgs; 436 // Create and insert file members with a deterministic index. 437 CmdArgs.push_back("rcsD"); 438 CmdArgs.push_back(Output.getFilename()); 439 440 for (const auto &II : Inputs) { 441 if (II.isFilename()) { 442 CmdArgs.push_back(II.getFilename()); 443 } 444 } 445 446 // Delete old output archive file if it already exists before generating a new 447 // archive file. 448 const char *OutputFileName = Output.getFilename(); 449 if (Output.isFilename() && llvm::sys::fs::exists(OutputFileName)) { 450 if (std::error_code EC = llvm::sys::fs::remove(OutputFileName)) { 451 D.Diag(diag::err_drv_unable_to_remove_file) << EC.message(); 452 return; 453 } 454 } 455 456 const char *Exec = Args.MakeArgString(getToolChain().GetStaticLibToolPath()); 457 C.addCommand(std::make_unique<Command>(JA, *this, 458 ResponseFileSupport::AtFileCurCP(), 459 Exec, CmdArgs, Inputs, Output)); 460 } 461 462 void baremetal::Linker::ConstructJob(Compilation &C, const JobAction &JA, 463 const InputInfo &Output, 464 const InputInfoList &Inputs, 465 const ArgList &Args, 466 const char *LinkingOutput) const { 467 ArgStringList CmdArgs; 468 469 auto &TC = static_cast<const toolchains::BareMetal &>(getToolChain()); 470 const Driver &D = getToolChain().getDriver(); 471 const llvm::Triple::ArchType Arch = TC.getArch(); 472 const llvm::Triple &Triple = getToolChain().getEffectiveTriple(); 473 474 AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA); 475 476 CmdArgs.push_back("-Bstatic"); 477 478 if (TC.getTriple().isRISCV() && Args.hasArg(options::OPT_mno_relax)) 479 CmdArgs.push_back("--no-relax"); 480 481 if (Triple.isARM() || Triple.isThumb()) { 482 bool IsBigEndian = arm::isARMBigEndian(Triple, Args); 483 if (IsBigEndian) 484 arm::appendBE8LinkFlag(Args, CmdArgs, Triple); 485 CmdArgs.push_back(IsBigEndian ? "-EB" : "-EL"); 486 } else if (Triple.isAArch64()) { 487 CmdArgs.push_back(Arch == llvm::Triple::aarch64_be ? "-EB" : "-EL"); 488 } 489 490 Args.addAllArgs(CmdArgs, {options::OPT_L, options::OPT_T_Group, 491 options::OPT_s, options::OPT_t, options::OPT_r}); 492 493 TC.AddFilePathLibArgs(Args, CmdArgs); 494 495 for (const auto &LibPath : TC.getLibraryPaths()) 496 CmdArgs.push_back(Args.MakeArgString(llvm::Twine("-L", LibPath))); 497 498 if (TC.ShouldLinkCXXStdlib(Args)) 499 TC.AddCXXStdlibLibArgs(Args, CmdArgs); 500 501 if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) { 502 CmdArgs.push_back("-lc"); 503 CmdArgs.push_back("-lm"); 504 505 TC.AddLinkRuntimeLib(Args, CmdArgs); 506 } 507 508 if (D.isUsingLTO()) { 509 assert(!Inputs.empty() && "Must have at least one input."); 510 // Find the first filename InputInfo object. 511 auto Input = llvm::find_if( 512 Inputs, [](const InputInfo &II) -> bool { return II.isFilename(); }); 513 if (Input == Inputs.end()) 514 // For a very rare case, all of the inputs to the linker are 515 // InputArg. If that happens, just use the first InputInfo. 516 Input = Inputs.begin(); 517 518 addLTOOptions(TC, Args, CmdArgs, Output, *Input, 519 D.getLTOMode() == LTOK_Thin); 520 } 521 if (TC.getTriple().isRISCV()) 522 CmdArgs.push_back("-X"); 523 524 // The R_ARM_TARGET2 relocation must be treated as R_ARM_REL32 on arm*-*-elf 525 // and arm*-*-eabi (the default is R_ARM_GOT_PREL, used on arm*-*-linux and 526 // arm*-*-*bsd). 527 if (isARMBareMetal(TC.getTriple())) 528 CmdArgs.push_back("--target2=rel"); 529 530 CmdArgs.push_back("-o"); 531 CmdArgs.push_back(Output.getFilename()); 532 533 C.addCommand(std::make_unique<Command>( 534 JA, *this, ResponseFileSupport::AtFileCurCP(), 535 Args.MakeArgString(TC.GetLinkerPath()), CmdArgs, Inputs, Output)); 536 } 537 538 // BareMetal toolchain allows all sanitizers where the compiler generates valid 539 // code, ignoring all runtime library support issues on the assumption that 540 // baremetal targets typically implement their own runtime support. 541 SanitizerMask BareMetal::getSupportedSanitizers() const { 542 const bool IsX86_64 = getTriple().getArch() == llvm::Triple::x86_64; 543 const bool IsAArch64 = getTriple().getArch() == llvm::Triple::aarch64 || 544 getTriple().getArch() == llvm::Triple::aarch64_be; 545 const bool IsRISCV64 = getTriple().getArch() == llvm::Triple::riscv64; 546 SanitizerMask Res = ToolChain::getSupportedSanitizers(); 547 Res |= SanitizerKind::Address; 548 Res |= SanitizerKind::KernelAddress; 549 Res |= SanitizerKind::PointerCompare; 550 Res |= SanitizerKind::PointerSubtract; 551 Res |= SanitizerKind::Fuzzer; 552 Res |= SanitizerKind::FuzzerNoLink; 553 Res |= SanitizerKind::Vptr; 554 Res |= SanitizerKind::SafeStack; 555 Res |= SanitizerKind::Thread; 556 Res |= SanitizerKind::Scudo; 557 if (IsX86_64 || IsAArch64 || IsRISCV64) { 558 Res |= SanitizerKind::HWAddress; 559 Res |= SanitizerKind::KernelHWAddress; 560 } 561 return Res; 562 } 563