aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp')
-rw-r--r--llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp433
1 files changed, 238 insertions, 195 deletions
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
index 81b00f65741b..0a1b75592290 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
@@ -36,123 +36,130 @@ const uint8_t DWARF_CFI_PRIMARY_OPERAND_MASK = 0x3f;
Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset,
uint64_t EndOffset) {
- while (*Offset < EndOffset) {
- uint8_t Opcode = Data.getRelocatedValue(1, Offset);
- // Some instructions have a primary opcode encoded in the top bits.
- uint8_t Primary = Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK;
+ DataExtractor::Cursor C(*Offset);
+ while (C && C.tell() < EndOffset) {
+ uint8_t Opcode = Data.getRelocatedValue(C, 1);
+ if (!C)
+ break;
- if (Primary) {
+ // Some instructions have a primary opcode encoded in the top bits.
+ if (uint8_t Primary = Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK) {
// If it's a primary opcode, the first operand is encoded in the bottom
// bits of the opcode itself.
uint64_t Op1 = Opcode & DWARF_CFI_PRIMARY_OPERAND_MASK;
switch (Primary) {
- default:
- return createStringError(errc::illegal_byte_sequence,
- "Invalid primary CFI opcode 0x%" PRIx8,
- Primary);
case DW_CFA_advance_loc:
case DW_CFA_restore:
addInstruction(Primary, Op1);
break;
case DW_CFA_offset:
- addInstruction(Primary, Op1, Data.getULEB128(Offset));
+ addInstruction(Primary, Op1, Data.getULEB128(C));
break;
- }
- } else {
- // Extended opcode - its value is Opcode itself.
- switch (Opcode) {
default:
- return createStringError(errc::illegal_byte_sequence,
- "Invalid extended CFI opcode 0x%" PRIx8,
- Opcode);
- case DW_CFA_nop:
- case DW_CFA_remember_state:
- case DW_CFA_restore_state:
- case DW_CFA_GNU_window_save:
- // No operands
- addInstruction(Opcode);
- break;
- case DW_CFA_set_loc:
- // Operands: Address
- addInstruction(Opcode, Data.getRelocatedAddress(Offset));
- break;
- case DW_CFA_advance_loc1:
- // Operands: 1-byte delta
- addInstruction(Opcode, Data.getRelocatedValue(1, Offset));
- break;
- case DW_CFA_advance_loc2:
- // Operands: 2-byte delta
- addInstruction(Opcode, Data.getRelocatedValue(2, Offset));
- break;
- case DW_CFA_advance_loc4:
- // Operands: 4-byte delta
- addInstruction(Opcode, Data.getRelocatedValue(4, Offset));
- break;
- case DW_CFA_restore_extended:
- case DW_CFA_undefined:
- case DW_CFA_same_value:
- case DW_CFA_def_cfa_register:
- case DW_CFA_def_cfa_offset:
- case DW_CFA_GNU_args_size:
- // Operands: ULEB128
- addInstruction(Opcode, Data.getULEB128(Offset));
- break;
- case DW_CFA_def_cfa_offset_sf:
- // Operands: SLEB128
- addInstruction(Opcode, Data.getSLEB128(Offset));
- break;
- case DW_CFA_offset_extended:
- case DW_CFA_register:
- case DW_CFA_def_cfa:
- case DW_CFA_val_offset: {
- // Operands: ULEB128, ULEB128
- // Note: We can not embed getULEB128 directly into function
- // argument list. getULEB128 changes Offset and order of evaluation
- // for arguments is unspecified.
- auto op1 = Data.getULEB128(Offset);
- auto op2 = Data.getULEB128(Offset);
- addInstruction(Opcode, op1, op2);
- break;
- }
- case DW_CFA_offset_extended_sf:
- case DW_CFA_def_cfa_sf:
- case DW_CFA_val_offset_sf: {
- // Operands: ULEB128, SLEB128
- // Note: see comment for the previous case
- auto op1 = Data.getULEB128(Offset);
- auto op2 = (uint64_t)Data.getSLEB128(Offset);
- addInstruction(Opcode, op1, op2);
- break;
- }
- case DW_CFA_def_cfa_expression: {
- uint32_t ExprLength = Data.getULEB128(Offset);
- addInstruction(Opcode, 0);
- DataExtractor Extractor(
- Data.getData().slice(*Offset, *Offset + ExprLength),
- Data.isLittleEndian(), Data.getAddressSize());
- Instructions.back().Expression = DWARFExpression(
- Extractor, Data.getAddressSize(), dwarf::DWARF_VERSION);
- *Offset += ExprLength;
- break;
- }
- case DW_CFA_expression:
- case DW_CFA_val_expression: {
- auto RegNum = Data.getULEB128(Offset);
- auto BlockLength = Data.getULEB128(Offset);
- addInstruction(Opcode, RegNum, 0);
- DataExtractor Extractor(
- Data.getData().slice(*Offset, *Offset + BlockLength),
- Data.isLittleEndian(), Data.getAddressSize());
- Instructions.back().Expression = DWARFExpression(
- Extractor, Data.getAddressSize(), dwarf::DWARF_VERSION);
- *Offset += BlockLength;
- break;
- }
+ llvm_unreachable("invalid primary CFI opcode");
}
+ continue;
+ }
+
+ // Extended opcode - its value is Opcode itself.
+ switch (Opcode) {
+ default:
+ return createStringError(errc::illegal_byte_sequence,
+ "invalid extended CFI opcode 0x%" PRIx8, Opcode);
+ case DW_CFA_nop:
+ case DW_CFA_remember_state:
+ case DW_CFA_restore_state:
+ case DW_CFA_GNU_window_save:
+ // No operands
+ addInstruction(Opcode);
+ break;
+ case DW_CFA_set_loc:
+ // Operands: Address
+ addInstruction(Opcode, Data.getRelocatedAddress(C));
+ break;
+ case DW_CFA_advance_loc1:
+ // Operands: 1-byte delta
+ addInstruction(Opcode, Data.getRelocatedValue(C, 1));
+ break;
+ case DW_CFA_advance_loc2:
+ // Operands: 2-byte delta
+ addInstruction(Opcode, Data.getRelocatedValue(C, 2));
+ break;
+ case DW_CFA_advance_loc4:
+ // Operands: 4-byte delta
+ addInstruction(Opcode, Data.getRelocatedValue(C, 4));
+ break;
+ case DW_CFA_restore_extended:
+ case DW_CFA_undefined:
+ case DW_CFA_same_value:
+ case DW_CFA_def_cfa_register:
+ case DW_CFA_def_cfa_offset:
+ case DW_CFA_GNU_args_size:
+ // Operands: ULEB128
+ addInstruction(Opcode, Data.getULEB128(C));
+ break;
+ case DW_CFA_def_cfa_offset_sf:
+ // Operands: SLEB128
+ addInstruction(Opcode, Data.getSLEB128(C));
+ break;
+ case DW_CFA_offset_extended:
+ case DW_CFA_register:
+ case DW_CFA_def_cfa:
+ case DW_CFA_val_offset: {
+ // Operands: ULEB128, ULEB128
+ // Note: We can not embed getULEB128 directly into function
+ // argument list. getULEB128 changes Offset and order of evaluation
+ // for arguments is unspecified.
+ uint64_t op1 = Data.getULEB128(C);
+ uint64_t op2 = Data.getULEB128(C);
+ addInstruction(Opcode, op1, op2);
+ break;
+ }
+ case DW_CFA_offset_extended_sf:
+ case DW_CFA_def_cfa_sf:
+ case DW_CFA_val_offset_sf: {
+ // Operands: ULEB128, SLEB128
+ // Note: see comment for the previous case
+ uint64_t op1 = Data.getULEB128(C);
+ uint64_t op2 = (uint64_t)Data.getSLEB128(C);
+ addInstruction(Opcode, op1, op2);
+ break;
+ }
+ case DW_CFA_def_cfa_expression: {
+ uint64_t ExprLength = Data.getULEB128(C);
+ addInstruction(Opcode, 0);
+ StringRef Expression = Data.getBytes(C, ExprLength);
+
+ DataExtractor Extractor(Expression, Data.isLittleEndian(),
+ Data.getAddressSize());
+ // Note. We do not pass the DWARF format to DWARFExpression, because
+ // DW_OP_call_ref, the only operation which depends on the format, is
+ // prohibited in call frame instructions, see sec. 6.4.2 in DWARFv5.
+ Instructions.back().Expression =
+ DWARFExpression(Extractor, Data.getAddressSize());
+ break;
+ }
+ case DW_CFA_expression:
+ case DW_CFA_val_expression: {
+ uint64_t RegNum = Data.getULEB128(C);
+ addInstruction(Opcode, RegNum, 0);
+
+ uint64_t BlockLength = Data.getULEB128(C);
+ StringRef Expression = Data.getBytes(C, BlockLength);
+ DataExtractor Extractor(Expression, Data.isLittleEndian(),
+ Data.getAddressSize());
+ // Note. We do not pass the DWARF format to DWARFExpression, because
+ // DW_OP_call_ref, the only operation which depends on the format, is
+ // prohibited in call frame instructions, see sec. 6.4.2 in DWARFv5.
+ Instructions.back().Expression =
+ DWARFExpression(Extractor, Data.getAddressSize());
+ break;
+ }
}
}
- return Error::success();
+ *Offset = C.tell();
+ return C.takeError();
}
namespace {
@@ -285,12 +292,33 @@ void CFIProgram::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH,
}
}
+// Returns the CIE identifier to be used by the requested format.
+// CIE ids for .debug_frame sections are defined in Section 7.24 of DWARFv5.
+// For CIE ID in .eh_frame sections see
+// https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
+constexpr uint64_t getCIEId(bool IsDWARF64, bool IsEH) {
+ if (IsEH)
+ return 0;
+ if (IsDWARF64)
+ return DW64_CIE_ID;
+ return DW_CIE_ID;
+}
+
void CIE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const {
- OS << format("%08x %08x %08x CIE", (uint32_t)Offset, (uint32_t)Length,
- DW_CIE_ID)
- << "\n";
- OS << format(" Version: %d\n", Version);
- OS << " Augmentation: \"" << Augmentation << "\"\n";
+ // A CIE with a zero length is a terminator entry in the .eh_frame section.
+ if (IsEH && Length == 0) {
+ OS << format("%08" PRIx64, Offset) << " ZERO terminator\n";
+ return;
+ }
+
+ OS << format("%08" PRIx64, Offset)
+ << format(" %0*" PRIx64, IsDWARF64 ? 16 : 8, Length)
+ << format(" %0*" PRIx64, IsDWARF64 && !IsEH ? 16 : 8,
+ getCIEId(IsDWARF64, IsEH))
+ << " CIE\n"
+ << " Format: " << FormatString(IsDWARF64) << "\n"
+ << format(" Version: %d\n", Version)
+ << " Augmentation: \"" << Augmentation << "\"\n";
if (Version >= 4) {
OS << format(" Address size: %u\n", (uint32_t)AddressSize);
OS << format(" Segment desc size: %u\n",
@@ -313,11 +341,17 @@ void CIE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const {
}
void FDE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const {
- OS << format("%08x %08x %08x FDE ", (uint32_t)Offset, (uint32_t)Length,
- (int32_t)LinkedCIEOffset);
- OS << format("cie=%08x pc=%08x...%08x\n", (int32_t)LinkedCIEOffset,
- (uint32_t)InitialLocation,
- (uint32_t)InitialLocation + (uint32_t)AddressRange);
+ OS << format("%08" PRIx64, Offset)
+ << format(" %0*" PRIx64, IsDWARF64 ? 16 : 8, Length)
+ << format(" %0*" PRIx64, IsDWARF64 && !IsEH ? 16 : 8, CIEPointer)
+ << " FDE cie=";
+ if (LinkedCIE)
+ OS << format("%08" PRIx64, LinkedCIE->getOffset());
+ else
+ OS << "<invalid offset>";
+ OS << format(" pc=%08" PRIx64 "...%08" PRIx64 "\n", InitialLocation,
+ InitialLocation + AddressRange);
+ OS << " Format: " << FormatString(IsDWARF64) << "\n";
if (LSDAAddress)
OS << format(" LSDA Address: %016" PRIx64 "\n", *LSDAAddress);
CFIs.dump(OS, MRI, IsEH);
@@ -340,36 +374,28 @@ static void LLVM_ATTRIBUTE_UNUSED dumpDataAux(DataExtractor Data,
errs() << "\n";
}
-// This is a workaround for old compilers which do not allow
-// noreturn attribute usage in lambdas. Once the support for those
-// compilers are phased out, we can remove this and return back to
-// a ReportError lambda: [StartOffset](const char *ErrorMsg).
-static void LLVM_ATTRIBUTE_NORETURN ReportError(uint64_t StartOffset,
- const char *ErrorMsg) {
- std::string Str;
- raw_string_ostream OS(Str);
- OS << format(ErrorMsg, StartOffset);
- OS.flush();
- report_fatal_error(Str);
-}
-
-void DWARFDebugFrame::parse(DWARFDataExtractor Data) {
+Error DWARFDebugFrame::parse(DWARFDataExtractor Data) {
uint64_t Offset = 0;
DenseMap<uint64_t, CIE *> CIEs;
while (Data.isValidOffset(Offset)) {
uint64_t StartOffset = Offset;
- bool IsDWARF64 = false;
- uint64_t Length = Data.getRelocatedValue(4, &Offset);
- uint64_t Id;
+ uint64_t Length;
+ DwarfFormat Format;
+ std::tie(Length, Format) = Data.getInitialLength(&Offset);
+ bool IsDWARF64 = Format == DWARF64;
- if (Length == dwarf::DW_LENGTH_DWARF64) {
- // DWARF-64 is distinguished by the first 32 bits of the initial length
- // field being 0xffffffff. Then, the next 64 bits are the actual entry
- // length.
- IsDWARF64 = true;
- Length = Data.getRelocatedValue(8, &Offset);
+ // If the Length is 0, then this CIE is a terminator. We add it because some
+ // dumper tools might need it to print something special for such entries
+ // (e.g. llvm-objdump --dwarf=frames prints "ZERO terminator").
+ if (Length == 0) {
+ auto Cie = std::make_unique<CIE>(
+ IsDWARF64, StartOffset, 0, 0, SmallString<8>(), 0, 0, 0, 0, 0,
+ SmallString<8>(), 0, 0, None, None, Arch);
+ CIEs[StartOffset] = Cie.get();
+ Entries.push_back(std::move(Cie));
+ break;
}
// At this point, Offset points to the next field after Length.
@@ -380,14 +406,21 @@ void DWARFDebugFrame::parse(DWARFDataExtractor Data) {
uint64_t EndStructureOffset = Offset + Length;
// The Id field's size depends on the DWARF format
- Id = Data.getUnsigned(&Offset, (IsDWARF64 && !IsEH) ? 8 : 4);
- bool IsCIE =
- ((IsDWARF64 && Id == DW64_CIE_ID) || Id == DW_CIE_ID || (IsEH && !Id));
+ Error Err = Error::success();
+ uint64_t Id = Data.getRelocatedValue((IsDWARF64 && !IsEH) ? 8 : 4, &Offset,
+ /*SectionIndex=*/nullptr, &Err);
+ if (Err)
+ return Err;
- if (IsCIE) {
+ if (Id == getCIEId(IsDWARF64, IsEH)) {
uint8_t Version = Data.getU8(&Offset);
const char *Augmentation = Data.getCStr(&Offset);
StringRef AugmentationString(Augmentation ? Augmentation : "");
+ // TODO: we should provide a way to report a warning and continue dumping.
+ if (IsEH && Version != 1)
+ return createStringError(errc::not_supported,
+ "unsupported CIE version: %" PRIu8, Version);
+
uint8_t AddressSize = Version < 4 ? Data.getAddressSize() :
Data.getU8(&Offset);
Data.setAddressSize(AddressSize);
@@ -411,61 +444,66 @@ void DWARFDebugFrame::parse(DWARFDataExtractor Data) {
// Walk the augmentation string to get all the augmentation data.
for (unsigned i = 0, e = AugmentationString.size(); i != e; ++i) {
switch (AugmentationString[i]) {
- default:
- ReportError(
- StartOffset,
- "Unknown augmentation character in entry at %" PRIx64);
- case 'L':
- LSDAPointerEncoding = Data.getU8(&Offset);
- break;
- case 'P': {
- if (Personality)
- ReportError(StartOffset,
- "Duplicate personality in entry at %" PRIx64);
- PersonalityEncoding = Data.getU8(&Offset);
- Personality = Data.getEncodedPointer(
- &Offset, *PersonalityEncoding,
- EHFrameAddress ? EHFrameAddress + Offset : 0);
- break;
- }
- case 'R':
- FDEPointerEncoding = Data.getU8(&Offset);
- break;
- case 'S':
- // Current frame is a signal trampoline.
- break;
- case 'z':
- if (i)
- ReportError(StartOffset,
- "'z' must be the first character at %" PRIx64);
- // Parse the augmentation length first. We only parse it if
- // the string contains a 'z'.
- AugmentationLength = Data.getULEB128(&Offset);
- StartAugmentationOffset = Offset;
- EndAugmentationOffset = Offset + *AugmentationLength;
- break;
- case 'B':
- // B-Key is used for signing functions associated with this
- // augmentation string
- break;
+ default:
+ return createStringError(
+ errc::invalid_argument,
+ "unknown augmentation character in entry at 0x%" PRIx64,
+ StartOffset);
+ case 'L':
+ LSDAPointerEncoding = Data.getU8(&Offset);
+ break;
+ case 'P': {
+ if (Personality)
+ return createStringError(
+ errc::invalid_argument,
+ "duplicate personality in entry at 0x%" PRIx64, StartOffset);
+ PersonalityEncoding = Data.getU8(&Offset);
+ Personality = Data.getEncodedPointer(
+ &Offset, *PersonalityEncoding,
+ EHFrameAddress ? EHFrameAddress + Offset : 0);
+ break;
+ }
+ case 'R':
+ FDEPointerEncoding = Data.getU8(&Offset);
+ break;
+ case 'S':
+ // Current frame is a signal trampoline.
+ break;
+ case 'z':
+ if (i)
+ return createStringError(
+ errc::invalid_argument,
+ "'z' must be the first character at 0x%" PRIx64, StartOffset);
+ // Parse the augmentation length first. We only parse it if
+ // the string contains a 'z'.
+ AugmentationLength = Data.getULEB128(&Offset);
+ StartAugmentationOffset = Offset;
+ EndAugmentationOffset = Offset + *AugmentationLength;
+ break;
+ case 'B':
+ // B-Key is used for signing functions associated with this
+ // augmentation string
+ break;
}
}
if (AugmentationLength.hasValue()) {
if (Offset != EndAugmentationOffset)
- ReportError(StartOffset,
- "Parsing augmentation data at %" PRIx64 " failed");
-
+ return createStringError(errc::invalid_argument,
+ "parsing augmentation data at 0x%" PRIx64
+ " failed",
+ StartOffset);
AugmentationData = Data.getData().slice(StartAugmentationOffset,
EndAugmentationOffset);
}
}
auto Cie = std::make_unique<CIE>(
- StartOffset, Length, Version, AugmentationString, AddressSize,
- SegmentDescriptorSize, CodeAlignmentFactor, DataAlignmentFactor,
- ReturnAddressRegister, AugmentationData, FDEPointerEncoding,
- LSDAPointerEncoding, Personality, PersonalityEncoding, Arch);
+ IsDWARF64, StartOffset, Length, Version, AugmentationString,
+ AddressSize, SegmentDescriptorSize, CodeAlignmentFactor,
+ DataAlignmentFactor, ReturnAddressRegister, AugmentationData,
+ FDEPointerEncoding, LSDAPointerEncoding, Personality,
+ PersonalityEncoding, Arch);
CIEs[StartOffset] = Cie.get();
Entries.emplace_back(std::move(Cie));
} else {
@@ -479,9 +517,10 @@ void DWARFDebugFrame::parse(DWARFDataExtractor Data) {
if (IsEH) {
// The address size is encoded in the CIE we reference.
if (!Cie)
- ReportError(StartOffset, "Parsing FDE data at %" PRIx64
- " failed due to missing CIE");
-
+ return createStringError(errc::invalid_argument,
+ "parsing FDE data at 0x%" PRIx64
+ " failed due to missing CIE",
+ StartOffset);
if (auto Val = Data.getEncodedPointer(
&Offset, Cie->getFDEPointerEncoding(),
EHFrameAddress ? EHFrameAddress + Offset : 0)) {
@@ -507,28 +546,32 @@ void DWARFDebugFrame::parse(DWARFDataExtractor Data) {
}
if (Offset != EndAugmentationOffset)
- ReportError(StartOffset,
- "Parsing augmentation data at %" PRIx64 " failed");
+ return createStringError(errc::invalid_argument,
+ "parsing augmentation data at 0x%" PRIx64
+ " failed",
+ StartOffset);
}
} else {
InitialLocation = Data.getRelocatedAddress(&Offset);
AddressRange = Data.getRelocatedAddress(&Offset);
}
- Entries.emplace_back(new FDE(StartOffset, Length, CIEPointer,
- InitialLocation, AddressRange,
- Cie, LSDAAddress, Arch));
+ Entries.emplace_back(new FDE(IsDWARF64, StartOffset, Length, CIEPointer,
+ InitialLocation, AddressRange, Cie,
+ LSDAAddress, Arch));
}
if (Error E =
- Entries.back()->cfis().parse(Data, &Offset, EndStructureOffset)) {
- report_fatal_error(toString(std::move(E)));
- }
+ Entries.back()->cfis().parse(Data, &Offset, EndStructureOffset))
+ return E;
if (Offset != EndStructureOffset)
- ReportError(StartOffset,
- "Parsing entry instructions at %" PRIx64 " failed");
+ return createStringError(
+ errc::invalid_argument,
+ "parsing entry instructions at 0x%" PRIx64 " failed", StartOffset);
}
+
+ return Error::success();
}
FrameEntry *DWARFDebugFrame::getEntryAtOffset(uint64_t Offset) const {