Please use GitHub pull requests for new patches. Phabricator shutdown timeline
Changeset View
Changeset View
Standalone View
Standalone View
llvm/lib/ObjectYAML/DWARFEmitter.cpp
//===- DWARFEmitter - Convert YAML to DWARF binary data -------------------===// | //===- DWARFEmitter - Convert YAML to DWARF binary data -------------------===// | ||||
// | // | ||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||||
// See https://llvm.org/LICENSE.txt for license information. | // See https://llvm.org/LICENSE.txt for license information. | ||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||||
// | // | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
/// | /// | ||||
/// \file | /// \file | ||||
/// The DWARF component of yaml2obj. Provided as library code for tests. | /// The DWARF component of yaml2obj. Provided as library code for tests. | ||||
/// | /// | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
#include "llvm/ObjectYAML/DWARFEmitter.h" | #include "llvm/ObjectYAML/DWARFEmitter.h" | ||||
#include "DWARFVisitor.h" | |||||
#include "llvm/ADT/ArrayRef.h" | #include "llvm/ADT/ArrayRef.h" | ||||
#include "llvm/ADT/StringMap.h" | #include "llvm/ADT/StringMap.h" | ||||
#include "llvm/ADT/StringRef.h" | #include "llvm/ADT/StringRef.h" | ||||
#include "llvm/BinaryFormat/Dwarf.h" | #include "llvm/BinaryFormat/Dwarf.h" | ||||
#include "llvm/ObjectYAML/DWARFYAML.h" | #include "llvm/ObjectYAML/DWARFYAML.h" | ||||
#include "llvm/Support/Errc.h" | #include "llvm/Support/Errc.h" | ||||
#include "llvm/Support/Error.h" | #include "llvm/Support/Error.h" | ||||
#include "llvm/Support/Host.h" | #include "llvm/Support/Host.h" | ||||
▲ Show 20 Lines • Show All 176 Lines • ▼ Show 20 Lines | if (IsGNUPubSec) | ||||
writeInteger((uint8_t)Entry.Descriptor, OS, IsLittleEndian); | writeInteger((uint8_t)Entry.Descriptor, OS, IsLittleEndian); | ||||
OS.write(Entry.Name.data(), Entry.Name.size()); | OS.write(Entry.Name.data(), Entry.Name.size()); | ||||
OS.write('\0'); | OS.write('\0'); | ||||
} | } | ||||
return Error::success(); | return Error::success(); | ||||
} | } | ||||
namespace { | static unsigned getOffsetSize(const DWARFYAML::Unit &Unit) { | ||||
/// An extension of the DWARFYAML::ConstVisitor which writes compile | return Unit.Format == dwarf::DWARF64 ? 8 : 4; | ||||
/// units and DIEs to a stream. | |||||
class DumpVisitor : public DWARFYAML::ConstVisitor { | |||||
raw_ostream &OS; | |||||
protected: | |||||
void onStartCompileUnit(const DWARFYAML::Unit &CU) override { | |||||
writeInitialLength(CU.Format, 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); | |||||
cantFail(writeVariableSizedInteger(CU.AbbrOffset, | |||||
CU.Format == dwarf::DWARF64 ? 8 : 4, | |||||
OS, DebugInfo.IsLittleEndian)); | |||||
} else { | |||||
cantFail(writeVariableSizedInteger(CU.AbbrOffset, | |||||
CU.Format == dwarf::DWARF64 ? 8 : 4, | |||||
OS, DebugInfo.IsLittleEndian)); | |||||
writeInteger((uint8_t)CU.AddrSize, OS, DebugInfo.IsLittleEndian); | |||||
} | |||||
} | } | ||||
void onStartDIE(const DWARFYAML::Unit &CU, | static unsigned getRefSize(const DWARFYAML::Unit &Unit) { | ||||
const DWARFYAML::Entry &DIE) override { | if (Unit.Version == 2) | ||||
encodeULEB128(DIE.AbbrCode, OS); | return Unit.AddrSize; | ||||
return getOffsetSize(Unit); | |||||
} | } | ||||
void onValue(const uint8_t U) override { | static Expected<uint64_t> writeDIE(ArrayRef<DWARFYAML::Abbrev> AbbrevDecls, | ||||
writeInteger(U, OS, DebugInfo.IsLittleEndian); | const DWARFYAML::Unit &Unit, | ||||
} | const DWARFYAML::Entry &Entry, | ||||
raw_ostream &OS, bool IsLittleEndian) { | |||||
uint64_t EntryBegin = OS.tell(); | |||||
encodeULEB128(Entry.AbbrCode, OS); | |||||
uint32_t AbbrCode = Entry.AbbrCode; | |||||
if (AbbrCode == 0 || Entry.Values.empty()) | |||||
return OS.tell() - EntryBegin; | |||||
void onValue(const uint16_t U) override { | if (AbbrCode > AbbrevDecls.size()) | ||||
writeInteger(U, OS, DebugInfo.IsLittleEndian); | return createStringError( | ||||
errc::invalid_argument, | |||||
"abbrev code must be less than or equal to the number of " | |||||
"entries in abbreviation table"); | |||||
const DWARFYAML::Abbrev &Abbrev = AbbrevDecls[AbbrCode - 1]; | |||||
auto FormVal = Entry.Values.begin(); | |||||
auto AbbrForm = Abbrev.Attributes.begin(); | |||||
for (; FormVal != Entry.Values.end() && AbbrForm != Abbrev.Attributes.end(); | |||||
++FormVal, ++AbbrForm) { | |||||
dwarf::Form Form = AbbrForm->Form; | |||||
bool Indirect; | |||||
do { | |||||
Indirect = false; | |||||
switch (Form) { | |||||
case dwarf::DW_FORM_addr: | |||||
// TODO: Test this error. | |||||
if (Error Err = writeVariableSizedInteger(FormVal->Value, Unit.AddrSize, | |||||
OS, IsLittleEndian)) | |||||
return std::move(Err); | |||||
break; | |||||
case dwarf::DW_FORM_ref_addr: | |||||
// TODO: Test this error. | |||||
if (Error Err = writeVariableSizedInteger( | |||||
FormVal->Value, getRefSize(Unit), OS, IsLittleEndian)) | |||||
return std::move(Err); | |||||
break; | |||||
case dwarf::DW_FORM_exprloc: | |||||
case dwarf::DW_FORM_block: | |||||
encodeULEB128(FormVal->BlockData.size(), OS); | |||||
OS.write((const char *)FormVal->BlockData.data(), | |||||
FormVal->BlockData.size()); | |||||
break; | |||||
case dwarf::DW_FORM_block1: { | |||||
writeInteger((uint8_t)FormVal->BlockData.size(), OS, IsLittleEndian); | |||||
OS.write((const char *)FormVal->BlockData.data(), | |||||
FormVal->BlockData.size()); | |||||
break; | |||||
} | |||||
case dwarf::DW_FORM_block2: { | |||||
writeInteger((uint16_t)FormVal->BlockData.size(), OS, IsLittleEndian); | |||||
OS.write((const char *)FormVal->BlockData.data(), | |||||
FormVal->BlockData.size()); | |||||
break; | |||||
} | |||||
case dwarf::DW_FORM_block4: { | |||||
writeInteger((uint32_t)FormVal->BlockData.size(), OS, IsLittleEndian); | |||||
OS.write((const char *)FormVal->BlockData.data(), | |||||
FormVal->BlockData.size()); | |||||
break; | |||||
} | |||||
case dwarf::DW_FORM_strx: | |||||
case dwarf::DW_FORM_addrx: | |||||
case dwarf::DW_FORM_rnglistx: | |||||
case dwarf::DW_FORM_loclistx: | |||||
case dwarf::DW_FORM_udata: | |||||
case dwarf::DW_FORM_ref_udata: | |||||
case dwarf::DW_FORM_GNU_addr_index: | |||||
case dwarf::DW_FORM_GNU_str_index: | |||||
encodeULEB128(FormVal->Value, OS); | |||||
break; | |||||
case dwarf::DW_FORM_data1: | |||||
case dwarf::DW_FORM_ref1: | |||||
case dwarf::DW_FORM_flag: | |||||
case dwarf::DW_FORM_strx1: | |||||
case dwarf::DW_FORM_addrx1: | |||||
writeInteger((uint8_t)FormVal->Value, OS, IsLittleEndian); | |||||
break; | |||||
case dwarf::DW_FORM_data2: | |||||
case dwarf::DW_FORM_ref2: | |||||
case dwarf::DW_FORM_strx2: | |||||
case dwarf::DW_FORM_addrx2: | |||||
writeInteger((uint16_t)FormVal->Value, OS, IsLittleEndian); | |||||
break; | |||||
case dwarf::DW_FORM_data4: | |||||
case dwarf::DW_FORM_ref4: | |||||
case dwarf::DW_FORM_ref_sup4: | |||||
case dwarf::DW_FORM_strx4: | |||||
case dwarf::DW_FORM_addrx4: | |||||
writeInteger((uint32_t)FormVal->Value, OS, IsLittleEndian); | |||||
break; | |||||
case dwarf::DW_FORM_data8: | |||||
case dwarf::DW_FORM_ref8: | |||||
case dwarf::DW_FORM_ref_sup8: | |||||
case dwarf::DW_FORM_ref_sig8: | |||||
writeInteger((uint64_t)FormVal->Value, OS, IsLittleEndian); | |||||
break; | |||||
case dwarf::DW_FORM_sdata: | |||||
encodeSLEB128(FormVal->Value, OS); | |||||
break; | |||||
case dwarf::DW_FORM_string: | |||||
OS.write(FormVal->CStr.data(), FormVal->CStr.size()); | |||||
OS.write('\0'); | |||||
break; | |||||
case dwarf::DW_FORM_indirect: | |||||
encodeULEB128(FormVal->Value, OS); | |||||
Indirect = true; | |||||
Form = static_cast<dwarf::Form>((uint64_t)FormVal->Value); | |||||
++FormVal; | |||||
break; | |||||
case dwarf::DW_FORM_strp: | |||||
case dwarf::DW_FORM_sec_offset: | |||||
case dwarf::DW_FORM_GNU_ref_alt: | |||||
case dwarf::DW_FORM_GNU_strp_alt: | |||||
case dwarf::DW_FORM_line_strp: | |||||
case dwarf::DW_FORM_strp_sup: | |||||
// TODO: Test this error. | |||||
if (Error Err = writeVariableSizedInteger( | |||||
FormVal->Value, getOffsetSize(Unit), OS, IsLittleEndian)) | |||||
return std::move(Err); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
} while (Indirect); | |||||
} | } | ||||
labath: Maybe this is for a separate patch, since you're just moving these, but this code already… | |||||
Thanks! I'll do it in a separate patch. Higuoxing: Thanks! I'll do it in a separate patch. | |||||
void onValue(const uint32_t U) override { | return OS.tell() - EntryBegin; | ||||
writeInteger(U, OS, DebugInfo.IsLittleEndian); | |||||
} | } | ||||
void onValue(const uint64_t U, const bool LEB = false) override { | static void writeDWARFOffset(uint64_t Offset, dwarf::DwarfFormat Format, | ||||
if (LEB) | raw_ostream &OS, bool IsLittleEndian) { | ||||
encodeULEB128(U, OS); | cantFail(writeVariableSizedInteger(Offset, Format == dwarf::DWARF64 ? 8 : 4, | ||||
else | OS, IsLittleEndian)); | ||||
writeInteger(U, OS, DebugInfo.IsLittleEndian); | |||||
} | } | ||||
void onValue(const int64_t S, const bool LEB = false) override { | Error DWARFYAML::emitDebugInfo(raw_ostream &OS, const DWARFYAML::Data &DI) { | ||||
if (LEB) | for (const DWARFYAML::Unit &Unit : DI.CompileUnits) { | ||||
encodeSLEB128(S, OS); | uint64_t Length = 3; // sizeof(version) + sizeof(address_size) | ||||
Length += Unit.Version >= 5 ? 1 : 0; // sizeof(unit_type) | |||||
Length += | |||||
Unit.Format == dwarf::DWARF64 ? 8 : 4; // sizeof(debug_abbrev_offset) | |||||
Since the comments repeat the entire expression, it would be less repetitive to just structure the code in a way that comments can be attached to the actual expressions. Maybe something like: Lenght = 3; // version + address_size Length += (Unit.Version >= 5 ? 1 : 0); // unit_type Length += Unit.getFormParams().getDwarfOffsetByteSize(); // abbrev_offset labath: Since the comments repeat the entire expression, it would be less repetitive to just structure… | |||||
// Since the length of the current compilation unit is undetermined yet, we | |||||
// firstly write the content of the compilation unit to a buffer to | |||||
// calculate it and then serialize the buffer content to the actual output | |||||
// stream. | |||||
std::string EntryBuffer; | |||||
raw_string_ostream EntryBufferOS(EntryBuffer); | |||||
for (const DWARFYAML::Entry &Entry : Unit.Entries) { | |||||
if (Expected<uint64_t> EntryLength = writeDIE( | |||||
DI.AbbrevDecls, Unit, Entry, EntryBufferOS, DI.IsLittleEndian)) | |||||
Length += *EntryLength; | |||||
else | else | ||||
writeInteger(S, OS, DebugInfo.IsLittleEndian); | return EntryLength.takeError(); | ||||
} | } | ||||
void onValue(const StringRef String) override { | // If the length is specified in the YAML description, we use it instead of | ||||
OS.write(String.data(), String.size()); | // the actual length. | ||||
OS.write('\0'); | if (Unit.Length) | ||||
} | Length = *Unit.Length; | ||||
void onValue(const MemoryBufferRef MBR) override { | writeInitialLength(Unit.Format, Length, OS, DI.IsLittleEndian); | ||||
OS.write(MBR.getBufferStart(), MBR.getBufferSize()); | writeInteger((uint16_t)Unit.Version, OS, DI.IsLittleEndian); | ||||
if (Unit.Version >= 5) { | |||||
writeInteger((uint8_t)Unit.Type, OS, DI.IsLittleEndian); | |||||
writeInteger((uint8_t)Unit.AddrSize, OS, DI.IsLittleEndian); | |||||
writeDWARFOffset(Unit.AbbrOffset, Unit.Format, OS, DI.IsLittleEndian); | |||||
} else { | |||||
writeDWARFOffset(Unit.AbbrOffset, Unit.Format, OS, DI.IsLittleEndian); | |||||
a writeDwarfOffset function might be handy. labath: a writeDwarfOffset function might be handy. | |||||
Not Done ReplyInline ActionsI'm guessing the new function can be used elsewhere too (I expect in places like .debug_aranges for example). In another patch, please consider looking at that. jhenderson: I'm guessing the new function can be used elsewhere too (I expect in places like .debug_aranges… | |||||
Yeah, it can be used in the .debug_str_offsets/debug_rnglists/debug_loclists/debug_aranges sections, etc. I've applied it in this patch and will apply it to other sections in a follow-up patch. Higuoxing: Yeah, it can be used in the .debug_str_offsets/debug_rnglists/debug_loclists/debug_aranges… | |||||
writeInteger((uint8_t)Unit.AddrSize, OS, DI.IsLittleEndian); | |||||
} | } | ||||
public: | OS.write(EntryBuffer.data(), EntryBuffer.size()); | ||||
DumpVisitor(const DWARFYAML::Data &DI, raw_ostream &Out) | } | ||||
: DWARFYAML::ConstVisitor(DI), OS(Out) {} | |||||
}; | |||||
} // namespace | |||||
Error DWARFYAML::emitDebugInfo(raw_ostream &OS, const DWARFYAML::Data &DI) { | return Error::success(); | ||||
DumpVisitor Visitor(DI, OS); | |||||
return Visitor.traverseDebugInfo(); | |||||
} | } | ||||
static void emitFileEntry(raw_ostream &OS, const DWARFYAML::File &File) { | static void emitFileEntry(raw_ostream &OS, const DWARFYAML::File &File) { | ||||
OS.write(File.Name.data(), File.Name.size()); | OS.write(File.Name.data(), File.Name.size()); | ||||
OS.write('\0'); | OS.write('\0'); | ||||
encodeULEB128(File.DirIdx, OS); | encodeULEB128(File.DirIdx, OS); | ||||
encodeULEB128(File.ModTime, OS); | encodeULEB128(File.ModTime, OS); | ||||
encodeULEB128(File.Length, OS); | encodeULEB128(File.Length, OS); | ||||
▲ Show 20 Lines • Show All 340 Lines • ▼ Show 20 Lines | if (Error Err = EmitFunc(DebugInfoStream, DI)) | ||||
return Err; | return Err; | ||||
DebugInfoStream.flush(); | DebugInfoStream.flush(); | ||||
if (!Data.empty()) | if (!Data.empty()) | ||||
OutputBuffers[Sec] = MemoryBuffer::getMemBufferCopy(Data); | OutputBuffers[Sec] = MemoryBuffer::getMemBufferCopy(Data); | ||||
return Error::success(); | return Error::success(); | ||||
} | } | ||||
namespace { | |||||
class DIEFixupVisitor : public DWARFYAML::Visitor { | |||||
uint64_t Length; | |||||
public: | |||||
DIEFixupVisitor(DWARFYAML::Data &DI) : DWARFYAML::Visitor(DI){}; | |||||
protected: | |||||
void onStartCompileUnit(DWARFYAML::Unit &CU) override { | |||||
// Size of the unit header, excluding the length field itself. | |||||
Length = CU.Version >= 5 ? 8 : 7; | |||||
} | |||||
void onEndCompileUnit(DWARFYAML::Unit &CU) override { CU.Length = Length; } | |||||
void onStartDIE(DWARFYAML::Unit &CU, DWARFYAML::Entry &DIE) override { | |||||
Length += getULEB128Size(DIE.AbbrCode); | |||||
} | |||||
void onValue(const uint8_t U) override { Length += 1; } | |||||
void onValue(const uint16_t U) override { Length += 2; } | |||||
void onValue(const uint32_t U) override { Length += 4; } | |||||
void onValue(const uint64_t U, const bool LEB = false) override { | |||||
if (LEB) | |||||
Length += getULEB128Size(U); | |||||
else | |||||
Length += 8; | |||||
} | |||||
void onValue(const int64_t S, const bool LEB = false) override { | |||||
if (LEB) | |||||
Length += getSLEB128Size(S); | |||||
else | |||||
Length += 8; | |||||
} | |||||
void onValue(const StringRef String) override { Length += String.size() + 1; } | |||||
void onValue(const MemoryBufferRef MBR) override { | |||||
Length += MBR.getBufferSize(); | |||||
} | |||||
}; | |||||
} // namespace | |||||
Expected<StringMap<std::unique_ptr<MemoryBuffer>>> | Expected<StringMap<std::unique_ptr<MemoryBuffer>>> | ||||
DWARFYAML::emitDebugSections(StringRef YAMLString, bool ApplyFixups, | DWARFYAML::emitDebugSections(StringRef YAMLString, bool IsLittleEndian) { | ||||
bool IsLittleEndian) { | |||||
auto CollectDiagnostic = [](const SMDiagnostic &Diag, void *DiagContext) { | auto CollectDiagnostic = [](const SMDiagnostic &Diag, void *DiagContext) { | ||||
*static_cast<SMDiagnostic *>(DiagContext) = Diag; | *static_cast<SMDiagnostic *>(DiagContext) = Diag; | ||||
}; | }; | ||||
SMDiagnostic GeneratedDiag; | SMDiagnostic GeneratedDiag; | ||||
yaml::Input YIn(YAMLString, /*Ctxt=*/nullptr, CollectDiagnostic, | yaml::Input YIn(YAMLString, /*Ctxt=*/nullptr, CollectDiagnostic, | ||||
&GeneratedDiag); | &GeneratedDiag); | ||||
DWARFYAML::Data DI; | DWARFYAML::Data DI; | ||||
DI.IsLittleEndian = IsLittleEndian; | DI.IsLittleEndian = IsLittleEndian; | ||||
YIn >> DI; | YIn >> DI; | ||||
if (YIn.error()) | if (YIn.error()) | ||||
return createStringError(YIn.error(), GeneratedDiag.getMessage()); | return createStringError(YIn.error(), GeneratedDiag.getMessage()); | ||||
if (ApplyFixups) { | |||||
DIEFixupVisitor DIFixer(DI); | |||||
if (Error Err = DIFixer.traverseDebugInfo()) | |||||
return std::move(Err); | |||||
} | |||||
StringMap<std::unique_ptr<MemoryBuffer>> DebugSections; | StringMap<std::unique_ptr<MemoryBuffer>> DebugSections; | ||||
Error Err = emitDebugSectionImpl(DI, &DWARFYAML::emitDebugInfo, "debug_info", | Error Err = emitDebugSectionImpl(DI, &DWARFYAML::emitDebugInfo, "debug_info", | ||||
DebugSections); | DebugSections); | ||||
Err = joinErrors(std::move(Err), | Err = joinErrors(std::move(Err), | ||||
emitDebugSectionImpl(DI, &DWARFYAML::emitDebugLine, | emitDebugSectionImpl(DI, &DWARFYAML::emitDebugLine, | ||||
"debug_line", DebugSections)); | "debug_line", DebugSections)); | ||||
Err = joinErrors(std::move(Err), | Err = joinErrors(std::move(Err), | ||||
emitDebugSectionImpl(DI, &DWARFYAML::emitDebugStr, | emitDebugSectionImpl(DI, &DWARFYAML::emitDebugStr, | ||||
Show All 15 Lines |
Maybe this is for a separate patch, since you're just moving these, but this code already exists in BinaryFormat/Dwarf.h. It would be nice if DWARFYaml::Unit had a dwarf::FormParams field or a getter, and then these things could be retrieved by querying that object...