Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
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
22 changes: 19 additions & 3 deletions llvm/include/llvm/Object/SFrameParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,40 @@ 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;
}
};

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);
}
67 changes: 58 additions & 9 deletions llvm/lib/Object/SFrameParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,41 @@
#include "llvm/BinaryFormat/SFrame.h"
#include "llvm/Object/Error.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/MathExtras.h"

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) {
uint64_t End = SaturatingAdd(Offset, Size);
// Data.size() cannot be UINT64_MAX, as it would occupy the whole address
// space.
if (End > 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, End)
.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 +62,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