Summary: This causes a segfault on ARM when (I think) the pass manager is used multiple times. Reset set the (last) current section to NULL without saving the corresponding LastEMSInfo back into the map. The next use of the streamer then save the LastEMSInfo for the NULL section leaving the LastEMSInfo mapping for the last current section (the one that was there before the reset) NULL which cause the LastEMSInfo to be set to NULL when the section is being used again. The reuse of the section (pointer) might mean that the map was holding dangling pointers previously which is why I went for clearing the map and resetting the info, making it as similar to the state right after the constructor run as possible. The AArch64 one doesn't have segfault (since LastEMS isn't a pointer) but it seems to have the same issue. The segfault is likely caused by https://reviews.llvm.org/D30724 which turns LastEMSInfo into a pointer. As mentioned above, it seems that the actual issue was older though. No test is included since the test is believed to be too complicated for such an obvious fix and not worth doing. Reviewers: llvm-commits, shankare, t.p.northover, peter.smith, rengolin Reviewed By: rengolin Subscribers: mgorny, aemerson, rengolin, javed.absar, kristof.beyls Differential Revision: https://reviews.llvm.org/D38588 llvm-svn: 316679
233 lines
7.5 KiB
C++
233 lines
7.5 KiB
C++
//===- lib/MC/AArch64ELFStreamer.cpp - ELF Object Output for AArch64 ------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file assembles .s files and emits AArch64 ELF .o object files. Different
|
|
// from generic ELF streamer in emitting mapping symbols ($x and $d) to delimit
|
|
// regions of data and code.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "AArch64TargetStreamer.h"
|
|
#include "AArch64WinCOFFStreamer.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/Triple.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/BinaryFormat/ELF.h"
|
|
#include "llvm/MC/MCAsmBackend.h"
|
|
#include "llvm/MC/MCAssembler.h"
|
|
#include "llvm/MC/MCCodeEmitter.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCELFStreamer.h"
|
|
#include "llvm/MC/MCExpr.h"
|
|
#include "llvm/MC/MCInst.h"
|
|
#include "llvm/MC/MCSection.h"
|
|
#include "llvm/MC/MCStreamer.h"
|
|
#include "llvm/MC/MCSubtargetInfo.h"
|
|
#include "llvm/MC/MCSymbolELF.h"
|
|
#include "llvm/MC/MCWinCOFFStreamer.h"
|
|
#include "llvm/Support/Casting.h"
|
|
#include "llvm/Support/FormattedStream.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
|
|
class AArch64ELFStreamer;
|
|
|
|
class AArch64TargetAsmStreamer : public AArch64TargetStreamer {
|
|
formatted_raw_ostream &OS;
|
|
|
|
void emitInst(uint32_t Inst) override;
|
|
|
|
public:
|
|
AArch64TargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS);
|
|
};
|
|
|
|
AArch64TargetAsmStreamer::AArch64TargetAsmStreamer(MCStreamer &S,
|
|
formatted_raw_ostream &OS)
|
|
: AArch64TargetStreamer(S), OS(OS) {}
|
|
|
|
void AArch64TargetAsmStreamer::emitInst(uint32_t Inst) {
|
|
OS << "\t.inst\t0x" << Twine::utohexstr(Inst) << "\n";
|
|
}
|
|
|
|
class AArch64TargetELFStreamer : public AArch64TargetStreamer {
|
|
private:
|
|
AArch64ELFStreamer &getStreamer();
|
|
|
|
void emitInst(uint32_t Inst) override;
|
|
|
|
public:
|
|
AArch64TargetELFStreamer(MCStreamer &S) : AArch64TargetStreamer(S) {}
|
|
};
|
|
|
|
/// Extend the generic ELFStreamer class so that it can emit mapping symbols at
|
|
/// the appropriate points in the object files. These symbols are defined in the
|
|
/// AArch64 ELF ABI:
|
|
/// infocenter.arm.com/help/topic/com.arm.doc.ihi0056a/IHI0056A_aaelf64.pdf
|
|
///
|
|
/// In brief: $x or $d should be emitted at the start of each contiguous region
|
|
/// of A64 code or data in a section. In practice, this emission does not rely
|
|
/// on explicit assembler directives but on inherent properties of the
|
|
/// directives doing the emission (e.g. ".byte" is data, "add x0, x0, x0" an
|
|
/// instruction).
|
|
///
|
|
/// As a result this system is orthogonal to the DataRegion infrastructure used
|
|
/// by MachO. Beware!
|
|
class AArch64ELFStreamer : public MCELFStreamer {
|
|
public:
|
|
friend class AArch64TargetELFStreamer;
|
|
|
|
AArch64ELFStreamer(MCContext &Context, std::unique_ptr<MCAsmBackend> TAB,
|
|
raw_pwrite_stream &OS,
|
|
std::unique_ptr<MCCodeEmitter> Emitter)
|
|
: MCELFStreamer(Context, std::move(TAB), OS, std::move(Emitter)),
|
|
MappingSymbolCounter(0), LastEMS(EMS_None) {}
|
|
|
|
void ChangeSection(MCSection *Section, const MCExpr *Subsection) override {
|
|
// We have to keep track of the mapping symbol state of any sections we
|
|
// use. Each one should start off as EMS_None, which is provided as the
|
|
// default constructor by DenseMap::lookup.
|
|
LastMappingSymbols[getPreviousSection().first] = LastEMS;
|
|
LastEMS = LastMappingSymbols.lookup(Section);
|
|
|
|
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.
|
|
void EmitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI,
|
|
bool) override {
|
|
EmitA64MappingSymbol();
|
|
MCELFStreamer::EmitInstruction(Inst, STI);
|
|
}
|
|
|
|
/// Emit a 32-bit value as an instruction. This is only used for the .inst
|
|
/// directive, EmitInstruction should be used in other cases.
|
|
void emitInst(uint32_t Inst) {
|
|
char Buffer[4];
|
|
|
|
// We can't just use EmitIntValue here, as that will emit a data mapping
|
|
// symbol, and swap the endianness on big-endian systems (instructions are
|
|
// always little-endian).
|
|
for (unsigned I = 0; I < 4; ++I) {
|
|
Buffer[I] = uint8_t(Inst);
|
|
Inst >>= 8;
|
|
}
|
|
|
|
EmitA64MappingSymbol();
|
|
MCELFStreamer::EmitBytes(StringRef(Buffer, 4));
|
|
}
|
|
|
|
/// This is one of the functions used to emit data into an ELF section, so the
|
|
/// AArch64 streamer overrides it to add the appropriate mapping symbol ($d)
|
|
/// if necessary.
|
|
void EmitBytes(StringRef Data) override {
|
|
EmitDataMappingSymbol();
|
|
MCELFStreamer::EmitBytes(Data);
|
|
}
|
|
|
|
/// This is one of the functions used to emit data into an ELF section, so the
|
|
/// AArch64 streamer overrides it to add the appropriate mapping symbol ($d)
|
|
/// if necessary.
|
|
void EmitValueImpl(const MCExpr *Value, unsigned Size, SMLoc Loc) override {
|
|
EmitDataMappingSymbol();
|
|
MCELFStreamer::EmitValueImpl(Value, Size, Loc);
|
|
}
|
|
|
|
private:
|
|
enum ElfMappingSymbol {
|
|
EMS_None,
|
|
EMS_A64,
|
|
EMS_Data
|
|
};
|
|
|
|
void EmitDataMappingSymbol() {
|
|
if (LastEMS == EMS_Data)
|
|
return;
|
|
EmitMappingSymbol("$d");
|
|
LastEMS = EMS_Data;
|
|
}
|
|
|
|
void EmitA64MappingSymbol() {
|
|
if (LastEMS == EMS_A64)
|
|
return;
|
|
EmitMappingSymbol("$x");
|
|
LastEMS = EMS_A64;
|
|
}
|
|
|
|
void EmitMappingSymbol(StringRef Name) {
|
|
auto *Symbol = cast<MCSymbolELF>(getContext().getOrCreateSymbol(
|
|
Name + "." + Twine(MappingSymbolCounter++)));
|
|
EmitLabel(Symbol);
|
|
Symbol->setType(ELF::STT_NOTYPE);
|
|
Symbol->setBinding(ELF::STB_LOCAL);
|
|
Symbol->setExternal(false);
|
|
}
|
|
|
|
int64_t MappingSymbolCounter;
|
|
|
|
DenseMap<const MCSection *, ElfMappingSymbol> LastMappingSymbols;
|
|
ElfMappingSymbol LastEMS;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
AArch64ELFStreamer &AArch64TargetELFStreamer::getStreamer() {
|
|
return static_cast<AArch64ELFStreamer &>(Streamer);
|
|
}
|
|
|
|
void AArch64TargetELFStreamer::emitInst(uint32_t Inst) {
|
|
getStreamer().emitInst(Inst);
|
|
}
|
|
|
|
namespace llvm {
|
|
|
|
MCTargetStreamer *createAArch64AsmTargetStreamer(MCStreamer &S,
|
|
formatted_raw_ostream &OS,
|
|
MCInstPrinter *InstPrint,
|
|
bool isVerboseAsm) {
|
|
return new AArch64TargetAsmStreamer(S, OS);
|
|
}
|
|
|
|
MCELFStreamer *createAArch64ELFStreamer(MCContext &Context,
|
|
std::unique_ptr<MCAsmBackend> TAB,
|
|
raw_pwrite_stream &OS,
|
|
std::unique_ptr<MCCodeEmitter> Emitter,
|
|
bool RelaxAll) {
|
|
AArch64ELFStreamer *S =
|
|
new AArch64ELFStreamer(Context, std::move(TAB), OS, std::move(Emitter));
|
|
if (RelaxAll)
|
|
S->getAssembler().setRelaxAll(true);
|
|
return S;
|
|
}
|
|
|
|
MCTargetStreamer *
|
|
createAArch64ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) {
|
|
const Triple &TT = STI.getTargetTriple();
|
|
if (TT.isOSBinFormatELF())
|
|
return new AArch64TargetELFStreamer(S);
|
|
if (TT.isOSBinFormatCOFF())
|
|
return new AArch64TargetWinCOFFStreamer(S);
|
|
return nullptr;
|
|
}
|
|
|
|
} // end namespace llvm
|