mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 04:02:41 +01:00
873caddeb4
DWARF v5 specifies that the root file (also given in the DW_AT_name attribute of the compilation unit DIE) should be emitted explicitly to the line table's list of files. This makes the line table more independent of the .debug_info section. We emit the new syntax only for DWARF v5 and later. Fixes the bug found by asan. Also XFAIL the new test for Darwin, which is stuck on DWARF v2, and fix up other tests so they stop failing on Windows. Last but not least, don't break "clang -g" of an assembler file that has .file directives in it. Differential Revision: https://reviews.llvm.org/D44054 llvm-svn: 328805
1823 lines
64 KiB
C++
1823 lines
64 KiB
C++
//===- lib/MC/MCDwarf.cpp - MCDwarf implementation ------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/MC/MCDwarf.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/Hashing.h"
|
|
#include "llvm/ADT/Optional.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/BinaryFormat/Dwarf.h"
|
|
#include "llvm/Config/config.h"
|
|
#include "llvm/MC/MCAsmInfo.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCExpr.h"
|
|
#include "llvm/MC/MCObjectFileInfo.h"
|
|
#include "llvm/MC/MCObjectStreamer.h"
|
|
#include "llvm/MC/MCRegisterInfo.h"
|
|
#include "llvm/MC/MCSection.h"
|
|
#include "llvm/MC/MCStreamer.h"
|
|
#include "llvm/MC/MCSymbol.h"
|
|
#include "llvm/MC/StringTableBuilder.h"
|
|
#include "llvm/Support/Casting.h"
|
|
#include "llvm/Support/Endian.h"
|
|
#include "llvm/Support/EndianStream.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/LEB128.h"
|
|
#include "llvm/Support/MathExtras.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <cassert>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
using namespace llvm;
|
|
|
|
/// Manage the .debug_line_str section contents, if we use it.
|
|
class llvm::MCDwarfLineStr {
|
|
MCSymbol *LineStrLabel = nullptr;
|
|
StringTableBuilder LineStrings{StringTableBuilder::DWARF};
|
|
bool UseRelocs = false;
|
|
|
|
public:
|
|
/// Construct an instance that can emit .debug_line_str (for use in a normal
|
|
/// v5 line table).
|
|
explicit MCDwarfLineStr(MCContext &Ctx) {
|
|
UseRelocs = Ctx.getAsmInfo()->doesDwarfUseRelocationsAcrossSections();
|
|
if (UseRelocs)
|
|
LineStrLabel =
|
|
Ctx.getObjectFileInfo()->getDwarfLineStrSection()->getBeginSymbol();
|
|
}
|
|
|
|
/// Emit a reference to the string.
|
|
void emitRef(MCStreamer *MCOS, StringRef Path);
|
|
|
|
/// Emit the .debug_line_str section if appropriate.
|
|
void emitSection(MCStreamer *MCOS);
|
|
};
|
|
|
|
static inline uint64_t ScaleAddrDelta(MCContext &Context, uint64_t AddrDelta) {
|
|
unsigned MinInsnLength = Context.getAsmInfo()->getMinInstAlignment();
|
|
if (MinInsnLength == 1)
|
|
return AddrDelta;
|
|
if (AddrDelta % MinInsnLength != 0) {
|
|
// TODO: report this error, but really only once.
|
|
;
|
|
}
|
|
return AddrDelta / MinInsnLength;
|
|
}
|
|
|
|
//
|
|
// This is called when an instruction is assembled into the specified section
|
|
// and if there is information from the last .loc directive that has yet to have
|
|
// a line entry made for it is made.
|
|
//
|
|
void MCDwarfLineEntry::Make(MCObjectStreamer *MCOS, MCSection *Section) {
|
|
if (!MCOS->getContext().getDwarfLocSeen())
|
|
return;
|
|
|
|
// Create a symbol at in the current section for use in the line entry.
|
|
MCSymbol *LineSym = MCOS->getContext().createTempSymbol();
|
|
// Set the value of the symbol to use for the MCDwarfLineEntry.
|
|
MCOS->EmitLabel(LineSym);
|
|
|
|
// Get the current .loc info saved in the context.
|
|
const MCDwarfLoc &DwarfLoc = MCOS->getContext().getCurrentDwarfLoc();
|
|
|
|
// Create a (local) line entry with the symbol and the current .loc info.
|
|
MCDwarfLineEntry LineEntry(LineSym, DwarfLoc);
|
|
|
|
// clear DwarfLocSeen saying the current .loc info is now used.
|
|
MCOS->getContext().clearDwarfLocSeen();
|
|
|
|
// Add the line entry to this section's entries.
|
|
MCOS->getContext()
|
|
.getMCDwarfLineTable(MCOS->getContext().getDwarfCompileUnitID())
|
|
.getMCLineSections()
|
|
.addLineEntry(LineEntry, Section);
|
|
}
|
|
|
|
//
|
|
// This helper routine returns an expression of End - Start + IntVal .
|
|
//
|
|
static inline const MCExpr *MakeStartMinusEndExpr(const MCStreamer &MCOS,
|
|
const MCSymbol &Start,
|
|
const MCSymbol &End,
|
|
int IntVal) {
|
|
MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None;
|
|
const MCExpr *Res =
|
|
MCSymbolRefExpr::create(&End, Variant, MCOS.getContext());
|
|
const MCExpr *RHS =
|
|
MCSymbolRefExpr::create(&Start, Variant, MCOS.getContext());
|
|
const MCExpr *Res1 =
|
|
MCBinaryExpr::create(MCBinaryExpr::Sub, Res, RHS, MCOS.getContext());
|
|
const MCExpr *Res2 =
|
|
MCConstantExpr::create(IntVal, MCOS.getContext());
|
|
const MCExpr *Res3 =
|
|
MCBinaryExpr::create(MCBinaryExpr::Sub, Res1, Res2, MCOS.getContext());
|
|
return Res3;
|
|
}
|
|
|
|
//
|
|
// This helper routine returns an expression of Start + IntVal .
|
|
//
|
|
static inline const MCExpr *
|
|
makeStartPlusIntExpr(MCContext &Ctx, const MCSymbol &Start, int IntVal) {
|
|
MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None;
|
|
const MCExpr *LHS = MCSymbolRefExpr::create(&Start, Variant, Ctx);
|
|
const MCExpr *RHS = MCConstantExpr::create(IntVal, Ctx);
|
|
const MCExpr *Res = MCBinaryExpr::create(MCBinaryExpr::Add, LHS, RHS, Ctx);
|
|
return Res;
|
|
}
|
|
|
|
//
|
|
// This emits the Dwarf line table for the specified section from the entries
|
|
// in the LineSection.
|
|
//
|
|
static inline void
|
|
EmitDwarfLineTable(MCObjectStreamer *MCOS, MCSection *Section,
|
|
const MCLineSection::MCDwarfLineEntryCollection &LineEntries) {
|
|
unsigned FileNum = 1;
|
|
unsigned LastLine = 1;
|
|
unsigned Column = 0;
|
|
unsigned Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0;
|
|
unsigned Isa = 0;
|
|
unsigned Discriminator = 0;
|
|
MCSymbol *LastLabel = nullptr;
|
|
|
|
// Loop through each MCDwarfLineEntry and encode the dwarf line number table.
|
|
for (const MCDwarfLineEntry &LineEntry : LineEntries) {
|
|
int64_t LineDelta = static_cast<int64_t>(LineEntry.getLine()) - LastLine;
|
|
|
|
if (FileNum != LineEntry.getFileNum()) {
|
|
FileNum = LineEntry.getFileNum();
|
|
MCOS->EmitIntValue(dwarf::DW_LNS_set_file, 1);
|
|
MCOS->EmitULEB128IntValue(FileNum);
|
|
}
|
|
if (Column != LineEntry.getColumn()) {
|
|
Column = LineEntry.getColumn();
|
|
MCOS->EmitIntValue(dwarf::DW_LNS_set_column, 1);
|
|
MCOS->EmitULEB128IntValue(Column);
|
|
}
|
|
if (Discriminator != LineEntry.getDiscriminator() &&
|
|
MCOS->getContext().getDwarfVersion() >= 4) {
|
|
Discriminator = LineEntry.getDiscriminator();
|
|
unsigned Size = getULEB128Size(Discriminator);
|
|
MCOS->EmitIntValue(dwarf::DW_LNS_extended_op, 1);
|
|
MCOS->EmitULEB128IntValue(Size + 1);
|
|
MCOS->EmitIntValue(dwarf::DW_LNE_set_discriminator, 1);
|
|
MCOS->EmitULEB128IntValue(Discriminator);
|
|
}
|
|
if (Isa != LineEntry.getIsa()) {
|
|
Isa = LineEntry.getIsa();
|
|
MCOS->EmitIntValue(dwarf::DW_LNS_set_isa, 1);
|
|
MCOS->EmitULEB128IntValue(Isa);
|
|
}
|
|
if ((LineEntry.getFlags() ^ Flags) & DWARF2_FLAG_IS_STMT) {
|
|
Flags = LineEntry.getFlags();
|
|
MCOS->EmitIntValue(dwarf::DW_LNS_negate_stmt, 1);
|
|
}
|
|
if (LineEntry.getFlags() & DWARF2_FLAG_BASIC_BLOCK)
|
|
MCOS->EmitIntValue(dwarf::DW_LNS_set_basic_block, 1);
|
|
if (LineEntry.getFlags() & DWARF2_FLAG_PROLOGUE_END)
|
|
MCOS->EmitIntValue(dwarf::DW_LNS_set_prologue_end, 1);
|
|
if (LineEntry.getFlags() & DWARF2_FLAG_EPILOGUE_BEGIN)
|
|
MCOS->EmitIntValue(dwarf::DW_LNS_set_epilogue_begin, 1);
|
|
|
|
MCSymbol *Label = LineEntry.getLabel();
|
|
|
|
// At this point we want to emit/create the sequence to encode the delta in
|
|
// line numbers and the increment of the address from the previous Label
|
|
// and the current Label.
|
|
const MCAsmInfo *asmInfo = MCOS->getContext().getAsmInfo();
|
|
MCOS->EmitDwarfAdvanceLineAddr(LineDelta, LastLabel, Label,
|
|
asmInfo->getCodePointerSize());
|
|
|
|
Discriminator = 0;
|
|
LastLine = LineEntry.getLine();
|
|
LastLabel = Label;
|
|
}
|
|
|
|
// Emit a DW_LNE_end_sequence for the end of the section.
|
|
// Use the section end label to compute the address delta and use INT64_MAX
|
|
// as the line delta which is the signal that this is actually a
|
|
// DW_LNE_end_sequence.
|
|
MCSymbol *SectionEnd = MCOS->endSection(Section);
|
|
|
|
// Switch back the dwarf line section, in case endSection had to switch the
|
|
// section.
|
|
MCContext &Ctx = MCOS->getContext();
|
|
MCOS->SwitchSection(Ctx.getObjectFileInfo()->getDwarfLineSection());
|
|
|
|
const MCAsmInfo *AsmInfo = Ctx.getAsmInfo();
|
|
MCOS->EmitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, SectionEnd,
|
|
AsmInfo->getCodePointerSize());
|
|
}
|
|
|
|
//
|
|
// This emits the Dwarf file and the line tables.
|
|
//
|
|
void MCDwarfLineTable::Emit(MCObjectStreamer *MCOS,
|
|
MCDwarfLineTableParams Params) {
|
|
MCContext &context = MCOS->getContext();
|
|
|
|
auto &LineTables = context.getMCDwarfLineTables();
|
|
|
|
// Bail out early so we don't switch to the debug_line section needlessly and
|
|
// in doing so create an unnecessary (if empty) section.
|
|
if (LineTables.empty())
|
|
return;
|
|
|
|
// In a v5 non-split line table, put the strings in a separate section.
|
|
Optional<MCDwarfLineStr> LineStr;
|
|
if (context.getDwarfVersion() >= 5)
|
|
LineStr = MCDwarfLineStr(context);
|
|
|
|
// Switch to the section where the table will be emitted into.
|
|
MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfLineSection());
|
|
|
|
// Handle the rest of the Compile Units.
|
|
for (const auto &CUIDTablePair : LineTables)
|
|
CUIDTablePair.second.EmitCU(MCOS, Params, LineStr);
|
|
|
|
if (LineStr)
|
|
LineStr->emitSection(MCOS);
|
|
}
|
|
|
|
void MCDwarfDwoLineTable::Emit(MCStreamer &MCOS, MCDwarfLineTableParams Params,
|
|
MCSection *Section) const {
|
|
if (Header.MCDwarfFiles.empty())
|
|
return;
|
|
Optional<MCDwarfLineStr> NoLineStr(None);
|
|
MCOS.SwitchSection(Section);
|
|
MCOS.EmitLabel(Header.Emit(&MCOS, Params, None, NoLineStr).second);
|
|
}
|
|
|
|
std::pair<MCSymbol *, MCSymbol *>
|
|
MCDwarfLineTableHeader::Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params,
|
|
Optional<MCDwarfLineStr> &LineStr) const {
|
|
static const char StandardOpcodeLengths[] = {
|
|
0, // length of DW_LNS_copy
|
|
1, // length of DW_LNS_advance_pc
|
|
1, // length of DW_LNS_advance_line
|
|
1, // length of DW_LNS_set_file
|
|
1, // length of DW_LNS_set_column
|
|
0, // length of DW_LNS_negate_stmt
|
|
0, // length of DW_LNS_set_basic_block
|
|
0, // length of DW_LNS_const_add_pc
|
|
1, // length of DW_LNS_fixed_advance_pc
|
|
0, // length of DW_LNS_set_prologue_end
|
|
0, // length of DW_LNS_set_epilogue_begin
|
|
1 // DW_LNS_set_isa
|
|
};
|
|
assert(array_lengthof(StandardOpcodeLengths) >=
|
|
(Params.DWARF2LineOpcodeBase - 1U));
|
|
return Emit(
|
|
MCOS, Params,
|
|
makeArrayRef(StandardOpcodeLengths, Params.DWARF2LineOpcodeBase - 1),
|
|
LineStr);
|
|
}
|
|
|
|
static const MCExpr *forceExpAbs(MCStreamer &OS, const MCExpr* Expr) {
|
|
MCContext &Context = OS.getContext();
|
|
assert(!isa<MCSymbolRefExpr>(Expr));
|
|
if (Context.getAsmInfo()->hasAggressiveSymbolFolding())
|
|
return Expr;
|
|
|
|
MCSymbol *ABS = Context.createTempSymbol();
|
|
OS.EmitAssignment(ABS, Expr);
|
|
return MCSymbolRefExpr::create(ABS, Context);
|
|
}
|
|
|
|
static void emitAbsValue(MCStreamer &OS, const MCExpr *Value, unsigned Size) {
|
|
const MCExpr *ABS = forceExpAbs(OS, Value);
|
|
OS.EmitValue(ABS, Size);
|
|
}
|
|
|
|
void MCDwarfLineStr::emitSection(MCStreamer *MCOS) {
|
|
// Switch to the .debug_line_str section.
|
|
MCOS->SwitchSection(
|
|
MCOS->getContext().getObjectFileInfo()->getDwarfLineStrSection());
|
|
// Emit the strings without perturbing the offsets we used.
|
|
LineStrings.finalizeInOrder();
|
|
SmallString<0> Data;
|
|
Data.resize(LineStrings.getSize());
|
|
LineStrings.write((uint8_t *)Data.data());
|
|
MCOS->EmitBinaryData(Data.str());
|
|
}
|
|
|
|
void MCDwarfLineStr::emitRef(MCStreamer *MCOS, StringRef Path) {
|
|
int RefSize = 4; // FIXME: Support DWARF-64
|
|
size_t Offset = LineStrings.add(Path);
|
|
if (UseRelocs) {
|
|
MCContext &Ctx = MCOS->getContext();
|
|
MCOS->EmitValue(makeStartPlusIntExpr(Ctx, *LineStrLabel, Offset), RefSize);
|
|
} else
|
|
MCOS->EmitIntValue(Offset, RefSize);
|
|
}
|
|
|
|
void MCDwarfLineTableHeader::emitV2FileDirTables(MCStreamer *MCOS) const {
|
|
// First the directory table.
|
|
for (auto &Dir : MCDwarfDirs) {
|
|
MCOS->EmitBytes(Dir); // The DirectoryName, and...
|
|
MCOS->EmitBytes(StringRef("\0", 1)); // its null terminator.
|
|
}
|
|
MCOS->EmitIntValue(0, 1); // Terminate the directory list.
|
|
|
|
// Second the file table.
|
|
for (unsigned i = 1; i < MCDwarfFiles.size(); i++) {
|
|
assert(!MCDwarfFiles[i].Name.empty());
|
|
MCOS->EmitBytes(MCDwarfFiles[i].Name); // FileName and...
|
|
MCOS->EmitBytes(StringRef("\0", 1)); // its null terminator.
|
|
MCOS->EmitULEB128IntValue(MCDwarfFiles[i].DirIndex); // Directory number.
|
|
MCOS->EmitIntValue(0, 1); // Last modification timestamp (always 0).
|
|
MCOS->EmitIntValue(0, 1); // File size (always 0).
|
|
}
|
|
MCOS->EmitIntValue(0, 1); // Terminate the file list.
|
|
}
|
|
|
|
static void emitOneV5FileEntry(MCStreamer *MCOS, const MCDwarfFile &DwarfFile,
|
|
bool HasMD5, bool HasSource,
|
|
Optional<MCDwarfLineStr> &LineStr) {
|
|
assert(!DwarfFile.Name.empty());
|
|
if (LineStr)
|
|
LineStr->emitRef(MCOS, DwarfFile.Name);
|
|
else {
|
|
MCOS->EmitBytes(DwarfFile.Name); // FileName and...
|
|
MCOS->EmitBytes(StringRef("\0", 1)); // its null terminator.
|
|
}
|
|
MCOS->EmitULEB128IntValue(DwarfFile.DirIndex); // Directory number.
|
|
if (HasMD5) {
|
|
MD5::MD5Result *Cksum = DwarfFile.Checksum;
|
|
MCOS->EmitBinaryData(
|
|
StringRef(reinterpret_cast<const char *>(Cksum->Bytes.data()),
|
|
Cksum->Bytes.size()));
|
|
}
|
|
if (HasSource) {
|
|
if (LineStr)
|
|
LineStr->emitRef(MCOS, DwarfFile.Source.getValueOr(StringRef()));
|
|
else {
|
|
MCOS->EmitBytes(
|
|
DwarfFile.Source.getValueOr(StringRef())); // Source and...
|
|
MCOS->EmitBytes(StringRef("\0", 1)); // its null terminator.
|
|
}
|
|
}
|
|
}
|
|
|
|
void MCDwarfLineTableHeader::emitV5FileDirTables(
|
|
MCStreamer *MCOS, Optional<MCDwarfLineStr> &LineStr) const {
|
|
// The directory format, which is just a list of the directory paths. In a
|
|
// non-split object, these are references to .debug_line_str; in a split
|
|
// object, they are inline strings.
|
|
MCOS->EmitIntValue(1, 1);
|
|
MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_path);
|
|
MCOS->EmitULEB128IntValue(LineStr ? dwarf::DW_FORM_line_strp
|
|
: dwarf::DW_FORM_string);
|
|
MCOS->EmitULEB128IntValue(MCDwarfDirs.size() + 1);
|
|
if (LineStr) {
|
|
// Record path strings, emit references here.
|
|
LineStr->emitRef(MCOS, CompilationDir);
|
|
for (auto &Dir : MCDwarfDirs)
|
|
LineStr->emitRef(MCOS, Dir);
|
|
} else {
|
|
// The list of directory paths. CompilationDir comes first.
|
|
MCOS->EmitBytes(CompilationDir);
|
|
MCOS->EmitBytes(StringRef("\0", 1));
|
|
for (auto &Dir : MCDwarfDirs) {
|
|
MCOS->EmitBytes(Dir); // The DirectoryName, and...
|
|
MCOS->EmitBytes(StringRef("\0", 1)); // its null terminator.
|
|
}
|
|
}
|
|
|
|
// The file format, which is the inline null-terminated filename and a
|
|
// directory index. We don't track file size/timestamp so don't emit them
|
|
// in the v5 table. Emit MD5 checksums and source if we have them.
|
|
uint64_t Entries = 2;
|
|
if (HasMD5)
|
|
Entries += 1;
|
|
if (HasSource)
|
|
Entries += 1;
|
|
MCOS->EmitIntValue(Entries, 1);
|
|
MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_path);
|
|
MCOS->EmitULEB128IntValue(LineStr ? dwarf::DW_FORM_line_strp
|
|
: dwarf::DW_FORM_string);
|
|
MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_directory_index);
|
|
MCOS->EmitULEB128IntValue(dwarf::DW_FORM_udata);
|
|
if (HasMD5) {
|
|
MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_MD5);
|
|
MCOS->EmitULEB128IntValue(dwarf::DW_FORM_data16);
|
|
}
|
|
if (HasSource) {
|
|
MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_LLVM_source);
|
|
MCOS->EmitULEB128IntValue(LineStr ? dwarf::DW_FORM_line_strp
|
|
: dwarf::DW_FORM_string);
|
|
}
|
|
// Then the counted list of files. The root file is file #0, then emit the
|
|
// files as provide by .file directives.
|
|
MCOS->EmitULEB128IntValue(MCDwarfFiles.size());
|
|
emitOneV5FileEntry(MCOS, RootFile, HasMD5, HasSource, LineStr);
|
|
for (unsigned i = 1; i < MCDwarfFiles.size(); ++i)
|
|
emitOneV5FileEntry(MCOS, MCDwarfFiles[i], HasMD5, HasSource, LineStr);
|
|
}
|
|
|
|
std::pair<MCSymbol *, MCSymbol *>
|
|
MCDwarfLineTableHeader::Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params,
|
|
ArrayRef<char> StandardOpcodeLengths,
|
|
Optional<MCDwarfLineStr> &LineStr) const {
|
|
MCContext &context = MCOS->getContext();
|
|
|
|
// Create a symbol at the beginning of the line table.
|
|
MCSymbol *LineStartSym = Label;
|
|
if (!LineStartSym)
|
|
LineStartSym = context.createTempSymbol();
|
|
// Set the value of the symbol, as we are at the start of the line table.
|
|
MCOS->EmitLabel(LineStartSym);
|
|
|
|
// Create a symbol for the end of the section (to be set when we get there).
|
|
MCSymbol *LineEndSym = context.createTempSymbol();
|
|
|
|
// The first 4 bytes is the total length of the information for this
|
|
// compilation unit (not including these 4 bytes for the length).
|
|
emitAbsValue(*MCOS,
|
|
MakeStartMinusEndExpr(*MCOS, *LineStartSym, *LineEndSym, 4), 4);
|
|
|
|
// Next 2 bytes is the Version.
|
|
// FIXME: On Darwin we still default to V2.
|
|
unsigned LineTableVersion = context.getDwarfVersion();
|
|
if (context.getObjectFileInfo()->getTargetTriple().isOSDarwin())
|
|
LineTableVersion = 2;
|
|
MCOS->EmitIntValue(LineTableVersion, 2);
|
|
|
|
// Keep track of the bytes between the very start and where the header length
|
|
// comes out.
|
|
unsigned PreHeaderLengthBytes = 4 + 2;
|
|
|
|
// In v5, we get address info next.
|
|
if (LineTableVersion >= 5) {
|
|
MCOS->EmitIntValue(context.getAsmInfo()->getCodePointerSize(), 1);
|
|
MCOS->EmitIntValue(0, 1); // Segment selector; same as EmitGenDwarfAranges.
|
|
PreHeaderLengthBytes += 2;
|
|
}
|
|
|
|
// Create a symbol for the end of the prologue (to be set when we get there).
|
|
MCSymbol *ProEndSym = context.createTempSymbol(); // Lprologue_end
|
|
|
|
// Length of the prologue, is the next 4 bytes. This is actually the length
|
|
// from after the length word, to the end of the prologue.
|
|
emitAbsValue(*MCOS,
|
|
MakeStartMinusEndExpr(*MCOS, *LineStartSym, *ProEndSym,
|
|
(PreHeaderLengthBytes + 4)),
|
|
4);
|
|
|
|
// Parameters of the state machine, are next.
|
|
MCOS->EmitIntValue(context.getAsmInfo()->getMinInstAlignment(), 1);
|
|
// maximum_operations_per_instruction
|
|
// For non-VLIW architectures this field is always 1.
|
|
// FIXME: VLIW architectures need to update this field accordingly.
|
|
if (LineTableVersion >= 4)
|
|
MCOS->EmitIntValue(1, 1);
|
|
MCOS->EmitIntValue(DWARF2_LINE_DEFAULT_IS_STMT, 1);
|
|
MCOS->EmitIntValue(Params.DWARF2LineBase, 1);
|
|
MCOS->EmitIntValue(Params.DWARF2LineRange, 1);
|
|
MCOS->EmitIntValue(StandardOpcodeLengths.size() + 1, 1);
|
|
|
|
// Standard opcode lengths
|
|
for (char Length : StandardOpcodeLengths)
|
|
MCOS->EmitIntValue(Length, 1);
|
|
|
|
// Put out the directory and file tables. The formats vary depending on
|
|
// the version.
|
|
if (LineTableVersion >= 5)
|
|
emitV5FileDirTables(MCOS, LineStr);
|
|
else
|
|
emitV2FileDirTables(MCOS);
|
|
|
|
// This is the end of the prologue, so set the value of the symbol at the
|
|
// end of the prologue (that was used in a previous expression).
|
|
MCOS->EmitLabel(ProEndSym);
|
|
|
|
return std::make_pair(LineStartSym, LineEndSym);
|
|
}
|
|
|
|
void MCDwarfLineTable::EmitCU(MCObjectStreamer *MCOS,
|
|
MCDwarfLineTableParams Params,
|
|
Optional<MCDwarfLineStr> &LineStr) const {
|
|
MCSymbol *LineEndSym = Header.Emit(MCOS, Params, LineStr).second;
|
|
|
|
// Put out the line tables.
|
|
for (const auto &LineSec : MCLineSections.getMCLineEntries())
|
|
EmitDwarfLineTable(MCOS, LineSec.first, LineSec.second);
|
|
|
|
// This is the end of the section, so set the value of the symbol at the end
|
|
// of this section (that was used in a previous expression).
|
|
MCOS->EmitLabel(LineEndSym);
|
|
}
|
|
|
|
Expected<unsigned> MCDwarfLineTable::tryGetFile(StringRef &Directory,
|
|
StringRef &FileName,
|
|
MD5::MD5Result *Checksum,
|
|
Optional<StringRef> Source,
|
|
unsigned FileNumber) {
|
|
return Header.tryGetFile(Directory, FileName, Checksum, Source, FileNumber);
|
|
}
|
|
|
|
Expected<unsigned>
|
|
MCDwarfLineTableHeader::tryGetFile(StringRef &Directory,
|
|
StringRef &FileName,
|
|
MD5::MD5Result *Checksum,
|
|
Optional<StringRef> &Source,
|
|
unsigned FileNumber) {
|
|
if (Directory == CompilationDir)
|
|
Directory = "";
|
|
if (FileName.empty()) {
|
|
FileName = "<stdin>";
|
|
Directory = "";
|
|
}
|
|
assert(!FileName.empty());
|
|
// If any files have an MD5 checksum or embedded source, they all must.
|
|
if (MCDwarfFiles.empty()) {
|
|
HasMD5 = (Checksum != nullptr);
|
|
HasSource = (Source != None);
|
|
}
|
|
if (FileNumber == 0) {
|
|
// File numbers start with 1 and/or after any file numbers
|
|
// allocated by inline-assembler .file directives.
|
|
FileNumber = MCDwarfFiles.empty() ? 1 : MCDwarfFiles.size();
|
|
SmallString<256> Buffer;
|
|
auto IterBool = SourceIdMap.insert(
|
|
std::make_pair((Directory + Twine('\0') + FileName).toStringRef(Buffer),
|
|
FileNumber));
|
|
if (!IterBool.second)
|
|
return IterBool.first->second;
|
|
}
|
|
// Make space for this FileNumber in the MCDwarfFiles vector if needed.
|
|
if (FileNumber >= MCDwarfFiles.size())
|
|
MCDwarfFiles.resize(FileNumber + 1);
|
|
|
|
// Get the new MCDwarfFile slot for this FileNumber.
|
|
MCDwarfFile &File = MCDwarfFiles[FileNumber];
|
|
|
|
// It is an error to see the same number more than once.
|
|
if (!File.Name.empty())
|
|
return make_error<StringError>("file number already allocated",
|
|
inconvertibleErrorCode());
|
|
|
|
// If any files have an MD5 checksum, they all must.
|
|
if (HasMD5 != (Checksum != nullptr))
|
|
return make_error<StringError>("inconsistent use of MD5 checksums",
|
|
inconvertibleErrorCode());
|
|
// If any files have embedded source, they all must.
|
|
if (HasSource != (Source != None))
|
|
return make_error<StringError>("inconsistent use of embedded source",
|
|
inconvertibleErrorCode());
|
|
|
|
if (Directory.empty()) {
|
|
// Separate the directory part from the basename of the FileName.
|
|
StringRef tFileName = sys::path::filename(FileName);
|
|
if (!tFileName.empty()) {
|
|
Directory = sys::path::parent_path(FileName);
|
|
if (!Directory.empty())
|
|
FileName = tFileName;
|
|
}
|
|
}
|
|
|
|
// Find or make an entry in the MCDwarfDirs vector for this Directory.
|
|
// Capture directory name.
|
|
unsigned DirIndex;
|
|
if (Directory.empty()) {
|
|
// For FileNames with no directories a DirIndex of 0 is used.
|
|
DirIndex = 0;
|
|
} else {
|
|
DirIndex = 0;
|
|
for (unsigned End = MCDwarfDirs.size(); DirIndex < End; DirIndex++) {
|
|
if (Directory == MCDwarfDirs[DirIndex])
|
|
break;
|
|
}
|
|
if (DirIndex >= MCDwarfDirs.size())
|
|
MCDwarfDirs.push_back(Directory);
|
|
// The DirIndex is one based, as DirIndex of 0 is used for FileNames with
|
|
// no directories. MCDwarfDirs[] is unlike MCDwarfFiles[] in that the
|
|
// directory names are stored at MCDwarfDirs[DirIndex-1] where FileNames
|
|
// are stored at MCDwarfFiles[FileNumber].Name .
|
|
DirIndex++;
|
|
}
|
|
|
|
File.Name = FileName;
|
|
File.DirIndex = DirIndex;
|
|
File.Checksum = Checksum;
|
|
if (Checksum)
|
|
HasMD5 = true;
|
|
File.Source = Source;
|
|
if (Source)
|
|
HasSource = true;
|
|
|
|
// return the allocated FileNumber.
|
|
return FileNumber;
|
|
}
|
|
|
|
/// Utility function to emit the encoding to a streamer.
|
|
void MCDwarfLineAddr::Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params,
|
|
int64_t LineDelta, uint64_t AddrDelta) {
|
|
MCContext &Context = MCOS->getContext();
|
|
SmallString<256> Tmp;
|
|
raw_svector_ostream OS(Tmp);
|
|
MCDwarfLineAddr::Encode(Context, Params, LineDelta, AddrDelta, OS);
|
|
MCOS->EmitBytes(OS.str());
|
|
}
|
|
|
|
/// Given a special op, return the address skip amount (in units of
|
|
/// DWARF2_LINE_MIN_INSN_LENGTH).
|
|
static uint64_t SpecialAddr(MCDwarfLineTableParams Params, uint64_t op) {
|
|
return (op - Params.DWARF2LineOpcodeBase) / Params.DWARF2LineRange;
|
|
}
|
|
|
|
/// Utility function to encode a Dwarf pair of LineDelta and AddrDeltas.
|
|
void MCDwarfLineAddr::Encode(MCContext &Context, MCDwarfLineTableParams Params,
|
|
int64_t LineDelta, uint64_t AddrDelta,
|
|
raw_ostream &OS) {
|
|
uint64_t Temp, Opcode;
|
|
bool NeedCopy = false;
|
|
|
|
// The maximum address skip amount that can be encoded with a special op.
|
|
uint64_t MaxSpecialAddrDelta = SpecialAddr(Params, 255);
|
|
|
|
// Scale the address delta by the minimum instruction length.
|
|
AddrDelta = ScaleAddrDelta(Context, AddrDelta);
|
|
|
|
// A LineDelta of INT64_MAX is a signal that this is actually a
|
|
// DW_LNE_end_sequence. We cannot use special opcodes here, since we want the
|
|
// end_sequence to emit the matrix entry.
|
|
if (LineDelta == INT64_MAX) {
|
|
if (AddrDelta == MaxSpecialAddrDelta)
|
|
OS << char(dwarf::DW_LNS_const_add_pc);
|
|
else if (AddrDelta) {
|
|
OS << char(dwarf::DW_LNS_advance_pc);
|
|
encodeULEB128(AddrDelta, OS);
|
|
}
|
|
OS << char(dwarf::DW_LNS_extended_op);
|
|
OS << char(1);
|
|
OS << char(dwarf::DW_LNE_end_sequence);
|
|
return;
|
|
}
|
|
|
|
// Bias the line delta by the base.
|
|
Temp = LineDelta - Params.DWARF2LineBase;
|
|
|
|
// If the line increment is out of range of a special opcode, we must encode
|
|
// it with DW_LNS_advance_line.
|
|
if (Temp >= Params.DWARF2LineRange ||
|
|
Temp + Params.DWARF2LineOpcodeBase > 255) {
|
|
OS << char(dwarf::DW_LNS_advance_line);
|
|
encodeSLEB128(LineDelta, OS);
|
|
|
|
LineDelta = 0;
|
|
Temp = 0 - Params.DWARF2LineBase;
|
|
NeedCopy = true;
|
|
}
|
|
|
|
// Use DW_LNS_copy instead of a "line +0, addr +0" special opcode.
|
|
if (LineDelta == 0 && AddrDelta == 0) {
|
|
OS << char(dwarf::DW_LNS_copy);
|
|
return;
|
|
}
|
|
|
|
// Bias the opcode by the special opcode base.
|
|
Temp += Params.DWARF2LineOpcodeBase;
|
|
|
|
// Avoid overflow when addr_delta is large.
|
|
if (AddrDelta < 256 + MaxSpecialAddrDelta) {
|
|
// Try using a special opcode.
|
|
Opcode = Temp + AddrDelta * Params.DWARF2LineRange;
|
|
if (Opcode <= 255) {
|
|
OS << char(Opcode);
|
|
return;
|
|
}
|
|
|
|
// Try using DW_LNS_const_add_pc followed by special op.
|
|
Opcode = Temp + (AddrDelta - MaxSpecialAddrDelta) * Params.DWARF2LineRange;
|
|
if (Opcode <= 255) {
|
|
OS << char(dwarf::DW_LNS_const_add_pc);
|
|
OS << char(Opcode);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Otherwise use DW_LNS_advance_pc.
|
|
OS << char(dwarf::DW_LNS_advance_pc);
|
|
encodeULEB128(AddrDelta, OS);
|
|
|
|
if (NeedCopy)
|
|
OS << char(dwarf::DW_LNS_copy);
|
|
else {
|
|
assert(Temp <= 255 && "Buggy special opcode encoding.");
|
|
OS << char(Temp);
|
|
}
|
|
}
|
|
|
|
// Utility function to write a tuple for .debug_abbrev.
|
|
static void EmitAbbrev(MCStreamer *MCOS, uint64_t Name, uint64_t Form) {
|
|
MCOS->EmitULEB128IntValue(Name);
|
|
MCOS->EmitULEB128IntValue(Form);
|
|
}
|
|
|
|
// When generating dwarf for assembly source files this emits
|
|
// the data for .debug_abbrev section which contains three DIEs.
|
|
static void EmitGenDwarfAbbrev(MCStreamer *MCOS) {
|
|
MCContext &context = MCOS->getContext();
|
|
MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfAbbrevSection());
|
|
|
|
// DW_TAG_compile_unit DIE abbrev (1).
|
|
MCOS->EmitULEB128IntValue(1);
|
|
MCOS->EmitULEB128IntValue(dwarf::DW_TAG_compile_unit);
|
|
MCOS->EmitIntValue(dwarf::DW_CHILDREN_yes, 1);
|
|
EmitAbbrev(MCOS, dwarf::DW_AT_stmt_list, context.getDwarfVersion() >= 4
|
|
? dwarf::DW_FORM_sec_offset
|
|
: dwarf::DW_FORM_data4);
|
|
if (context.getGenDwarfSectionSyms().size() > 1 &&
|
|
context.getDwarfVersion() >= 3) {
|
|
EmitAbbrev(MCOS, dwarf::DW_AT_ranges, context.getDwarfVersion() >= 4
|
|
? dwarf::DW_FORM_sec_offset
|
|
: dwarf::DW_FORM_data4);
|
|
} else {
|
|
EmitAbbrev(MCOS, dwarf::DW_AT_low_pc, dwarf::DW_FORM_addr);
|
|
EmitAbbrev(MCOS, dwarf::DW_AT_high_pc, dwarf::DW_FORM_addr);
|
|
}
|
|
EmitAbbrev(MCOS, dwarf::DW_AT_name, dwarf::DW_FORM_string);
|
|
if (!context.getCompilationDir().empty())
|
|
EmitAbbrev(MCOS, dwarf::DW_AT_comp_dir, dwarf::DW_FORM_string);
|
|
StringRef DwarfDebugFlags = context.getDwarfDebugFlags();
|
|
if (!DwarfDebugFlags.empty())
|
|
EmitAbbrev(MCOS, dwarf::DW_AT_APPLE_flags, dwarf::DW_FORM_string);
|
|
EmitAbbrev(MCOS, dwarf::DW_AT_producer, dwarf::DW_FORM_string);
|
|
EmitAbbrev(MCOS, dwarf::DW_AT_language, dwarf::DW_FORM_data2);
|
|
EmitAbbrev(MCOS, 0, 0);
|
|
|
|
// DW_TAG_label DIE abbrev (2).
|
|
MCOS->EmitULEB128IntValue(2);
|
|
MCOS->EmitULEB128IntValue(dwarf::DW_TAG_label);
|
|
MCOS->EmitIntValue(dwarf::DW_CHILDREN_yes, 1);
|
|
EmitAbbrev(MCOS, dwarf::DW_AT_name, dwarf::DW_FORM_string);
|
|
EmitAbbrev(MCOS, dwarf::DW_AT_decl_file, dwarf::DW_FORM_data4);
|
|
EmitAbbrev(MCOS, dwarf::DW_AT_decl_line, dwarf::DW_FORM_data4);
|
|
EmitAbbrev(MCOS, dwarf::DW_AT_low_pc, dwarf::DW_FORM_addr);
|
|
EmitAbbrev(MCOS, dwarf::DW_AT_prototyped, dwarf::DW_FORM_flag);
|
|
EmitAbbrev(MCOS, 0, 0);
|
|
|
|
// DW_TAG_unspecified_parameters DIE abbrev (3).
|
|
MCOS->EmitULEB128IntValue(3);
|
|
MCOS->EmitULEB128IntValue(dwarf::DW_TAG_unspecified_parameters);
|
|
MCOS->EmitIntValue(dwarf::DW_CHILDREN_no, 1);
|
|
EmitAbbrev(MCOS, 0, 0);
|
|
|
|
// Terminate the abbreviations for this compilation unit.
|
|
MCOS->EmitIntValue(0, 1);
|
|
}
|
|
|
|
// When generating dwarf for assembly source files this emits the data for
|
|
// .debug_aranges section. This section contains a header and a table of pairs
|
|
// of PointerSize'ed values for the address and size of section(s) with line
|
|
// table entries.
|
|
static void EmitGenDwarfAranges(MCStreamer *MCOS,
|
|
const MCSymbol *InfoSectionSymbol) {
|
|
MCContext &context = MCOS->getContext();
|
|
|
|
auto &Sections = context.getGenDwarfSectionSyms();
|
|
|
|
MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfARangesSection());
|
|
|
|
// This will be the length of the .debug_aranges section, first account for
|
|
// the size of each item in the header (see below where we emit these items).
|
|
int Length = 4 + 2 + 4 + 1 + 1;
|
|
|
|
// Figure the padding after the header before the table of address and size
|
|
// pairs who's values are PointerSize'ed.
|
|
const MCAsmInfo *asmInfo = context.getAsmInfo();
|
|
int AddrSize = asmInfo->getCodePointerSize();
|
|
int Pad = 2 * AddrSize - (Length & (2 * AddrSize - 1));
|
|
if (Pad == 2 * AddrSize)
|
|
Pad = 0;
|
|
Length += Pad;
|
|
|
|
// Add the size of the pair of PointerSize'ed values for the address and size
|
|
// of each section we have in the table.
|
|
Length += 2 * AddrSize * Sections.size();
|
|
// And the pair of terminating zeros.
|
|
Length += 2 * AddrSize;
|
|
|
|
// Emit the header for this section.
|
|
// The 4 byte length not including the 4 byte value for the length.
|
|
MCOS->EmitIntValue(Length - 4, 4);
|
|
// The 2 byte version, which is 2.
|
|
MCOS->EmitIntValue(2, 2);
|
|
// The 4 byte offset to the compile unit in the .debug_info from the start
|
|
// of the .debug_info.
|
|
if (InfoSectionSymbol)
|
|
MCOS->EmitSymbolValue(InfoSectionSymbol, 4,
|
|
asmInfo->needsDwarfSectionOffsetDirective());
|
|
else
|
|
MCOS->EmitIntValue(0, 4);
|
|
// The 1 byte size of an address.
|
|
MCOS->EmitIntValue(AddrSize, 1);
|
|
// The 1 byte size of a segment descriptor, we use a value of zero.
|
|
MCOS->EmitIntValue(0, 1);
|
|
// Align the header with the padding if needed, before we put out the table.
|
|
for(int i = 0; i < Pad; i++)
|
|
MCOS->EmitIntValue(0, 1);
|
|
|
|
// Now emit the table of pairs of PointerSize'ed values for the section
|
|
// addresses and sizes.
|
|
for (MCSection *Sec : Sections) {
|
|
const MCSymbol *StartSymbol = Sec->getBeginSymbol();
|
|
MCSymbol *EndSymbol = Sec->getEndSymbol(context);
|
|
assert(StartSymbol && "StartSymbol must not be NULL");
|
|
assert(EndSymbol && "EndSymbol must not be NULL");
|
|
|
|
const MCExpr *Addr = MCSymbolRefExpr::create(
|
|
StartSymbol, MCSymbolRefExpr::VK_None, context);
|
|
const MCExpr *Size = MakeStartMinusEndExpr(*MCOS,
|
|
*StartSymbol, *EndSymbol, 0);
|
|
MCOS->EmitValue(Addr, AddrSize);
|
|
emitAbsValue(*MCOS, Size, AddrSize);
|
|
}
|
|
|
|
// And finally the pair of terminating zeros.
|
|
MCOS->EmitIntValue(0, AddrSize);
|
|
MCOS->EmitIntValue(0, AddrSize);
|
|
}
|
|
|
|
// When generating dwarf for assembly source files this emits the data for
|
|
// .debug_info section which contains three parts. The header, the compile_unit
|
|
// DIE and a list of label DIEs.
|
|
static void EmitGenDwarfInfo(MCStreamer *MCOS,
|
|
const MCSymbol *AbbrevSectionSymbol,
|
|
const MCSymbol *LineSectionSymbol,
|
|
const MCSymbol *RangesSectionSymbol) {
|
|
MCContext &context = MCOS->getContext();
|
|
|
|
MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfInfoSection());
|
|
|
|
// Create a symbol at the start and end of this section used in here for the
|
|
// expression to calculate the length in the header.
|
|
MCSymbol *InfoStart = context.createTempSymbol();
|
|
MCOS->EmitLabel(InfoStart);
|
|
MCSymbol *InfoEnd = context.createTempSymbol();
|
|
|
|
// First part: the header.
|
|
|
|
// The 4 byte total length of the information for this compilation unit, not
|
|
// including these 4 bytes.
|
|
const MCExpr *Length = MakeStartMinusEndExpr(*MCOS, *InfoStart, *InfoEnd, 4);
|
|
emitAbsValue(*MCOS, Length, 4);
|
|
|
|
// The 2 byte DWARF version.
|
|
MCOS->EmitIntValue(context.getDwarfVersion(), 2);
|
|
|
|
// The DWARF v5 header has unit type, address size, abbrev offset.
|
|
// Earlier versions have abbrev offset, address size.
|
|
const MCAsmInfo &AsmInfo = *context.getAsmInfo();
|
|
int AddrSize = AsmInfo.getCodePointerSize();
|
|
if (context.getDwarfVersion() >= 5) {
|
|
MCOS->EmitIntValue(dwarf::DW_UT_compile, 1);
|
|
MCOS->EmitIntValue(AddrSize, 1);
|
|
}
|
|
// The 4 byte offset to the debug abbrevs from the start of the .debug_abbrev,
|
|
// it is at the start of that section so this is zero.
|
|
if (AbbrevSectionSymbol == nullptr)
|
|
MCOS->EmitIntValue(0, 4);
|
|
else
|
|
MCOS->EmitSymbolValue(AbbrevSectionSymbol, 4,
|
|
AsmInfo.needsDwarfSectionOffsetDirective());
|
|
if (context.getDwarfVersion() <= 4)
|
|
MCOS->EmitIntValue(AddrSize, 1);
|
|
|
|
// Second part: the compile_unit DIE.
|
|
|
|
// The DW_TAG_compile_unit DIE abbrev (1).
|
|
MCOS->EmitULEB128IntValue(1);
|
|
|
|
// DW_AT_stmt_list, a 4 byte offset from the start of the .debug_line section,
|
|
// which is at the start of that section so this is zero.
|
|
if (LineSectionSymbol)
|
|
MCOS->EmitSymbolValue(LineSectionSymbol, 4,
|
|
AsmInfo.needsDwarfSectionOffsetDirective());
|
|
else
|
|
MCOS->EmitIntValue(0, 4);
|
|
|
|
if (RangesSectionSymbol) {
|
|
// There are multiple sections containing code, so we must use the
|
|
// .debug_ranges sections.
|
|
|
|
// AT_ranges, the 4 byte offset from the start of the .debug_ranges section
|
|
// to the address range list for this compilation unit.
|
|
MCOS->EmitSymbolValue(RangesSectionSymbol, 4);
|
|
} else {
|
|
// If we only have one non-empty code section, we can use the simpler
|
|
// AT_low_pc and AT_high_pc attributes.
|
|
|
|
// Find the first (and only) non-empty text section
|
|
auto &Sections = context.getGenDwarfSectionSyms();
|
|
const auto TextSection = Sections.begin();
|
|
assert(TextSection != Sections.end() && "No text section found");
|
|
|
|
MCSymbol *StartSymbol = (*TextSection)->getBeginSymbol();
|
|
MCSymbol *EndSymbol = (*TextSection)->getEndSymbol(context);
|
|
assert(StartSymbol && "StartSymbol must not be NULL");
|
|
assert(EndSymbol && "EndSymbol must not be NULL");
|
|
|
|
// AT_low_pc, the first address of the default .text section.
|
|
const MCExpr *Start = MCSymbolRefExpr::create(
|
|
StartSymbol, MCSymbolRefExpr::VK_None, context);
|
|
MCOS->EmitValue(Start, AddrSize);
|
|
|
|
// AT_high_pc, the last address of the default .text section.
|
|
const MCExpr *End = MCSymbolRefExpr::create(
|
|
EndSymbol, MCSymbolRefExpr::VK_None, context);
|
|
MCOS->EmitValue(End, AddrSize);
|
|
}
|
|
|
|
// AT_name, the name of the source file. Reconstruct from the first directory
|
|
// and file table entries.
|
|
const SmallVectorImpl<std::string> &MCDwarfDirs = context.getMCDwarfDirs();
|
|
if (MCDwarfDirs.size() > 0) {
|
|
MCOS->EmitBytes(MCDwarfDirs[0]);
|
|
MCOS->EmitBytes(sys::path::get_separator());
|
|
}
|
|
const SmallVectorImpl<MCDwarfFile> &MCDwarfFiles =
|
|
MCOS->getContext().getMCDwarfFiles();
|
|
MCOS->EmitBytes(MCDwarfFiles[1].Name);
|
|
MCOS->EmitIntValue(0, 1); // NULL byte to terminate the string.
|
|
|
|
// AT_comp_dir, the working directory the assembly was done in.
|
|
if (!context.getCompilationDir().empty()) {
|
|
MCOS->EmitBytes(context.getCompilationDir());
|
|
MCOS->EmitIntValue(0, 1); // NULL byte to terminate the string.
|
|
}
|
|
|
|
// AT_APPLE_flags, the command line arguments of the assembler tool.
|
|
StringRef DwarfDebugFlags = context.getDwarfDebugFlags();
|
|
if (!DwarfDebugFlags.empty()){
|
|
MCOS->EmitBytes(DwarfDebugFlags);
|
|
MCOS->EmitIntValue(0, 1); // NULL byte to terminate the string.
|
|
}
|
|
|
|
// AT_producer, the version of the assembler tool.
|
|
StringRef DwarfDebugProducer = context.getDwarfDebugProducer();
|
|
if (!DwarfDebugProducer.empty())
|
|
MCOS->EmitBytes(DwarfDebugProducer);
|
|
else
|
|
MCOS->EmitBytes(StringRef("llvm-mc (based on LLVM " PACKAGE_VERSION ")"));
|
|
MCOS->EmitIntValue(0, 1); // NULL byte to terminate the string.
|
|
|
|
// AT_language, a 4 byte value. We use DW_LANG_Mips_Assembler as the dwarf2
|
|
// draft has no standard code for assembler.
|
|
MCOS->EmitIntValue(dwarf::DW_LANG_Mips_Assembler, 2);
|
|
|
|
// Third part: the list of label DIEs.
|
|
|
|
// Loop on saved info for dwarf labels and create the DIEs for them.
|
|
const std::vector<MCGenDwarfLabelEntry> &Entries =
|
|
MCOS->getContext().getMCGenDwarfLabelEntries();
|
|
for (const auto &Entry : Entries) {
|
|
// The DW_TAG_label DIE abbrev (2).
|
|
MCOS->EmitULEB128IntValue(2);
|
|
|
|
// AT_name, of the label without any leading underbar.
|
|
MCOS->EmitBytes(Entry.getName());
|
|
MCOS->EmitIntValue(0, 1); // NULL byte to terminate the string.
|
|
|
|
// AT_decl_file, index into the file table.
|
|
MCOS->EmitIntValue(Entry.getFileNumber(), 4);
|
|
|
|
// AT_decl_line, source line number.
|
|
MCOS->EmitIntValue(Entry.getLineNumber(), 4);
|
|
|
|
// AT_low_pc, start address of the label.
|
|
const MCExpr *AT_low_pc = MCSymbolRefExpr::create(Entry.getLabel(),
|
|
MCSymbolRefExpr::VK_None, context);
|
|
MCOS->EmitValue(AT_low_pc, AddrSize);
|
|
|
|
// DW_AT_prototyped, a one byte flag value of 0 saying we have no prototype.
|
|
MCOS->EmitIntValue(0, 1);
|
|
|
|
// The DW_TAG_unspecified_parameters DIE abbrev (3).
|
|
MCOS->EmitULEB128IntValue(3);
|
|
|
|
// Add the NULL DIE terminating the DW_TAG_unspecified_parameters DIE's.
|
|
MCOS->EmitIntValue(0, 1);
|
|
}
|
|
|
|
// Add the NULL DIE terminating the Compile Unit DIE's.
|
|
MCOS->EmitIntValue(0, 1);
|
|
|
|
// Now set the value of the symbol at the end of the info section.
|
|
MCOS->EmitLabel(InfoEnd);
|
|
}
|
|
|
|
// When generating dwarf for assembly source files this emits the data for
|
|
// .debug_ranges section. We only emit one range list, which spans all of the
|
|
// executable sections of this file.
|
|
static void EmitGenDwarfRanges(MCStreamer *MCOS) {
|
|
MCContext &context = MCOS->getContext();
|
|
auto &Sections = context.getGenDwarfSectionSyms();
|
|
|
|
const MCAsmInfo *AsmInfo = context.getAsmInfo();
|
|
int AddrSize = AsmInfo->getCodePointerSize();
|
|
|
|
MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfRangesSection());
|
|
|
|
for (MCSection *Sec : Sections) {
|
|
const MCSymbol *StartSymbol = Sec->getBeginSymbol();
|
|
MCSymbol *EndSymbol = Sec->getEndSymbol(context);
|
|
assert(StartSymbol && "StartSymbol must not be NULL");
|
|
assert(EndSymbol && "EndSymbol must not be NULL");
|
|
|
|
// Emit a base address selection entry for the start of this section
|
|
const MCExpr *SectionStartAddr = MCSymbolRefExpr::create(
|
|
StartSymbol, MCSymbolRefExpr::VK_None, context);
|
|
MCOS->emitFill(AddrSize, 0xFF);
|
|
MCOS->EmitValue(SectionStartAddr, AddrSize);
|
|
|
|
// Emit a range list entry spanning this section
|
|
const MCExpr *SectionSize = MakeStartMinusEndExpr(*MCOS,
|
|
*StartSymbol, *EndSymbol, 0);
|
|
MCOS->EmitIntValue(0, AddrSize);
|
|
emitAbsValue(*MCOS, SectionSize, AddrSize);
|
|
}
|
|
|
|
// Emit end of list entry
|
|
MCOS->EmitIntValue(0, AddrSize);
|
|
MCOS->EmitIntValue(0, AddrSize);
|
|
}
|
|
|
|
//
|
|
// When generating dwarf for assembly source files this emits the Dwarf
|
|
// sections.
|
|
//
|
|
void MCGenDwarfInfo::Emit(MCStreamer *MCOS) {
|
|
MCContext &context = MCOS->getContext();
|
|
|
|
// Create the dwarf sections in this order (.debug_line already created).
|
|
const MCAsmInfo *AsmInfo = context.getAsmInfo();
|
|
bool CreateDwarfSectionSymbols =
|
|
AsmInfo->doesDwarfUseRelocationsAcrossSections();
|
|
MCSymbol *LineSectionSymbol = nullptr;
|
|
if (CreateDwarfSectionSymbols)
|
|
LineSectionSymbol = MCOS->getDwarfLineTableSymbol(0);
|
|
MCSymbol *AbbrevSectionSymbol = nullptr;
|
|
MCSymbol *InfoSectionSymbol = nullptr;
|
|
MCSymbol *RangesSectionSymbol = nullptr;
|
|
|
|
// Create end symbols for each section, and remove empty sections
|
|
MCOS->getContext().finalizeDwarfSections(*MCOS);
|
|
|
|
// If there are no sections to generate debug info for, we don't need
|
|
// to do anything
|
|
if (MCOS->getContext().getGenDwarfSectionSyms().empty())
|
|
return;
|
|
|
|
// We only use the .debug_ranges section if we have multiple code sections,
|
|
// and we are emitting a DWARF version which supports it.
|
|
const bool UseRangesSection =
|
|
MCOS->getContext().getGenDwarfSectionSyms().size() > 1 &&
|
|
MCOS->getContext().getDwarfVersion() >= 3;
|
|
CreateDwarfSectionSymbols |= UseRangesSection;
|
|
|
|
MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfInfoSection());
|
|
if (CreateDwarfSectionSymbols) {
|
|
InfoSectionSymbol = context.createTempSymbol();
|
|
MCOS->EmitLabel(InfoSectionSymbol);
|
|
}
|
|
MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfAbbrevSection());
|
|
if (CreateDwarfSectionSymbols) {
|
|
AbbrevSectionSymbol = context.createTempSymbol();
|
|
MCOS->EmitLabel(AbbrevSectionSymbol);
|
|
}
|
|
if (UseRangesSection) {
|
|
MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfRangesSection());
|
|
if (CreateDwarfSectionSymbols) {
|
|
RangesSectionSymbol = context.createTempSymbol();
|
|
MCOS->EmitLabel(RangesSectionSymbol);
|
|
}
|
|
}
|
|
|
|
assert((RangesSectionSymbol != nullptr) || !UseRangesSection);
|
|
|
|
MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfARangesSection());
|
|
|
|
// Output the data for .debug_aranges section.
|
|
EmitGenDwarfAranges(MCOS, InfoSectionSymbol);
|
|
|
|
if (UseRangesSection)
|
|
EmitGenDwarfRanges(MCOS);
|
|
|
|
// Output the data for .debug_abbrev section.
|
|
EmitGenDwarfAbbrev(MCOS);
|
|
|
|
// Output the data for .debug_info section.
|
|
EmitGenDwarfInfo(MCOS, AbbrevSectionSymbol, LineSectionSymbol,
|
|
RangesSectionSymbol);
|
|
}
|
|
|
|
//
|
|
// When generating dwarf for assembly source files this is called when symbol
|
|
// for a label is created. If this symbol is not a temporary and is in the
|
|
// section that dwarf is being generated for, save the needed info to create
|
|
// a dwarf label.
|
|
//
|
|
void MCGenDwarfLabelEntry::Make(MCSymbol *Symbol, MCStreamer *MCOS,
|
|
SourceMgr &SrcMgr, SMLoc &Loc) {
|
|
// We won't create dwarf labels for temporary symbols.
|
|
if (Symbol->isTemporary())
|
|
return;
|
|
MCContext &context = MCOS->getContext();
|
|
// We won't create dwarf labels for symbols in sections that we are not
|
|
// generating debug info for.
|
|
if (!context.getGenDwarfSectionSyms().count(MCOS->getCurrentSectionOnly()))
|
|
return;
|
|
|
|
// The dwarf label's name does not have the symbol name's leading
|
|
// underbar if any.
|
|
StringRef Name = Symbol->getName();
|
|
if (Name.startswith("_"))
|
|
Name = Name.substr(1, Name.size()-1);
|
|
|
|
// Get the dwarf file number to be used for the dwarf label.
|
|
unsigned FileNumber = context.getGenDwarfFileNumber();
|
|
|
|
// Finding the line number is the expensive part which is why we just don't
|
|
// pass it in as for some symbols we won't create a dwarf label.
|
|
unsigned CurBuffer = SrcMgr.FindBufferContainingLoc(Loc);
|
|
unsigned LineNumber = SrcMgr.FindLineNumber(Loc, CurBuffer);
|
|
|
|
// We create a temporary symbol for use for the AT_high_pc and AT_low_pc
|
|
// values so that they don't have things like an ARM thumb bit from the
|
|
// original symbol. So when used they won't get a low bit set after
|
|
// relocation.
|
|
MCSymbol *Label = context.createTempSymbol();
|
|
MCOS->EmitLabel(Label);
|
|
|
|
// Create and entry for the info and add it to the other entries.
|
|
MCOS->getContext().addMCGenDwarfLabelEntry(
|
|
MCGenDwarfLabelEntry(Name, FileNumber, LineNumber, Label));
|
|
}
|
|
|
|
static int getDataAlignmentFactor(MCStreamer &streamer) {
|
|
MCContext &context = streamer.getContext();
|
|
const MCAsmInfo *asmInfo = context.getAsmInfo();
|
|
int size = asmInfo->getCalleeSaveStackSlotSize();
|
|
if (asmInfo->isStackGrowthDirectionUp())
|
|
return size;
|
|
else
|
|
return -size;
|
|
}
|
|
|
|
static unsigned getSizeForEncoding(MCStreamer &streamer,
|
|
unsigned symbolEncoding) {
|
|
MCContext &context = streamer.getContext();
|
|
unsigned format = symbolEncoding & 0x0f;
|
|
switch (format) {
|
|
default: llvm_unreachable("Unknown Encoding");
|
|
case dwarf::DW_EH_PE_absptr:
|
|
case dwarf::DW_EH_PE_signed:
|
|
return context.getAsmInfo()->getCodePointerSize();
|
|
case dwarf::DW_EH_PE_udata2:
|
|
case dwarf::DW_EH_PE_sdata2:
|
|
return 2;
|
|
case dwarf::DW_EH_PE_udata4:
|
|
case dwarf::DW_EH_PE_sdata4:
|
|
return 4;
|
|
case dwarf::DW_EH_PE_udata8:
|
|
case dwarf::DW_EH_PE_sdata8:
|
|
return 8;
|
|
}
|
|
}
|
|
|
|
static void emitFDESymbol(MCObjectStreamer &streamer, const MCSymbol &symbol,
|
|
unsigned symbolEncoding, bool isEH) {
|
|
MCContext &context = streamer.getContext();
|
|
const MCAsmInfo *asmInfo = context.getAsmInfo();
|
|
const MCExpr *v = asmInfo->getExprForFDESymbol(&symbol,
|
|
symbolEncoding,
|
|
streamer);
|
|
unsigned size = getSizeForEncoding(streamer, symbolEncoding);
|
|
if (asmInfo->doDwarfFDESymbolsUseAbsDiff() && isEH)
|
|
emitAbsValue(streamer, v, size);
|
|
else
|
|
streamer.EmitValue(v, size);
|
|
}
|
|
|
|
static void EmitPersonality(MCStreamer &streamer, const MCSymbol &symbol,
|
|
unsigned symbolEncoding) {
|
|
MCContext &context = streamer.getContext();
|
|
const MCAsmInfo *asmInfo = context.getAsmInfo();
|
|
const MCExpr *v = asmInfo->getExprForPersonalitySymbol(&symbol,
|
|
symbolEncoding,
|
|
streamer);
|
|
unsigned size = getSizeForEncoding(streamer, symbolEncoding);
|
|
streamer.EmitValue(v, size);
|
|
}
|
|
|
|
namespace {
|
|
|
|
class FrameEmitterImpl {
|
|
int CFAOffset = 0;
|
|
int InitialCFAOffset = 0;
|
|
bool IsEH;
|
|
MCObjectStreamer &Streamer;
|
|
|
|
public:
|
|
FrameEmitterImpl(bool IsEH, MCObjectStreamer &Streamer)
|
|
: IsEH(IsEH), Streamer(Streamer) {}
|
|
|
|
/// Emit the unwind information in a compact way.
|
|
void EmitCompactUnwind(const MCDwarfFrameInfo &frame);
|
|
|
|
const MCSymbol &EmitCIE(const MCDwarfFrameInfo &F);
|
|
void EmitFDE(const MCSymbol &cieStart, const MCDwarfFrameInfo &frame,
|
|
bool LastInSection, const MCSymbol &SectionStart);
|
|
void EmitCFIInstructions(ArrayRef<MCCFIInstruction> Instrs,
|
|
MCSymbol *BaseLabel);
|
|
void EmitCFIInstruction(const MCCFIInstruction &Instr);
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
static void emitEncodingByte(MCObjectStreamer &Streamer, unsigned Encoding) {
|
|
Streamer.EmitIntValue(Encoding, 1);
|
|
}
|
|
|
|
void FrameEmitterImpl::EmitCFIInstruction(const MCCFIInstruction &Instr) {
|
|
int dataAlignmentFactor = getDataAlignmentFactor(Streamer);
|
|
auto *MRI = Streamer.getContext().getRegisterInfo();
|
|
|
|
switch (Instr.getOperation()) {
|
|
case MCCFIInstruction::OpRegister: {
|
|
unsigned Reg1 = Instr.getRegister();
|
|
unsigned Reg2 = Instr.getRegister2();
|
|
if (!IsEH) {
|
|
Reg1 = MRI->getDwarfRegNumFromDwarfEHRegNum(Reg1);
|
|
Reg2 = MRI->getDwarfRegNumFromDwarfEHRegNum(Reg2);
|
|
}
|
|
Streamer.EmitIntValue(dwarf::DW_CFA_register, 1);
|
|
Streamer.EmitULEB128IntValue(Reg1);
|
|
Streamer.EmitULEB128IntValue(Reg2);
|
|
return;
|
|
}
|
|
case MCCFIInstruction::OpWindowSave:
|
|
Streamer.EmitIntValue(dwarf::DW_CFA_GNU_window_save, 1);
|
|
return;
|
|
|
|
case MCCFIInstruction::OpUndefined: {
|
|
unsigned Reg = Instr.getRegister();
|
|
Streamer.EmitIntValue(dwarf::DW_CFA_undefined, 1);
|
|
Streamer.EmitULEB128IntValue(Reg);
|
|
return;
|
|
}
|
|
case MCCFIInstruction::OpAdjustCfaOffset:
|
|
case MCCFIInstruction::OpDefCfaOffset: {
|
|
const bool IsRelative =
|
|
Instr.getOperation() == MCCFIInstruction::OpAdjustCfaOffset;
|
|
|
|
Streamer.EmitIntValue(dwarf::DW_CFA_def_cfa_offset, 1);
|
|
|
|
if (IsRelative)
|
|
CFAOffset += Instr.getOffset();
|
|
else
|
|
CFAOffset = -Instr.getOffset();
|
|
|
|
Streamer.EmitULEB128IntValue(CFAOffset);
|
|
|
|
return;
|
|
}
|
|
case MCCFIInstruction::OpDefCfa: {
|
|
unsigned Reg = Instr.getRegister();
|
|
if (!IsEH)
|
|
Reg = MRI->getDwarfRegNumFromDwarfEHRegNum(Reg);
|
|
Streamer.EmitIntValue(dwarf::DW_CFA_def_cfa, 1);
|
|
Streamer.EmitULEB128IntValue(Reg);
|
|
CFAOffset = -Instr.getOffset();
|
|
Streamer.EmitULEB128IntValue(CFAOffset);
|
|
|
|
return;
|
|
}
|
|
case MCCFIInstruction::OpDefCfaRegister: {
|
|
unsigned Reg = Instr.getRegister();
|
|
if (!IsEH)
|
|
Reg = MRI->getDwarfRegNumFromDwarfEHRegNum(Reg);
|
|
Streamer.EmitIntValue(dwarf::DW_CFA_def_cfa_register, 1);
|
|
Streamer.EmitULEB128IntValue(Reg);
|
|
|
|
return;
|
|
}
|
|
case MCCFIInstruction::OpOffset:
|
|
case MCCFIInstruction::OpRelOffset: {
|
|
const bool IsRelative =
|
|
Instr.getOperation() == MCCFIInstruction::OpRelOffset;
|
|
|
|
unsigned Reg = Instr.getRegister();
|
|
if (!IsEH)
|
|
Reg = MRI->getDwarfRegNumFromDwarfEHRegNum(Reg);
|
|
|
|
int Offset = Instr.getOffset();
|
|
if (IsRelative)
|
|
Offset -= CFAOffset;
|
|
Offset = Offset / dataAlignmentFactor;
|
|
|
|
if (Offset < 0) {
|
|
Streamer.EmitIntValue(dwarf::DW_CFA_offset_extended_sf, 1);
|
|
Streamer.EmitULEB128IntValue(Reg);
|
|
Streamer.EmitSLEB128IntValue(Offset);
|
|
} else if (Reg < 64) {
|
|
Streamer.EmitIntValue(dwarf::DW_CFA_offset + Reg, 1);
|
|
Streamer.EmitULEB128IntValue(Offset);
|
|
} else {
|
|
Streamer.EmitIntValue(dwarf::DW_CFA_offset_extended, 1);
|
|
Streamer.EmitULEB128IntValue(Reg);
|
|
Streamer.EmitULEB128IntValue(Offset);
|
|
}
|
|
return;
|
|
}
|
|
case MCCFIInstruction::OpRememberState:
|
|
Streamer.EmitIntValue(dwarf::DW_CFA_remember_state, 1);
|
|
return;
|
|
case MCCFIInstruction::OpRestoreState:
|
|
Streamer.EmitIntValue(dwarf::DW_CFA_restore_state, 1);
|
|
return;
|
|
case MCCFIInstruction::OpSameValue: {
|
|
unsigned Reg = Instr.getRegister();
|
|
Streamer.EmitIntValue(dwarf::DW_CFA_same_value, 1);
|
|
Streamer.EmitULEB128IntValue(Reg);
|
|
return;
|
|
}
|
|
case MCCFIInstruction::OpRestore: {
|
|
unsigned Reg = Instr.getRegister();
|
|
if (!IsEH)
|
|
Reg = MRI->getDwarfRegNumFromDwarfEHRegNum(Reg);
|
|
Streamer.EmitIntValue(dwarf::DW_CFA_restore | Reg, 1);
|
|
return;
|
|
}
|
|
case MCCFIInstruction::OpGnuArgsSize:
|
|
Streamer.EmitIntValue(dwarf::DW_CFA_GNU_args_size, 1);
|
|
Streamer.EmitULEB128IntValue(Instr.getOffset());
|
|
return;
|
|
|
|
case MCCFIInstruction::OpEscape:
|
|
Streamer.EmitBytes(Instr.getValues());
|
|
return;
|
|
}
|
|
llvm_unreachable("Unhandled case in switch");
|
|
}
|
|
|
|
/// Emit frame instructions to describe the layout of the frame.
|
|
void FrameEmitterImpl::EmitCFIInstructions(ArrayRef<MCCFIInstruction> Instrs,
|
|
MCSymbol *BaseLabel) {
|
|
for (const MCCFIInstruction &Instr : Instrs) {
|
|
MCSymbol *Label = Instr.getLabel();
|
|
// Throw out move if the label is invalid.
|
|
if (Label && !Label->isDefined()) continue; // Not emitted, in dead code.
|
|
|
|
// Advance row if new location.
|
|
if (BaseLabel && Label) {
|
|
MCSymbol *ThisSym = Label;
|
|
if (ThisSym != BaseLabel) {
|
|
Streamer.EmitDwarfAdvanceFrameAddr(BaseLabel, ThisSym);
|
|
BaseLabel = ThisSym;
|
|
}
|
|
}
|
|
|
|
EmitCFIInstruction(Instr);
|
|
}
|
|
}
|
|
|
|
/// Emit the unwind information in a compact way.
|
|
void FrameEmitterImpl::EmitCompactUnwind(const MCDwarfFrameInfo &Frame) {
|
|
MCContext &Context = Streamer.getContext();
|
|
const MCObjectFileInfo *MOFI = Context.getObjectFileInfo();
|
|
|
|
// range-start range-length compact-unwind-enc personality-func lsda
|
|
// _foo LfooEnd-_foo 0x00000023 0 0
|
|
// _bar LbarEnd-_bar 0x00000025 __gxx_personality except_tab1
|
|
//
|
|
// .section __LD,__compact_unwind,regular,debug
|
|
//
|
|
// # compact unwind for _foo
|
|
// .quad _foo
|
|
// .set L1,LfooEnd-_foo
|
|
// .long L1
|
|
// .long 0x01010001
|
|
// .quad 0
|
|
// .quad 0
|
|
//
|
|
// # compact unwind for _bar
|
|
// .quad _bar
|
|
// .set L2,LbarEnd-_bar
|
|
// .long L2
|
|
// .long 0x01020011
|
|
// .quad __gxx_personality
|
|
// .quad except_tab1
|
|
|
|
uint32_t Encoding = Frame.CompactUnwindEncoding;
|
|
if (!Encoding) return;
|
|
bool DwarfEHFrameOnly = (Encoding == MOFI->getCompactUnwindDwarfEHFrameOnly());
|
|
|
|
// The encoding needs to know we have an LSDA.
|
|
if (!DwarfEHFrameOnly && Frame.Lsda)
|
|
Encoding |= 0x40000000;
|
|
|
|
// Range Start
|
|
unsigned FDEEncoding = MOFI->getFDEEncoding();
|
|
unsigned Size = getSizeForEncoding(Streamer, FDEEncoding);
|
|
Streamer.EmitSymbolValue(Frame.Begin, Size);
|
|
|
|
// Range Length
|
|
const MCExpr *Range = MakeStartMinusEndExpr(Streamer, *Frame.Begin,
|
|
*Frame.End, 0);
|
|
emitAbsValue(Streamer, Range, 4);
|
|
|
|
// Compact Encoding
|
|
Size = getSizeForEncoding(Streamer, dwarf::DW_EH_PE_udata4);
|
|
Streamer.EmitIntValue(Encoding, Size);
|
|
|
|
// Personality Function
|
|
Size = getSizeForEncoding(Streamer, dwarf::DW_EH_PE_absptr);
|
|
if (!DwarfEHFrameOnly && Frame.Personality)
|
|
Streamer.EmitSymbolValue(Frame.Personality, Size);
|
|
else
|
|
Streamer.EmitIntValue(0, Size); // No personality fn
|
|
|
|
// LSDA
|
|
Size = getSizeForEncoding(Streamer, Frame.LsdaEncoding);
|
|
if (!DwarfEHFrameOnly && Frame.Lsda)
|
|
Streamer.EmitSymbolValue(Frame.Lsda, Size);
|
|
else
|
|
Streamer.EmitIntValue(0, Size); // No LSDA
|
|
}
|
|
|
|
static unsigned getCIEVersion(bool IsEH, unsigned DwarfVersion) {
|
|
if (IsEH)
|
|
return 1;
|
|
switch (DwarfVersion) {
|
|
case 2:
|
|
return 1;
|
|
case 3:
|
|
return 3;
|
|
case 4:
|
|
case 5:
|
|
return 4;
|
|
}
|
|
llvm_unreachable("Unknown version");
|
|
}
|
|
|
|
const MCSymbol &FrameEmitterImpl::EmitCIE(const MCDwarfFrameInfo &Frame) {
|
|
MCContext &context = Streamer.getContext();
|
|
const MCRegisterInfo *MRI = context.getRegisterInfo();
|
|
const MCObjectFileInfo *MOFI = context.getObjectFileInfo();
|
|
|
|
MCSymbol *sectionStart = context.createTempSymbol();
|
|
Streamer.EmitLabel(sectionStart);
|
|
|
|
MCSymbol *sectionEnd = context.createTempSymbol();
|
|
|
|
// Length
|
|
const MCExpr *Length =
|
|
MakeStartMinusEndExpr(Streamer, *sectionStart, *sectionEnd, 4);
|
|
emitAbsValue(Streamer, Length, 4);
|
|
|
|
// CIE ID
|
|
unsigned CIE_ID = IsEH ? 0 : -1;
|
|
Streamer.EmitIntValue(CIE_ID, 4);
|
|
|
|
// Version
|
|
uint8_t CIEVersion = getCIEVersion(IsEH, context.getDwarfVersion());
|
|
Streamer.EmitIntValue(CIEVersion, 1);
|
|
|
|
// Augmentation String
|
|
SmallString<8> Augmentation;
|
|
if (IsEH) {
|
|
Augmentation += "z";
|
|
if (Frame.Personality)
|
|
Augmentation += "P";
|
|
if (Frame.Lsda)
|
|
Augmentation += "L";
|
|
Augmentation += "R";
|
|
if (Frame.IsSignalFrame)
|
|
Augmentation += "S";
|
|
Streamer.EmitBytes(Augmentation);
|
|
}
|
|
Streamer.EmitIntValue(0, 1);
|
|
|
|
if (CIEVersion >= 4) {
|
|
// Address Size
|
|
Streamer.EmitIntValue(context.getAsmInfo()->getCodePointerSize(), 1);
|
|
|
|
// Segment Descriptor Size
|
|
Streamer.EmitIntValue(0, 1);
|
|
}
|
|
|
|
// Code Alignment Factor
|
|
Streamer.EmitULEB128IntValue(context.getAsmInfo()->getMinInstAlignment());
|
|
|
|
// Data Alignment Factor
|
|
Streamer.EmitSLEB128IntValue(getDataAlignmentFactor(Streamer));
|
|
|
|
// Return Address Register
|
|
unsigned RAReg = Frame.RAReg;
|
|
if (RAReg == static_cast<unsigned>(INT_MAX))
|
|
RAReg = MRI->getDwarfRegNum(MRI->getRARegister(), IsEH);
|
|
|
|
if (CIEVersion == 1) {
|
|
assert(RAReg <= 255 &&
|
|
"DWARF 2 encodes return_address_register in one byte");
|
|
Streamer.EmitIntValue(RAReg, 1);
|
|
} else {
|
|
Streamer.EmitULEB128IntValue(RAReg);
|
|
}
|
|
|
|
// Augmentation Data Length (optional)
|
|
unsigned augmentationLength = 0;
|
|
if (IsEH) {
|
|
if (Frame.Personality) {
|
|
// Personality Encoding
|
|
augmentationLength += 1;
|
|
// Personality
|
|
augmentationLength +=
|
|
getSizeForEncoding(Streamer, Frame.PersonalityEncoding);
|
|
}
|
|
if (Frame.Lsda)
|
|
augmentationLength += 1;
|
|
// Encoding of the FDE pointers
|
|
augmentationLength += 1;
|
|
|
|
Streamer.EmitULEB128IntValue(augmentationLength);
|
|
|
|
// Augmentation Data (optional)
|
|
if (Frame.Personality) {
|
|
// Personality Encoding
|
|
emitEncodingByte(Streamer, Frame.PersonalityEncoding);
|
|
// Personality
|
|
EmitPersonality(Streamer, *Frame.Personality, Frame.PersonalityEncoding);
|
|
}
|
|
|
|
if (Frame.Lsda)
|
|
emitEncodingByte(Streamer, Frame.LsdaEncoding);
|
|
|
|
// Encoding of the FDE pointers
|
|
emitEncodingByte(Streamer, MOFI->getFDEEncoding());
|
|
}
|
|
|
|
// Initial Instructions
|
|
|
|
const MCAsmInfo *MAI = context.getAsmInfo();
|
|
if (!Frame.IsSimple) {
|
|
const std::vector<MCCFIInstruction> &Instructions =
|
|
MAI->getInitialFrameState();
|
|
EmitCFIInstructions(Instructions, nullptr);
|
|
}
|
|
|
|
InitialCFAOffset = CFAOffset;
|
|
|
|
// Padding
|
|
Streamer.EmitValueToAlignment(IsEH ? 4 : MAI->getCodePointerSize());
|
|
|
|
Streamer.EmitLabel(sectionEnd);
|
|
return *sectionStart;
|
|
}
|
|
|
|
void FrameEmitterImpl::EmitFDE(const MCSymbol &cieStart,
|
|
const MCDwarfFrameInfo &frame,
|
|
bool LastInSection,
|
|
const MCSymbol &SectionStart) {
|
|
MCContext &context = Streamer.getContext();
|
|
MCSymbol *fdeStart = context.createTempSymbol();
|
|
MCSymbol *fdeEnd = context.createTempSymbol();
|
|
const MCObjectFileInfo *MOFI = context.getObjectFileInfo();
|
|
|
|
CFAOffset = InitialCFAOffset;
|
|
|
|
// Length
|
|
const MCExpr *Length = MakeStartMinusEndExpr(Streamer, *fdeStart, *fdeEnd, 0);
|
|
emitAbsValue(Streamer, Length, 4);
|
|
|
|
Streamer.EmitLabel(fdeStart);
|
|
|
|
// CIE Pointer
|
|
const MCAsmInfo *asmInfo = context.getAsmInfo();
|
|
if (IsEH) {
|
|
const MCExpr *offset =
|
|
MakeStartMinusEndExpr(Streamer, cieStart, *fdeStart, 0);
|
|
emitAbsValue(Streamer, offset, 4);
|
|
} else if (!asmInfo->doesDwarfUseRelocationsAcrossSections()) {
|
|
const MCExpr *offset =
|
|
MakeStartMinusEndExpr(Streamer, SectionStart, cieStart, 0);
|
|
emitAbsValue(Streamer, offset, 4);
|
|
} else {
|
|
Streamer.EmitSymbolValue(&cieStart, 4);
|
|
}
|
|
|
|
// PC Begin
|
|
unsigned PCEncoding =
|
|
IsEH ? MOFI->getFDEEncoding() : (unsigned)dwarf::DW_EH_PE_absptr;
|
|
unsigned PCSize = getSizeForEncoding(Streamer, PCEncoding);
|
|
emitFDESymbol(Streamer, *frame.Begin, PCEncoding, IsEH);
|
|
|
|
// PC Range
|
|
const MCExpr *Range =
|
|
MakeStartMinusEndExpr(Streamer, *frame.Begin, *frame.End, 0);
|
|
emitAbsValue(Streamer, Range, PCSize);
|
|
|
|
if (IsEH) {
|
|
// Augmentation Data Length
|
|
unsigned augmentationLength = 0;
|
|
|
|
if (frame.Lsda)
|
|
augmentationLength += getSizeForEncoding(Streamer, frame.LsdaEncoding);
|
|
|
|
Streamer.EmitULEB128IntValue(augmentationLength);
|
|
|
|
// Augmentation Data
|
|
if (frame.Lsda)
|
|
emitFDESymbol(Streamer, *frame.Lsda, frame.LsdaEncoding, true);
|
|
}
|
|
|
|
// Call Frame Instructions
|
|
EmitCFIInstructions(frame.Instructions, frame.Begin);
|
|
|
|
// Padding
|
|
// The size of a .eh_frame section has to be a multiple of the alignment
|
|
// since a null CIE is interpreted as the end. Old systems overaligned
|
|
// .eh_frame, so we do too and account for it in the last FDE.
|
|
unsigned Align = LastInSection ? asmInfo->getCodePointerSize() : PCSize;
|
|
Streamer.EmitValueToAlignment(Align);
|
|
|
|
Streamer.EmitLabel(fdeEnd);
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct CIEKey {
|
|
static const CIEKey getEmptyKey() {
|
|
return CIEKey(nullptr, 0, -1, false, false, static_cast<unsigned>(INT_MAX));
|
|
}
|
|
|
|
static const CIEKey getTombstoneKey() {
|
|
return CIEKey(nullptr, -1, 0, false, false, static_cast<unsigned>(INT_MAX));
|
|
}
|
|
|
|
CIEKey(const MCSymbol *Personality, unsigned PersonalityEncoding,
|
|
unsigned LSDAEncoding, bool IsSignalFrame, bool IsSimple,
|
|
unsigned RAReg)
|
|
: Personality(Personality), PersonalityEncoding(PersonalityEncoding),
|
|
LsdaEncoding(LSDAEncoding), IsSignalFrame(IsSignalFrame),
|
|
IsSimple(IsSimple), RAReg(RAReg) {}
|
|
|
|
explicit CIEKey(const MCDwarfFrameInfo &Frame)
|
|
: Personality(Frame.Personality),
|
|
PersonalityEncoding(Frame.PersonalityEncoding),
|
|
LsdaEncoding(Frame.LsdaEncoding), IsSignalFrame(Frame.IsSignalFrame),
|
|
IsSimple(Frame.IsSimple), RAReg(Frame.RAReg) {}
|
|
|
|
const MCSymbol *Personality;
|
|
unsigned PersonalityEncoding;
|
|
unsigned LsdaEncoding;
|
|
bool IsSignalFrame;
|
|
bool IsSimple;
|
|
unsigned RAReg;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
namespace llvm {
|
|
|
|
template <> struct DenseMapInfo<CIEKey> {
|
|
static CIEKey getEmptyKey() { return CIEKey::getEmptyKey(); }
|
|
static CIEKey getTombstoneKey() { return CIEKey::getTombstoneKey(); }
|
|
|
|
static unsigned getHashValue(const CIEKey &Key) {
|
|
return static_cast<unsigned>(
|
|
hash_combine(Key.Personality, Key.PersonalityEncoding, Key.LsdaEncoding,
|
|
Key.IsSignalFrame, Key.IsSimple, Key.RAReg));
|
|
}
|
|
|
|
static bool isEqual(const CIEKey &LHS, const CIEKey &RHS) {
|
|
return LHS.Personality == RHS.Personality &&
|
|
LHS.PersonalityEncoding == RHS.PersonalityEncoding &&
|
|
LHS.LsdaEncoding == RHS.LsdaEncoding &&
|
|
LHS.IsSignalFrame == RHS.IsSignalFrame &&
|
|
LHS.IsSimple == RHS.IsSimple &&
|
|
LHS.RAReg == RHS.RAReg;
|
|
}
|
|
};
|
|
|
|
} // end namespace llvm
|
|
|
|
void MCDwarfFrameEmitter::Emit(MCObjectStreamer &Streamer, MCAsmBackend *MAB,
|
|
bool IsEH) {
|
|
Streamer.generateCompactUnwindEncodings(MAB);
|
|
|
|
MCContext &Context = Streamer.getContext();
|
|
const MCObjectFileInfo *MOFI = Context.getObjectFileInfo();
|
|
const MCAsmInfo *AsmInfo = Context.getAsmInfo();
|
|
FrameEmitterImpl Emitter(IsEH, Streamer);
|
|
ArrayRef<MCDwarfFrameInfo> FrameArray = Streamer.getDwarfFrameInfos();
|
|
|
|
// Emit the compact unwind info if available.
|
|
bool NeedsEHFrameSection = !MOFI->getSupportsCompactUnwindWithoutEHFrame();
|
|
if (IsEH && MOFI->getCompactUnwindSection()) {
|
|
bool SectionEmitted = false;
|
|
for (const MCDwarfFrameInfo &Frame : FrameArray) {
|
|
if (Frame.CompactUnwindEncoding == 0) continue;
|
|
if (!SectionEmitted) {
|
|
Streamer.SwitchSection(MOFI->getCompactUnwindSection());
|
|
Streamer.EmitValueToAlignment(AsmInfo->getCodePointerSize());
|
|
SectionEmitted = true;
|
|
}
|
|
NeedsEHFrameSection |=
|
|
Frame.CompactUnwindEncoding ==
|
|
MOFI->getCompactUnwindDwarfEHFrameOnly();
|
|
Emitter.EmitCompactUnwind(Frame);
|
|
}
|
|
}
|
|
|
|
if (!NeedsEHFrameSection) return;
|
|
|
|
MCSection &Section =
|
|
IsEH ? *const_cast<MCObjectFileInfo *>(MOFI)->getEHFrameSection()
|
|
: *MOFI->getDwarfFrameSection();
|
|
|
|
Streamer.SwitchSection(&Section);
|
|
MCSymbol *SectionStart = Context.createTempSymbol();
|
|
Streamer.EmitLabel(SectionStart);
|
|
|
|
DenseMap<CIEKey, const MCSymbol *> CIEStarts;
|
|
|
|
const MCSymbol *DummyDebugKey = nullptr;
|
|
bool CanOmitDwarf = MOFI->getOmitDwarfIfHaveCompactUnwind();
|
|
for (auto I = FrameArray.begin(), E = FrameArray.end(); I != E;) {
|
|
const MCDwarfFrameInfo &Frame = *I;
|
|
++I;
|
|
if (CanOmitDwarf && Frame.CompactUnwindEncoding !=
|
|
MOFI->getCompactUnwindDwarfEHFrameOnly())
|
|
// Don't generate an EH frame if we don't need one. I.e., it's taken care
|
|
// of by the compact unwind encoding.
|
|
continue;
|
|
|
|
CIEKey Key(Frame);
|
|
const MCSymbol *&CIEStart = IsEH ? CIEStarts[Key] : DummyDebugKey;
|
|
if (!CIEStart)
|
|
CIEStart = &Emitter.EmitCIE(Frame);
|
|
|
|
Emitter.EmitFDE(*CIEStart, Frame, I == E, *SectionStart);
|
|
}
|
|
}
|
|
|
|
void MCDwarfFrameEmitter::EmitAdvanceLoc(MCObjectStreamer &Streamer,
|
|
uint64_t AddrDelta) {
|
|
MCContext &Context = Streamer.getContext();
|
|
SmallString<256> Tmp;
|
|
raw_svector_ostream OS(Tmp);
|
|
MCDwarfFrameEmitter::EncodeAdvanceLoc(Context, AddrDelta, OS);
|
|
Streamer.EmitBytes(OS.str());
|
|
}
|
|
|
|
void MCDwarfFrameEmitter::EncodeAdvanceLoc(MCContext &Context,
|
|
uint64_t AddrDelta,
|
|
raw_ostream &OS) {
|
|
// Scale the address delta by the minimum instruction length.
|
|
AddrDelta = ScaleAddrDelta(Context, AddrDelta);
|
|
|
|
if (AddrDelta == 0) {
|
|
} else if (isUIntN(6, AddrDelta)) {
|
|
uint8_t Opcode = dwarf::DW_CFA_advance_loc | AddrDelta;
|
|
OS << Opcode;
|
|
} else if (isUInt<8>(AddrDelta)) {
|
|
OS << uint8_t(dwarf::DW_CFA_advance_loc1);
|
|
OS << uint8_t(AddrDelta);
|
|
} else if (isUInt<16>(AddrDelta)) {
|
|
OS << uint8_t(dwarf::DW_CFA_advance_loc2);
|
|
if (Context.getAsmInfo()->isLittleEndian())
|
|
support::endian::Writer<support::little>(OS).write<uint16_t>(AddrDelta);
|
|
else
|
|
support::endian::Writer<support::big>(OS).write<uint16_t>(AddrDelta);
|
|
} else {
|
|
assert(isUInt<32>(AddrDelta));
|
|
OS << uint8_t(dwarf::DW_CFA_advance_loc4);
|
|
if (Context.getAsmInfo()->isLittleEndian())
|
|
support::endian::Writer<support::little>(OS).write<uint32_t>(AddrDelta);
|
|
else
|
|
support::endian::Writer<support::big>(OS).write<uint32_t>(AddrDelta);
|
|
}
|
|
}
|