llvm-mirror/lib/ObjectYAML/DWARFEmitter.cpp
Chandler Carruth ae65e281f3 Update the file headers across all of the LLVM projects in the monorepo
to reflect the new license.

We understand that people may be surprised that we're moving the header
entirely to discuss the new license. We checked this carefully with the
Foundation's lawyer and we believe this is the correct approach.

Essentially, all code in the project is now made available by the LLVM
project under our new license, so you will see that the license headers
include that license only. Some of our contributors have contributed
code under our old license, and accordingly, we have retained a copy of
our old license notice in the top-level files in each project and
repository.

llvm-svn: 351636
2019-01-19 08:50:56 +00:00

379 lines
13 KiB
C++

//===- DWARFEmitter - Convert YAML to DWARF binary data -------------------===//
//
// 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
/// The DWARF component of yaml2obj. Provided as library code for tests.
///
//===----------------------------------------------------------------------===//
#include "llvm/ObjectYAML/DWARFEmitter.h"
#include "DWARFVisitor.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ObjectYAML/DWARFYAML.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SwapByteOrder.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
using namespace llvm;
template <typename T>
static void writeInteger(T Integer, raw_ostream &OS, bool IsLittleEndian) {
if (IsLittleEndian != sys::IsLittleEndianHost)
sys::swapByteOrder(Integer);
OS.write(reinterpret_cast<char *>(&Integer), sizeof(T));
}
static void writeVariableSizedInteger(uint64_t Integer, size_t Size,
raw_ostream &OS, bool IsLittleEndian) {
if (8 == Size)
writeInteger((uint64_t)Integer, OS, IsLittleEndian);
else if (4 == Size)
writeInteger((uint32_t)Integer, OS, IsLittleEndian);
else if (2 == Size)
writeInteger((uint16_t)Integer, OS, IsLittleEndian);
else if (1 == Size)
writeInteger((uint8_t)Integer, OS, IsLittleEndian);
else
assert(false && "Invalid integer write size.");
}
static void ZeroFillBytes(raw_ostream &OS, size_t Size) {
std::vector<uint8_t> FillData;
FillData.insert(FillData.begin(), Size, 0);
OS.write(reinterpret_cast<char *>(FillData.data()), Size);
}
static void writeInitialLength(const DWARFYAML::InitialLength &Length,
raw_ostream &OS, bool IsLittleEndian) {
writeInteger((uint32_t)Length.TotalLength, OS, IsLittleEndian);
if (Length.isDWARF64())
writeInteger((uint64_t)Length.TotalLength64, OS, IsLittleEndian);
}
void DWARFYAML::EmitDebugStr(raw_ostream &OS, const DWARFYAML::Data &DI) {
for (auto Str : DI.DebugStrings) {
OS.write(Str.data(), Str.size());
OS.write('\0');
}
}
void DWARFYAML::EmitDebugAbbrev(raw_ostream &OS, const DWARFYAML::Data &DI) {
for (auto AbbrevDecl : DI.AbbrevDecls) {
encodeULEB128(AbbrevDecl.Code, OS);
encodeULEB128(AbbrevDecl.Tag, OS);
OS.write(AbbrevDecl.Children);
for (auto Attr : AbbrevDecl.Attributes) {
encodeULEB128(Attr.Attribute, OS);
encodeULEB128(Attr.Form, OS);
if (Attr.Form == dwarf::DW_FORM_implicit_const)
encodeSLEB128(Attr.Value, OS);
}
encodeULEB128(0, OS);
encodeULEB128(0, OS);
}
}
void DWARFYAML::EmitDebugAranges(raw_ostream &OS, const DWARFYAML::Data &DI) {
for (auto Range : DI.ARanges) {
auto HeaderStart = OS.tell();
writeInitialLength(Range.Length, OS, DI.IsLittleEndian);
writeInteger((uint16_t)Range.Version, OS, DI.IsLittleEndian);
writeInteger((uint32_t)Range.CuOffset, OS, DI.IsLittleEndian);
writeInteger((uint8_t)Range.AddrSize, OS, DI.IsLittleEndian);
writeInteger((uint8_t)Range.SegSize, OS, DI.IsLittleEndian);
auto HeaderSize = OS.tell() - HeaderStart;
auto FirstDescriptor = alignTo(HeaderSize, Range.AddrSize * 2);
ZeroFillBytes(OS, FirstDescriptor - HeaderSize);
for (auto Descriptor : Range.Descriptors) {
writeVariableSizedInteger(Descriptor.Address, Range.AddrSize, OS,
DI.IsLittleEndian);
writeVariableSizedInteger(Descriptor.Length, Range.AddrSize, OS,
DI.IsLittleEndian);
}
ZeroFillBytes(OS, Range.AddrSize * 2);
}
}
void DWARFYAML::EmitPubSection(raw_ostream &OS,
const DWARFYAML::PubSection &Sect,
bool IsLittleEndian) {
writeInitialLength(Sect.Length, OS, IsLittleEndian);
writeInteger((uint16_t)Sect.Version, OS, IsLittleEndian);
writeInteger((uint32_t)Sect.UnitOffset, OS, IsLittleEndian);
writeInteger((uint32_t)Sect.UnitSize, OS, IsLittleEndian);
for (auto Entry : Sect.Entries) {
writeInteger((uint32_t)Entry.DieOffset, OS, IsLittleEndian);
if (Sect.IsGNUStyle)
writeInteger((uint32_t)Entry.Descriptor, OS, IsLittleEndian);
OS.write(Entry.Name.data(), Entry.Name.size());
OS.write('\0');
}
}
namespace {
/// An extension of the DWARFYAML::ConstVisitor which writes compile
/// units and DIEs to a stream.
class DumpVisitor : public DWARFYAML::ConstVisitor {
raw_ostream &OS;
protected:
void onStartCompileUnit(const DWARFYAML::Unit &CU) override {
writeInitialLength(CU.Length, OS, DebugInfo.IsLittleEndian);
writeInteger((uint16_t)CU.Version, OS, DebugInfo.IsLittleEndian);
if(CU.Version >= 5) {
writeInteger((uint8_t)CU.Type, OS, DebugInfo.IsLittleEndian);
writeInteger((uint8_t)CU.AddrSize, OS, DebugInfo.IsLittleEndian);
writeInteger((uint32_t)CU.AbbrOffset, OS, DebugInfo.IsLittleEndian);
}else {
writeInteger((uint32_t)CU.AbbrOffset, OS, DebugInfo.IsLittleEndian);
writeInteger((uint8_t)CU.AddrSize, OS, DebugInfo.IsLittleEndian);
}
}
void onStartDIE(const DWARFYAML::Unit &CU,
const DWARFYAML::Entry &DIE) override {
encodeULEB128(DIE.AbbrCode, OS);
}
void onValue(const uint8_t U) override {
writeInteger(U, OS, DebugInfo.IsLittleEndian);
}
void onValue(const uint16_t U) override {
writeInteger(U, OS, DebugInfo.IsLittleEndian);
}
void onValue(const uint32_t U) override {
writeInteger(U, OS, DebugInfo.IsLittleEndian);
}
void onValue(const uint64_t U, const bool LEB = false) override {
if (LEB)
encodeULEB128(U, OS);
else
writeInteger(U, OS, DebugInfo.IsLittleEndian);
}
void onValue(const int64_t S, const bool LEB = false) override {
if (LEB)
encodeSLEB128(S, OS);
else
writeInteger(S, OS, DebugInfo.IsLittleEndian);
}
void onValue(const StringRef String) override {
OS.write(String.data(), String.size());
OS.write('\0');
}
void onValue(const MemoryBufferRef MBR) override {
OS.write(MBR.getBufferStart(), MBR.getBufferSize());
}
public:
DumpVisitor(const DWARFYAML::Data &DI, raw_ostream &Out)
: DWARFYAML::ConstVisitor(DI), OS(Out) {}
};
} // namespace
void DWARFYAML::EmitDebugInfo(raw_ostream &OS, const DWARFYAML::Data &DI) {
DumpVisitor Visitor(DI, OS);
Visitor.traverseDebugInfo();
}
static void EmitFileEntry(raw_ostream &OS, const DWARFYAML::File &File) {
OS.write(File.Name.data(), File.Name.size());
OS.write('\0');
encodeULEB128(File.DirIdx, OS);
encodeULEB128(File.ModTime, OS);
encodeULEB128(File.Length, OS);
}
void DWARFYAML::EmitDebugLine(raw_ostream &OS, const DWARFYAML::Data &DI) {
for (const auto &LineTable : DI.DebugLines) {
writeInitialLength(LineTable.Length, OS, DI.IsLittleEndian);
uint64_t SizeOfPrologueLength = LineTable.Length.isDWARF64() ? 8 : 4;
writeInteger((uint16_t)LineTable.Version, OS, DI.IsLittleEndian);
writeVariableSizedInteger(LineTable.PrologueLength, SizeOfPrologueLength,
OS, DI.IsLittleEndian);
writeInteger((uint8_t)LineTable.MinInstLength, OS, DI.IsLittleEndian);
if (LineTable.Version >= 4)
writeInteger((uint8_t)LineTable.MaxOpsPerInst, OS, DI.IsLittleEndian);
writeInteger((uint8_t)LineTable.DefaultIsStmt, OS, DI.IsLittleEndian);
writeInteger((uint8_t)LineTable.LineBase, OS, DI.IsLittleEndian);
writeInteger((uint8_t)LineTable.LineRange, OS, DI.IsLittleEndian);
writeInteger((uint8_t)LineTable.OpcodeBase, OS, DI.IsLittleEndian);
for (auto OpcodeLength : LineTable.StandardOpcodeLengths)
writeInteger((uint8_t)OpcodeLength, OS, DI.IsLittleEndian);
for (auto IncludeDir : LineTable.IncludeDirs) {
OS.write(IncludeDir.data(), IncludeDir.size());
OS.write('\0');
}
OS.write('\0');
for (auto File : LineTable.Files)
EmitFileEntry(OS, File);
OS.write('\0');
for (auto Op : LineTable.Opcodes) {
writeInteger((uint8_t)Op.Opcode, OS, DI.IsLittleEndian);
if (Op.Opcode == 0) {
encodeULEB128(Op.ExtLen, OS);
writeInteger((uint8_t)Op.SubOpcode, OS, DI.IsLittleEndian);
switch (Op.SubOpcode) {
case dwarf::DW_LNE_set_address:
case dwarf::DW_LNE_set_discriminator:
writeVariableSizedInteger(Op.Data, DI.CompileUnits[0].AddrSize, OS,
DI.IsLittleEndian);
break;
case dwarf::DW_LNE_define_file:
EmitFileEntry(OS, Op.FileEntry);
break;
case dwarf::DW_LNE_end_sequence:
break;
default:
for (auto OpByte : Op.UnknownOpcodeData)
writeInteger((uint8_t)OpByte, OS, DI.IsLittleEndian);
}
} else if (Op.Opcode < LineTable.OpcodeBase) {
switch (Op.Opcode) {
case dwarf::DW_LNS_copy:
case dwarf::DW_LNS_negate_stmt:
case dwarf::DW_LNS_set_basic_block:
case dwarf::DW_LNS_const_add_pc:
case dwarf::DW_LNS_set_prologue_end:
case dwarf::DW_LNS_set_epilogue_begin:
break;
case dwarf::DW_LNS_advance_pc:
case dwarf::DW_LNS_set_file:
case dwarf::DW_LNS_set_column:
case dwarf::DW_LNS_set_isa:
encodeULEB128(Op.Data, OS);
break;
case dwarf::DW_LNS_advance_line:
encodeSLEB128(Op.SData, OS);
break;
case dwarf::DW_LNS_fixed_advance_pc:
writeInteger((uint16_t)Op.Data, OS, DI.IsLittleEndian);
break;
default:
for (auto OpData : Op.StandardOpcodeData) {
encodeULEB128(OpData, OS);
}
}
}
}
}
}
using EmitFuncType = void (*)(raw_ostream &, const DWARFYAML::Data &);
static void
EmitDebugSectionImpl(const DWARFYAML::Data &DI, EmitFuncType EmitFunc,
StringRef Sec,
StringMap<std::unique_ptr<MemoryBuffer>> &OutputBuffers) {
std::string Data;
raw_string_ostream DebugInfoStream(Data);
EmitFunc(DebugInfoStream, DI);
DebugInfoStream.flush();
if (!Data.empty())
OutputBuffers[Sec] = MemoryBuffer::getMemBufferCopy(Data);
}
namespace {
class DIEFixupVisitor : public DWARFYAML::Visitor {
uint64_t Length;
public:
DIEFixupVisitor(DWARFYAML::Data &DI) : DWARFYAML::Visitor(DI){};
private:
virtual void onStartCompileUnit(DWARFYAML::Unit &CU) { Length = 7; }
virtual void onEndCompileUnit(DWARFYAML::Unit &CU) {
CU.Length.setLength(Length);
}
virtual void onStartDIE(DWARFYAML::Unit &CU, DWARFYAML::Entry &DIE) {
Length += getULEB128Size(DIE.AbbrCode);
}
virtual void onValue(const uint8_t U) { Length += 1; }
virtual void onValue(const uint16_t U) { Length += 2; }
virtual void onValue(const uint32_t U) { Length += 4; }
virtual void onValue(const uint64_t U, const bool LEB = false) {
if (LEB)
Length += getULEB128Size(U);
else
Length += 8;
}
virtual void onValue(const int64_t S, const bool LEB = false) {
if (LEB)
Length += getSLEB128Size(S);
else
Length += 8;
}
virtual void onValue(const StringRef String) { Length += String.size() + 1; }
virtual void onValue(const MemoryBufferRef MBR) {
Length += MBR.getBufferSize();
}
};
} // namespace
Expected<StringMap<std::unique_ptr<MemoryBuffer>>>
DWARFYAML::EmitDebugSections(StringRef YAMLString, bool ApplyFixups,
bool IsLittleEndian) {
yaml::Input YIn(YAMLString);
DWARFYAML::Data DI;
DI.IsLittleEndian = IsLittleEndian;
YIn >> DI;
if (YIn.error())
return errorCodeToError(YIn.error());
if (ApplyFixups) {
DIEFixupVisitor DIFixer(DI);
DIFixer.traverseDebugInfo();
}
StringMap<std::unique_ptr<MemoryBuffer>> DebugSections;
EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugInfo, "debug_info",
DebugSections);
EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugLine, "debug_line",
DebugSections);
EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugStr, "debug_str",
DebugSections);
EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugAbbrev, "debug_abbrev",
DebugSections);
EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugAranges, "debug_aranges",
DebugSections);
return std::move(DebugSections);
}