The shared header of an FDE/CIE, containing a length in bytes (DWARF's "initial length field")
and a value which differentiates CIEs from FDEs and maps FDEs to their corresponding CIEs. The
.eh_frame format also includes a third variation, here called .terminator, which acts as a
sentinel for the whole section.
CommonInformationEntry.parse and FrameDescriptionEntry.parse expect the EntryHeader to
have been parsed first: they accept data stored in the EntryHeader, and only read the bytes
following this header.
const EntryHeader = union(enum)
const EntryHeader = union(enum) {
cie: struct {
format: Format,
/// Remaining bytes in the CIE. These are parseable by `CommonInformationEntry.parse`.
bytes_len: u64,
},
fde: struct {
/// Offset into the section of the corresponding CIE, *including* its entry header.
cie_offset: u64,
/// Remaining bytes in the FDE. These are parseable by `FrameDescriptionEntry.parse`.
bytes_len: u64,
},
/// The `.eh_frame` format includes terminators which indicate that the last CIE/FDE has been
/// reached. However, `.debug_frame` does not include such a terminator, so the caller must
/// keep track of how many section bytes remain when parsing all entries in `.debug_frame`.
terminator,
fn read(r: *Reader, header_section_offset: u64, section: Section, endian: Endian) !EntryHeader {
const unit_header = try Dwarf.readUnitHeader(r, endian);
if (unit_header.unit_length == 0) return .terminator;
// Next is a value which will disambiguate CIEs and FDEs. Annoyingly, LSB Core makes this
// value always 4-byte, whereas DWARF makes it depend on the `dwarf.Format`.
const cie_ptr_or_id_size: u8 = switch (section) {
.eh_frame => 4,
.debug_frame => switch (unit_header.format) {
.@"32" => 4,
.@"64" => 8,
},
};
const cie_ptr_or_id = switch (cie_ptr_or_id_size) {
4 => try r.takeInt(u32, endian),
8 => try r.takeInt(u64, endian),
else => unreachable,
};
const remaining_bytes = unit_header.unit_length - cie_ptr_or_id_size;
// If this entry is a CIE, then `cie_ptr_or_id` will have this value, which is different
// between the DWARF `.debug_frame` section and the LSB Core `.eh_frame` section.
const cie_id: u64 = switch (section) {
.eh_frame => 0,
.debug_frame => switch (unit_header.format) {
.@"32" => maxInt(u32),
.@"64" => maxInt(u64),
},
};
if (cie_ptr_or_id == cie_id) {
return .{ .cie = .{
.format = unit_header.format,
.bytes_len = remaining_bytes,
} };
}
// This is an FDE -- `cie_ptr_or_id` points to the associated CIE. Unfortunately, the format
// of that pointer again differs between `.debug_frame` and `.eh_frame`.
const cie_offset = switch (section) {
.eh_frame => try std.math.sub(u64, header_section_offset + unit_header.header_length, cie_ptr_or_id),
.debug_frame => cie_ptr_or_id,
};
return .{ .fde = .{
.cie_offset = cie_offset,
.bytes_len = remaining_bytes,
} };
}
}