Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions llvm/include/llvm/BinaryFormat/SFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,29 +49,27 @@ enum class ABI : uint8_t {

/// SFrame FRE Types. Bits 0-3 of FuncDescEntry.Info.
enum class FREType : uint8_t {
Addr1 = 0,
Addr2 = 1,
Addr4 = 2,
#define HANDLE_SFRAME_FRE_TYPE(CODE, NAME) NAME = CODE,
#include "llvm/BinaryFormat/SFrameConstants.def"
};

/// SFrame FDE Types. Bit 4 of FuncDescEntry.Info.
enum class FDEType : uint8_t {
PCInc = 0,
PCMask = 1,
#define HANDLE_SFRAME_FDE_TYPE(CODE, NAME) NAME = CODE,
#include "llvm/BinaryFormat/SFrameConstants.def"
};

/// Speficies key used for signing return addresses. Bit 5 of
/// FuncDescEntry.Info.
enum class AArch64PAuthKey : uint8_t {
A = 0,
B = 1,
#define HANDLE_SFRAME_AARCH64_PAUTH_KEY(CODE, NAME) NAME = CODE,
#include "llvm/BinaryFormat/SFrameConstants.def"
};

/// Size of stack offsets. Bits 5-6 of FREInfo.Info.
/// Size of stack offsets. Bits 6-7 of FREInfo.Info.
enum class FREOffset : uint8_t {
B1 = 0,
B2 = 1,
B4 = 2,
#define HANDLE_SFRAME_FRE_OFFSET(CODE, NAME) NAME = CODE,
#include "llvm/BinaryFormat/SFrameConstants.def"
};

/// Stack frame base register. Bit 0 of FREInfo.Info.
Expand Down Expand Up @@ -166,6 +164,10 @@ template <endianness E> using FrameRowEntryAddr4 = FrameRowEntry<uint32_t, E>;
ArrayRef<EnumEntry<Version>> getVersions();
ArrayRef<EnumEntry<Flags>> getFlags();
ArrayRef<EnumEntry<ABI>> getABIs();
ArrayRef<EnumEntry<FREType>> getFRETypes();
ArrayRef<EnumEntry<FDEType>> getFDETypes();
ArrayRef<EnumEntry<AArch64PAuthKey>> getAArch64PAuthKeys();
ArrayRef<EnumEntry<FREOffset>> getFREOffsets();

} // namespace sframe
} // namespace llvm
Expand Down
41 changes: 39 additions & 2 deletions llvm/include/llvm/BinaryFormat/SFrameConstants.def
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
//
//===----------------------------------------------------------------------===//

#if !(defined(HANDLE_SFRAME_VERSION) || defined(HANDLE_SFRAME_FLAG) || \
defined(HANDLE_SFRAME_ABI))
#if !(defined(HANDLE_SFRAME_VERSION) || defined(HANDLE_SFRAME_FLAG) || \
defined(HANDLE_SFRAME_ABI) || defined(HANDLE_SFRAME_FRE_TYPE) || \
defined(HANDLE_SFRAME_FDE_TYPE) || \
defined(HANDLE_SFRAME_AARCH64_PAUTH_KEY) || \
defined(HANDLE_SFRAME_FRE_OFFSET))
#error "Missing HANDLE_SFRAME definition"
#endif

Expand All @@ -23,6 +26,22 @@
#define HANDLE_SFRAME_ABI(CODE, NAME)
#endif

#ifndef HANDLE_SFRAME_FRE_TYPE
#define HANDLE_SFRAME_FRE_TYPE(CODE, NAME)
#endif

#ifndef HANDLE_SFRAME_FDE_TYPE
#define HANDLE_SFRAME_FDE_TYPE(CODE, NAME)
#endif

#ifndef HANDLE_SFRAME_AARCH64_PAUTH_KEY
#define HANDLE_SFRAME_AARCH64_PAUTH_KEY(CODE, NAME)
#endif

#ifndef HANDLE_SFRAME_FRE_OFFSET
#define HANDLE_SFRAME_FRE_OFFSET(CODE, NAME)
#endif

HANDLE_SFRAME_VERSION(0x01, V1)
HANDLE_SFRAME_VERSION(0x02, V2)

Expand All @@ -34,6 +53,24 @@ HANDLE_SFRAME_ABI(0x01, AArch64EndianBig)
HANDLE_SFRAME_ABI(0x02, AArch64EndianLittle)
HANDLE_SFRAME_ABI(0x03, AMD64EndianLittle)

HANDLE_SFRAME_FRE_TYPE(0x00, Addr1)
HANDLE_SFRAME_FRE_TYPE(0x01, Addr2)
HANDLE_SFRAME_FRE_TYPE(0x02, Addr4)

HANDLE_SFRAME_FDE_TYPE(0, PCInc)
HANDLE_SFRAME_FDE_TYPE(1, PCMask)

HANDLE_SFRAME_AARCH64_PAUTH_KEY(0, A)
HANDLE_SFRAME_AARCH64_PAUTH_KEY(1, B)

HANDLE_SFRAME_FRE_OFFSET(0, B1)
HANDLE_SFRAME_FRE_OFFSET(1, B2)
HANDLE_SFRAME_FRE_OFFSET(2, B4)

#undef HANDLE_SFRAME_VERSION
#undef HANDLE_SFRAME_FLAG
#undef HANDLE_SFRAME_ABI
#undef HANDLE_SFRAME_FRE_TYPE
#undef HANDLE_SFRAME_FDE_TYPE
#undef HANDLE_SFRAME_AARCH64_PAUTH_KEY
#undef HANDLE_SFRAME_FRE_OFFSET
19 changes: 16 additions & 3 deletions llvm/include/llvm/Object/SFrameParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,37 @@ namespace object {

template <endianness E> class SFrameParser {
public:
static Expected<SFrameParser> create(ArrayRef<uint8_t> Contents);
static Expected<SFrameParser> create(ArrayRef<uint8_t> Contents,
uint64_t SectionAddress);

const sframe::Preamble<E> &getPreamble() const { return Header.Preamble; }
const sframe::Header<E> &getHeader() const { return Header; }

Expected<ArrayRef<uint8_t>> getAuxHeader() const;

bool usesFixedRAOffset() const {
return getHeader().ABIArch == sframe::ABI::AMD64EndianLittle;
}
bool usesFixedFPOffset() const {
return false; // Not used in any currently defined ABI.
}

using FDERange = ArrayRef<sframe::FuncDescEntry<E>>;
Expected<FDERange> fdes() const;

// Decodes the start address of the given FDE, which must be one of the
// objects returned by the `fdes()` function.
uint64_t getAbsoluteStartAddress(typename FDERange::iterator FDE) const;

private:
ArrayRef<uint8_t> Data;
uint64_t SectionAddress;
const sframe::Header<E> &Header;

SFrameParser(ArrayRef<uint8_t> Data, const sframe::Header<E> &Header)
: Data(Data), Header(Header) {}
SFrameParser(ArrayRef<uint8_t> Data, uint64_t SectionAddress, const sframe::Header<E> &Header)
: Data(Data), SectionAddress(SectionAddress), Header(Header) {}

uint64_t getFDEBegin() const { return sizeof(Header) + Header.AuxHdrLen + Header.FDEOff; }
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ibhagatgnu, I'm a little but unsure about this part. This is the interpretation I get from the specification, and I think it matches this endiannes-flipping code in libsframe. However, that's not what happens in the actual parsing code. Can I assume the last part to be an omission/bug?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@labath the SFrame FDE offset is the offset of the FDE sub-section from the end of the SFrame header. So, the omission in the sframe_decode is harmless, but should be done for doc purposes and clarity.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand. The value I linked to is used to locate the FDEs, so if sfh_fdeoff is not zero, it will end up looking at the wrong place. Or are you saying that it does not matter in practice because the value will always be zero? That, I suppose is true (and I don't see why would anyone use a value other than zero here), but the spec does allow it so I suppose someone could do it.

I guess my main question is whether I've implemented the FDE location code correctly.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, using the FDEoffset is the right thing to do. You are right that the spec allows non-zero value for fde_offset. So I correct my previous statement: the omission in sframe_decode should be corrected to use the fde_offset to be fully compliant.

};

extern template class SFrameParser<endianness::big>;
Expand Down
33 changes: 33 additions & 0 deletions llvm/lib/BinaryFormat/SFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,36 @@ ArrayRef<EnumEntry<sframe::ABI>> sframe::getABIs() {
};
return ArrayRef(ABIs);
}

ArrayRef<EnumEntry<sframe::FREType>> sframe::getFRETypes() {
static constexpr EnumEntry<sframe::FREType> FRETypes[] = {
#define HANDLE_SFRAME_FRE_TYPE(CODE, NAME) {#NAME, sframe::FREType::NAME},
#include "llvm/BinaryFormat/SFrameConstants.def"
};
return ArrayRef(FRETypes);
}

ArrayRef<EnumEntry<sframe::FDEType>> sframe::getFDETypes() {
static constexpr EnumEntry<sframe::FDEType> FDETypes[] = {
#define HANDLE_SFRAME_FDE_TYPE(CODE, NAME) {#NAME, sframe::FDEType::NAME},
#include "llvm/BinaryFormat/SFrameConstants.def"
};
return ArrayRef(FDETypes);
}

ArrayRef<EnumEntry<sframe::AArch64PAuthKey>> sframe::getAArch64PAuthKeys() {
static constexpr EnumEntry<sframe::AArch64PAuthKey> AArch64PAuthKeys[] = {
#define HANDLE_SFRAME_AARCH64_PAUTH_KEY(CODE, NAME) \
{#NAME, sframe::AArch64PAuthKey::NAME},
#include "llvm/BinaryFormat/SFrameConstants.def"
};
return ArrayRef(AArch64PAuthKeys);
}

ArrayRef<EnumEntry<sframe::FREOffset>> sframe::getFREOffsets() {
static constexpr EnumEntry<sframe::FREOffset> FREOffsets[] = {
#define HANDLE_SFRAME_FRE_OFFSET(CODE, NAME) {#NAME, sframe::FREOffset::NAME},
#include "llvm/BinaryFormat/SFrameConstants.def"
};
return ArrayRef(FREOffsets);
}
64 changes: 55 additions & 9 deletions llvm/lib/Object/SFrameParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,34 @@
using namespace llvm;
using namespace llvm::object;

template <typename T>
static Expected<const T &> getDataSliceAs(ArrayRef<uint8_t> Data,
uint64_t Offset) {
static_assert(std::is_trivial_v<T>);
if (Data.size() < Offset + sizeof(T)) {
static Expected<ArrayRef<uint8_t>>
getDataSlice(ArrayRef<uint8_t> Data, uint64_t Offset, uint64_t Size) {
// Check for overflow.
if (Offset + Size < Offset || Offset + Size < Size ||
Offset + Size > Data.size()) {
return createStringError(
formatv("unexpected end of data at offset {0:x} while reading [{1:x}, "
"{2:x})",
Data.size(), Offset, Offset + sizeof(T))
Data.size(), Offset, Offset + Size)
.str(),
object_error::unexpected_eof);
}
return *reinterpret_cast<const T *>(Data.data() + Offset);
return Data.slice(Offset, Size);
}

template <typename T>
static Expected<const T &> getDataSliceAs(ArrayRef<uint8_t> Data,
uint64_t Offset) {
static_assert(std::is_trivial_v<T>);
Expected<ArrayRef<uint8_t>> Slice = getDataSlice(Data, Offset, sizeof(T));
if (!Slice)
return Slice.takeError();

return *reinterpret_cast<const T *>(Slice->data());
}

template <endianness E>
Expected<SFrameParser<E>> SFrameParser<E>::create(ArrayRef<uint8_t> Contents) {
Expected<SFrameParser<E>> SFrameParser<E>::create(ArrayRef<uint8_t> Contents, uint64_t SectionAddress) {
Expected<const sframe::Preamble<E> &> Preamble =
getDataSliceAs<sframe::Preamble<E>>(Contents, 0);
if (!Preamble)
Expand All @@ -48,7 +59,42 @@ Expected<SFrameParser<E>> SFrameParser<E>::create(ArrayRef<uint8_t> Contents) {
getDataSliceAs<sframe::Header<E>>(Contents, 0);
if (!Header)
return Header.takeError();
return SFrameParser(Contents, *Header);
return SFrameParser(Contents, SectionAddress, *Header);
}


template<endianness E>
Expected<ArrayRef<uint8_t>> SFrameParser<E>::getAuxHeader() const {
return getDataSlice(Data, sizeof(Header), Header.AuxHdrLen);
}

template<endianness E>
Expected<ArrayRef<sframe::FuncDescEntry<E>>> SFrameParser<E>::fdes() const {
Expected<ArrayRef<uint8_t>> Slice = getDataSlice(
Data, getFDEBegin(), Header.NumFDEs * sizeof(sframe::FuncDescEntry<E>));
if (!Slice)
return Slice.takeError();
return ArrayRef(
reinterpret_cast<const sframe::FuncDescEntry<E> *>(Slice->data()),
Header.NumFDEs);
}

template<endianness E>
uint64_t SFrameParser<E>::getAbsoluteStartAddress(typename FDERange::iterator FDE) const {
uint64_t Result = SectionAddress + FDE->StartAddress;

if ((getPreamble().Flags.value() & sframe::Flags::FDEFuncStartPCRel) ==
sframe::Flags::FDEFuncStartPCRel) {
uintptr_t DataPtr = reinterpret_cast<uintptr_t>(Data.data());
uintptr_t FDEPtr = reinterpret_cast<uintptr_t>(&*FDE);

assert(DataPtr <= FDEPtr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sfde_func_start_address is a signed displacement, so the sframe segment could be placed before or after the text segment. I personally favor making it unsigned to double the possible range and which would make this assertion valid, but that isn't as the spec is written today.

Copy link
Collaborator Author

@labath labath Jul 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't check the value of the field -- only its address. The goal is to catch the (UB) case where someone passes an FDE iterator from one object into the parser for another object. I'll add a message to the assertion to make that clearer.

assert(FDEPtr < DataPtr + Data.size());

Result += FDEPtr - DataPtr;
}

return Result;
}

template class llvm::object::SFrameParser<endianness::big>;
Expand Down
Loading
Loading