Index: lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp =================================================================== --- lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp +++ lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp @@ -102,6 +102,14 @@ MCELFStreamer::ChangeSection(Section, Subsection); } + // Reset state between object emissions + void reset() override { + MappingSymbolCounter = 0; + MCELFStreamer::reset(); + LastMappingSymbols.clear(); + LastEMS = EMS_None; + } + /// This function is the one used to emit instruction data into the ELF /// streamer. We override it to add the appropriate mapping symbol if /// necessary. Index: lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp =================================================================== --- lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp +++ lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp @@ -1171,6 +1171,8 @@ ATS.reset(); MappingSymbolCounter = 0; MCELFStreamer::reset(); + LastMappingSymbols.clear(); + LastEMSInfo.reset(); // MCELFStreamer clear's the assembler's e_flags. However, for // arm we manually set the ABI version on streamer creation, so // do the same here Index: unittests/Target/ARM/CMakeLists.txt =================================================================== --- /dev/null +++ unittests/Target/ARM/CMakeLists.txt @@ -0,0 +1,22 @@ +include_directories( + ${CMAKE_SOURCE_DIR}/lib/Target/ARM + ${CMAKE_BINARY_DIR}/lib/Target/ARM + ) + +set(LLVM_LINK_COMPONENTS + ARMCodeGen + ARMDesc + ARMInfo + CodeGen + Core + MC + Object + Support + Target + ) + +add_llvm_unittest(ARMTests + ELFStreamer.cpp + ) + +target_link_libraries(ARMTests LLVMTestingSupport) Index: unittests/Target/ARM/ELFStreamer.cpp =================================================================== --- /dev/null +++ unittests/Target/ARM/ELFStreamer.cpp @@ -0,0 +1,102 @@ +#include "ARMSubtarget.h" +#include "ARMTargetMachine.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +struct Generator { + std::unique_ptr MRI; + std::unique_ptr MAI; + std::unique_ptr MOFI; + std::unique_ptr MC; + std::unique_ptr MII; + std::unique_ptr MSTI; + std::unique_ptr MS; + + SmallString<4096> FileBytes; + std::unique_ptr Stream; + + Generator(); +}; + +Generator::Generator() { + Triple TheTriple(Triple::normalize("arm--")); + + LLVMInitializeARMTargetInfo(); + LLVMInitializeARMTarget(); + LLVMInitializeARMTargetMC(); + + std::string ErrorStr; + std::string TripleName; + + // Get the target. + const Target *TheTarget = + TargetRegistry::lookupTarget(TripleName, TheTriple, ErrorStr); + + TripleName = TheTriple.getTriple(); + + // Create all the MC Objects. + MRI.reset(TheTarget->createMCRegInfo(TripleName)); + MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName)); + MOFI.reset(new MCObjectFileInfo); + MC.reset(new MCContext(MAI.get(), MRI.get(), MOFI.get())); + MOFI->InitMCObjectFileInfo(TheTriple, /*PIC*/ false, *MC); + + MCTargetOptions Options; + auto MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, "", Options); + MII.reset(TheTarget->createMCInstrInfo()); + MSTI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", "")); + auto MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, *MC); + + Stream = make_unique(FileBytes); + + MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); + MS.reset(TheTarget->createMCObjectStreamer( + TheTriple, *MC, std::unique_ptr(MAB), *Stream, + std::unique_ptr(MCE), *MSTI, MCOptions.MCRelaxAll, + MCOptions.MCIncrementalLinkerCompatible, false)); +} + +} // anonymous namespace + +TEST(ELFStreamer, TestReset) { + Generator Gen; + + // Test that the OutStreamer can be reused without crashing + // A C++ unittest is needed here since no LLVM tool currently does this (reset and reuse) + // > llvm-mc only supports one input file and hence only one streamer, + // > clang will create a new streamer for each object + for (int i = 0; i < 10; i++) { + // Switch section back and forth and emit some random data between resets + // to trigger section switching logic. + auto s1 = Gen.MC->getELFSection(".foo", 0 /*Type*/, 0 /*Flags*/); + auto s2 = Gen.MC->getELFSection(".bar", 0 /*Type*/, 0 /*Flags*/); + Gen.MS->SwitchSection( s1); + Gen.MS->EmitBytes("0"); + Gen.MS->SwitchSection( s2); + Gen.MS->EmitBytes("0"); + Gen.MS->SwitchSection( s1); + Gen.MS->EmitBytes("0"); + Gen.MS->SwitchSection( s2); + Gen.MS->EmitBytes("0"); + Gen.MS->Finish(); + Gen.MS->reset(); + // Not really sure what to test about the output here. Just to make sure no crash for now... + } +}