diff --git a/llvm/docs/LFI.rst b/llvm/docs/LFI.rst index 3c1cc96b2178..65d8b70f17e0 100644 --- a/llvm/docs/LFI.rst +++ b/llvm/docs/LFI.rst @@ -398,6 +398,33 @@ In certain cases, guards may be hoisted outside of loops. | | | +-----------------------+-------------------------------+ +Assembler Directives +==================== + +The LFI assembler supports the following directives for controlling the +rewriter. + +``.lfi_rewrite_disable`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +Disables LFI assembly rewrites for all subsequent instructions, until +``.lfi_rewrite_enable`` is used. This can be useful for hand-written assembly +that is already safe and should not be modified by the rewriter. + +``.lfi_rewrite_enable`` +~~~~~~~~~~~~~~~~~~~~~~~ + +Re-enables LFI assembly rewrites after a previous ``.lfi_rewrite_disable``. + +Example: + +.. code-block:: gas + + .lfi_rewrite_disable + // No rewrites applied here. + ldr x0, [x27, w1, uxtw] + .lfi_rewrite_enable + References ++++++++++ diff --git a/llvm/include/llvm/MC/MCLFI.h b/llvm/include/llvm/MC/MCLFI.h new file mode 100644 index 000000000000..6ceeb5b5051c --- /dev/null +++ b/llvm/include/llvm/MC/MCLFI.h @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// LFI-specific code for MC. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" + +namespace llvm { + +class MCContext; +class MCStreamer; +class Triple; + +LLVM_ABI void initializeLFIMCStreamer(MCStreamer &Streamer, MCContext &Ctx, + const Triple &TheTriple); + +} // namespace llvm diff --git a/llvm/include/llvm/MC/MCLFIRewriter.h b/llvm/include/llvm/MC/MCLFIRewriter.h new file mode 100644 index 000000000000..90f8a9b0e0c0 --- /dev/null +++ b/llvm/include/llvm/MC/MCLFIRewriter.h @@ -0,0 +1,68 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file declares the MCLFIRewriter class, an abstract class that +/// encapsulates the rewriting logic for MCInsts. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_MC_MCLFIREWRITER_H +#define LLVM_MC_MCLFIREWRITER_H + +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/Support/Compiler.h" + +namespace llvm { +class MCInst; +class MCSubtargetInfo; +class MCStreamer; +class MCSymbol; + +class MCLFIRewriter { +private: + MCContext &Ctx; + +protected: + bool Enabled = true; + std::unique_ptr InstInfo; + std::unique_ptr RegInfo; + +public: + MCLFIRewriter(MCContext &Ctx, std::unique_ptr &&RI, + std::unique_ptr &&II) + : Ctx(Ctx), InstInfo(std::move(II)), RegInfo(std::move(RI)) {} + + LLVM_ABI void error(const MCInst &Inst, const char Msg[]); + + void disable() { Enabled = false; } + void enable() { Enabled = true; } + + LLVM_ABI bool isCall(const MCInst &Inst) const; + LLVM_ABI bool isBranch(const MCInst &Inst) const; + LLVM_ABI bool isIndirectBranch(const MCInst &Inst) const; + LLVM_ABI bool isReturn(const MCInst &Inst) const; + + LLVM_ABI bool mayLoad(const MCInst &Inst) const; + LLVM_ABI bool mayStore(const MCInst &Inst) const; + + LLVM_ABI bool mayModifyRegister(const MCInst &Inst, MCRegister Reg) const; + + virtual ~MCLFIRewriter() = default; + virtual bool rewriteInst(const MCInst &Inst, MCStreamer &Out, + const MCSubtargetInfo &STI) = 0; + + // Called when a label is emitted. Used for optimizations that require + // information about jump targets, such as guard elimination. + virtual void onLabel(const MCSymbol *Symbol) {} +}; + +} // namespace llvm +#endif diff --git a/llvm/include/llvm/MC/MCParser/MCAsmParserExtension.h b/llvm/include/llvm/MC/MCParser/MCAsmParserExtension.h index 66fd28827065..a32982ea315a 100644 --- a/llvm/include/llvm/MC/MCParser/MCAsmParserExtension.h +++ b/llvm/include/llvm/MC/MCParser/MCAsmParserExtension.h @@ -11,6 +11,7 @@ #include "llvm/ADT/STLFunctionalExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/MC/MCLFIRewriter.h" #include "llvm/MC/MCParser/MCAsmParser.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/SMLoc.h" @@ -127,6 +128,7 @@ MCAsmParserExtension *createCOFFMasmParser(); MCAsmParserExtension *createGOFFAsmParser(); MCAsmParserExtension *createXCOFFAsmParser(); MCAsmParserExtension *createWasmAsmParser(); +MCAsmParserExtension *createLFIAsmParser(MCLFIRewriter *Exp); } // end namespace llvm diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h index a60a3dd21859..9c0386dbacda 100644 --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -19,6 +19,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCDwarf.h" +#include "llvm/MC/MCLFIRewriter.h" #include "llvm/MC/MCLinkerOptimizationHint.h" #include "llvm/MC/MCPseudoProbe.h" #include "llvm/MC/MCSection.h" @@ -291,6 +292,8 @@ protected: /// Returns true if the .cv_loc directive is in the right section. bool checkCVLocSection(unsigned FuncId, unsigned FileNo, SMLoc Loc); + std::unique_ptr LFIRewriter; + public: MCStreamer(const MCStreamer &) = delete; MCStreamer &operator=(const MCStreamer &) = delete; @@ -308,6 +311,12 @@ public: return StartTokLocPtr ? *StartTokLocPtr : SMLoc(); } + void setLFIRewriter(std::unique_ptr Rewriter) { + LFIRewriter = std::move(Rewriter); + } + + MCLFIRewriter *getLFIRewriter() { return LFIRewriter.get(); } + /// State management /// virtual void reset(); diff --git a/llvm/include/llvm/MC/TargetRegistry.h b/llvm/include/llvm/MC/TargetRegistry.h index 4451dfa72a5f..46c4076ba4ae 100644 --- a/llvm/include/llvm/MC/TargetRegistry.h +++ b/llvm/include/llvm/MC/TargetRegistry.h @@ -46,6 +46,7 @@ class MCDisassembler; class MCInstPrinter; class MCInstrAnalysis; class MCInstrInfo; +class MCLFIRewriter; class MCObjectWriter; class MCRegisterInfo; class MCRelocationInfo; @@ -237,6 +238,11 @@ public: mca::InstrumentManager *(*)(const MCSubtargetInfo &STI, const MCInstrInfo &MCII); + using MCLFIRewriterCtorTy = + MCLFIRewriter *(*)(MCStreamer & S, + std::unique_ptr &&RegInfo, + std::unique_ptr &&InstInfo); + private: /// Next - The next registered target in the linked list, maintained by the /// TargetRegistry. @@ -351,6 +357,10 @@ private: /// InstrumentManager, if registered (default = nullptr). InstrumentManagerCtorTy InstrumentManagerCtorFn = nullptr; + // MCLFIRewriterCtorFn - Construction function for this target's + // MCLFIRewriter, if registered (default = nullptr). + MCLFIRewriterCtorTy MCLFIRewriterCtorFn = nullptr; + public: Target() = default; @@ -566,6 +576,13 @@ public: return nullptr; } + void createMCLFIRewriter(MCStreamer &S, + std::unique_ptr &&RegInfo, + std::unique_ptr &&InstInfo) const { + if (MCLFIRewriterCtorFn) + MCLFIRewriterCtorFn(S, std::move(RegInfo), std::move(InstInfo)); + } + /// createMCRelocationInfo - Create a target specific MCRelocationInfo. /// /// \param TT The target triple. @@ -1021,6 +1038,10 @@ struct TargetRegistry { T.InstrumentManagerCtorFn = Fn; } + static void RegisterMCLFIRewriter(Target &T, Target::MCLFIRewriterCtorTy Fn) { + T.MCLFIRewriterCtorFn = Fn; + } + /// @} }; diff --git a/llvm/lib/MC/CMakeLists.txt b/llvm/lib/MC/CMakeLists.txt index 1388f130bb80..7a9e26af415c 100644 --- a/llvm/lib/MC/CMakeLists.txt +++ b/llvm/lib/MC/CMakeLists.txt @@ -31,6 +31,8 @@ add_llvm_component_library(LLVMMC MCInstrAnalysis.cpp MCInstrDesc.cpp MCInstrInfo.cpp + MCLFI.cpp + MCLFIRewriter.cpp MCLabel.cpp MCLinkerOptimizationHint.cpp MCMachOStreamer.cpp diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index d9a22bc2716f..1a50ae43cd9c 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -19,6 +19,7 @@ #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCLFIRewriter.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCPseudoProbe.h" @@ -2494,6 +2495,9 @@ void MCAsmStreamer::AddEncodingComment(const MCInst &Inst, void MCAsmStreamer::emitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI) { + if (LFIRewriter && LFIRewriter->rewriteInst(Inst, *this, STI)) + return; + if (CurFrag) { MCSection *Sec = getCurrentSectionOnly(); Sec->setHasInstructions(true); diff --git a/llvm/lib/MC/MCLFI.cpp b/llvm/lib/MC/MCLFI.cpp new file mode 100644 index 000000000000..0092f867a8ca --- /dev/null +++ b/llvm/lib/MC/MCLFI.cpp @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// LFI-specific MC implementation. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/MCLFI.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCLFIRewriter.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/Alignment.h" +#include "llvm/TargetParser/Triple.h" + +static const char NoteNamespace[] = "LFI"; + +namespace llvm { + +cl::opt FlagEnableRewriting("lfi-enable-rewriter", + cl::desc("Enable rewriting for LFI."), + cl::init(true)); + +void initializeLFIMCStreamer(MCStreamer &Streamer, MCContext &Ctx, + const Triple &TheTriple) { + assert(TheTriple.isLFI()); + const char *NoteName; + const char *NoteArch; + switch (TheTriple.getArch()) { + case Triple::aarch64: + NoteName = ".note.LFI.ABI.aarch64"; + NoteArch = "aarch64"; + break; + default: + reportFatalUsageError("Unsupported architecture for LFI"); + } + + std::string Error; // empty + const Target *TheTarget = TargetRegistry::lookupTarget(TheTriple, Error); + + // Create the Target specific MCLFIRewriter. + assert(TheTarget != nullptr); + if (FlagEnableRewriting) { + TheTarget->createMCLFIRewriter( + Streamer, + std::unique_ptr(TheTarget->createMCRegInfo(TheTriple)), + std::unique_ptr(TheTarget->createMCInstrInfo())); + } + + // Emit an ELF Note section in its own COMDAT group which identifies LFI + // object files. + MCSectionELF *Note = Ctx.getELFSection(NoteName, ELF::SHT_NOTE, + ELF::SHF_ALLOC | ELF::SHF_GROUP, 0, + NoteName, /*IsComdat=*/true); + + Streamer.pushSection(); + Streamer.switchSection(Note); + Streamer.emitIntValue(strlen(NoteNamespace) + 1, 4); + Streamer.emitIntValue(strlen(NoteArch) + 1, 4); + Streamer.emitIntValue(ELF::NT_VERSION, 4); + Streamer.emitBytes(NoteNamespace); + Streamer.emitIntValue(0, 1); // NUL terminator + Streamer.emitValueToAlignment(Align(4)); + Streamer.emitBytes(NoteArch); + Streamer.emitIntValue(0, 1); // NUL terminator + Streamer.emitValueToAlignment(Align(4)); + Streamer.popSection(); +} + +} // namespace llvm diff --git a/llvm/lib/MC/MCLFIRewriter.cpp b/llvm/lib/MC/MCLFIRewriter.cpp new file mode 100644 index 000000000000..0ffbc02689aa --- /dev/null +++ b/llvm/lib/MC/MCLFIRewriter.cpp @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements the MCLFIRewriter class, a base class that +/// encapsulates the rewriting logic for MCInsts. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/MCLFIRewriter.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" + +namespace llvm { + +void MCLFIRewriter::error(const MCInst &Inst, const char Msg[]) { + Ctx.reportError(Inst.getLoc(), Msg); +} + +bool MCLFIRewriter::isCall(const MCInst &Inst) const { + return InstInfo->get(Inst.getOpcode()).isCall(); +} + +bool MCLFIRewriter::isBranch(const MCInst &Inst) const { + return InstInfo->get(Inst.getOpcode()).isBranch(); +} + +bool MCLFIRewriter::isIndirectBranch(const MCInst &Inst) const { + return InstInfo->get(Inst.getOpcode()).isIndirectBranch(); +} + +bool MCLFIRewriter::isReturn(const MCInst &Inst) const { + return InstInfo->get(Inst.getOpcode()).isReturn(); +} + +bool MCLFIRewriter::mayLoad(const MCInst &Inst) const { + return InstInfo->get(Inst.getOpcode()).mayLoad(); +} + +bool MCLFIRewriter::mayStore(const MCInst &Inst) const { + return InstInfo->get(Inst.getOpcode()).mayStore(); +} + +bool MCLFIRewriter::mayModifyRegister(const MCInst &Inst, + MCRegister Reg) const { + return InstInfo->get(Inst.getOpcode()).hasDefOfPhysReg(Inst, Reg, *RegInfo); +} +} // namespace llvm diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp index 453d4d20d972..58aa7945d739 100644 --- a/llvm/lib/MC/MCObjectStreamer.cpp +++ b/llvm/lib/MC/MCObjectStreamer.cpp @@ -15,6 +15,7 @@ #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDwarf.h" #include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCLFIRewriter.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCSFrame.h" @@ -398,6 +399,9 @@ bool MCObjectStreamer::mayHaveInstructions(MCSection &Sec) const { void MCObjectStreamer::emitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI) { + if (LFIRewriter && LFIRewriter->rewriteInst(Inst, *this, STI)) + return; + MCStreamer::emitInstruction(Inst, STI); MCSection *Sec = getCurrentSectionOnly(); diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp index 3277994701b7..3452708bcec8 100644 --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -120,6 +120,7 @@ private: SourceMgr::DiagHandlerTy SavedDiagHandler; void *SavedDiagContext; std::unique_ptr PlatformParser; + std::unique_ptr LFIParser; SMLoc StartTokLoc; std::optional CFIStartProcLoc; @@ -788,6 +789,10 @@ AsmParser::AsmParser(SourceMgr &SM, MCContext &Ctx, MCStreamer &Out, } PlatformParser->Initialize(*this); + if (Out.getLFIRewriter()) { + LFIParser.reset(createLFIAsmParser(Out.getLFIRewriter())); + LFIParser->Initialize(*this); + } initializeDirectiveKindMap(); initializeCVDefRangeTypeMap(); } diff --git a/llvm/lib/MC/MCParser/CMakeLists.txt b/llvm/lib/MC/MCParser/CMakeLists.txt index 008a50e9da66..c911874fc654 100644 --- a/llvm/lib/MC/MCParser/CMakeLists.txt +++ b/llvm/lib/MC/MCParser/CMakeLists.txt @@ -6,6 +6,7 @@ add_llvm_component_library(LLVMMCParser GOFFAsmParser.cpp DarwinAsmParser.cpp ELFAsmParser.cpp + LFIAsmParser.cpp MCAsmParser.cpp MCAsmParserExtension.cpp MCTargetAsmParser.cpp diff --git a/llvm/lib/MC/MCParser/LFIAsmParser.cpp b/llvm/lib/MC/MCParser/LFIAsmParser.cpp new file mode 100644 index 000000000000..138b4500c50e --- /dev/null +++ b/llvm/lib/MC/MCParser/LFIAsmParser.cpp @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// LFI assembly parser. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/MCLFIRewriter.h" +#include "llvm/MC/MCParser/MCAsmParserExtension.h" +#include "llvm/MC/MCStreamer.h" + +using namespace llvm; + +class LFIAsmParser : public MCAsmParserExtension { + MCLFIRewriter *Rewriter; + template + void addDirectiveHandler(StringRef Directive) { + MCAsmParser::ExtensionDirectiveHandler Handler = + std::make_pair(this, HandleDirective); + + getParser().addDirectiveHandler(Directive, Handler); + } + +public: + LFIAsmParser(MCLFIRewriter *Exp) : Rewriter(Exp) {} + void Initialize(MCAsmParser &Parser) override { + // Call the base implementation. + MCAsmParserExtension::Initialize(Parser); + addDirectiveHandler<&LFIAsmParser::parseRewriteDisable>( + ".lfi_rewrite_disable"); + addDirectiveHandler<&LFIAsmParser::parseRewriteEnable>( + ".lfi_rewrite_enable"); + } + + /// ::= {.lfi_rewrite_disable} + bool parseRewriteDisable(StringRef Directive, SMLoc Loc) { + getParser().checkForValidSection(); + if (getLexer().isNot(AsmToken::EndOfStatement)) + return TokError("unexpected token"); + Lex(); + + Rewriter->disable(); + + return false; + } + + /// ::= {.lfi_rewrite_enable} + bool parseRewriteEnable(StringRef Directive, SMLoc Loc) { + getParser().checkForValidSection(); + if (getLexer().isNot(AsmToken::EndOfStatement)) + return TokError("unexpected token"); + Lex(); + + Rewriter->enable(); + + return false; + } +}; + +namespace llvm { +MCAsmParserExtension *createLFIAsmParser(MCLFIRewriter *Exp) { + return new LFIAsmParser(Exp); +} +} // namespace llvm diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp index 5c7ebcf6b66e..33c9a05bec11 100644 --- a/llvm/lib/MC/MCStreamer.cpp +++ b/llvm/lib/MC/MCStreamer.cpp @@ -399,6 +399,9 @@ void MCStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) { Symbol->setFragment(&getCurrentSectionOnly()->getDummyFragment()); + if (LFIRewriter) + LFIRewriter->onLabel(Symbol); + MCTargetStreamer *TS = getTargetStreamer(); if (TS) TS->emitLabel(Symbol); diff --git a/llvm/lib/MC/TargetRegistry.cpp b/llvm/lib/MC/TargetRegistry.cpp index 9263dda65a8b..db743ef9bdcb 100644 --- a/llvm/lib/MC/TargetRegistry.cpp +++ b/llvm/lib/MC/TargetRegistry.cpp @@ -13,6 +13,7 @@ #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCLFI.h" #include "llvm/MC/MCObjectStreamer.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/Support/raw_ostream.h" @@ -76,6 +77,8 @@ MCStreamer *Target::createMCObjectStreamer( } if (ObjectTargetStreamerCtorFn) ObjectTargetStreamerCtorFn(*S, STI); + if (T.isLFI()) + initializeLFIMCStreamer(*S, Ctx, T); return S; } diff --git a/llvm/tools/llvm-mc/llvm-mc.cpp b/llvm/tools/llvm-mc/llvm-mc.cpp index baf3b5536138..df7f813d1139 100644 --- a/llvm/tools/llvm-mc/llvm-mc.cpp +++ b/llvm/tools/llvm-mc/llvm-mc.cpp @@ -21,6 +21,7 @@ #include "llvm/MC/MCContext.h" #include "llvm/MC/MCInstPrinter.h" #include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCLFI.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCParser/AsmLexer.h" @@ -626,6 +627,11 @@ int main(int argc, char **argv) { Str.reset(TheTarget->createAsmStreamer(Ctx, std::move(FOut), std::move(IP), std::move(CE), std::move(MAB))); + Triple T(TripleName); + if (T.isLFI()) { + Str->initSections(NoExecStack, *STI); + initializeLFIMCStreamer(*Str.get(), Ctx, T); + } } else if (FileType == OFT_Null) { Str.reset(TheTarget->createNullStreamer(Ctx)); } else {