//===- SFrameParser.cpp ---------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Object/SFrameParser.h" #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; static Expected> getDataSlice(ArrayRef 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, End) .str(), object_error::unexpected_eof); } return Data.slice(Offset, Size); } template static Expected> getDataSliceAsArrayOf(ArrayRef Data, uint64_t Offset, uint64_t Count) { static_assert(std::is_trivial_v); Expected> Slice = getDataSlice(Data, Offset, sizeof(T) * Count); if (!Slice) return Slice.takeError(); return ArrayRef(reinterpret_cast(Slice->data()), Count); } template static Expected getDataSliceAs(ArrayRef Data, uint64_t Offset) { Expected> Array = getDataSliceAsArrayOf(Data, Offset, 1); if (!Array) return Array.takeError(); return Array->front(); } template Expected> SFrameParser::create(ArrayRef Contents, uint64_t SectionAddress) { Expected &> Preamble = getDataSliceAs>(Contents, 0); if (!Preamble) return Preamble.takeError(); if (Preamble->Magic != sframe::Magic) return createError( formatv("invalid magic number ({0:x+4})", Preamble->Magic.value())); if (Preamble->Version != sframe::Version::V2) return createError( formatv("invalid/unsupported version number ({0})", static_cast(Preamble->Version.value()))); Expected &> Header = getDataSliceAs>(Contents, 0); if (!Header) return Header.takeError(); return SFrameParser(Contents, SectionAddress, *Header); } template Expected> SFrameParser::getAuxHeader() const { return getDataSlice(Data, sizeof(Header), Header.AuxHdrLen); } template Expected>> SFrameParser::fdes() const { Expected> Slice = getDataSlice( Data, getFDEBase(), Header.NumFDEs * sizeof(sframe::FuncDescEntry)); if (!Slice) return Slice.takeError(); return ArrayRef( reinterpret_cast *>(Slice->data()), Header.NumFDEs); } template uint64_t SFrameParser::getAbsoluteStartAddress( typename FDERange::iterator FDE) const { uint64_t Result = SectionAddress + FDE->StartAddress; if ((getPreamble().Flags.value() & sframe::Flags::FDEFuncStartPCRel) == sframe::Flags::FDEFuncStartPCRel) Result += offsetOf(FDE); return Result; } template uint64_t SFrameParser::offsetOf(typename FDERange::iterator FDE) const { uintptr_t DataPtr = reinterpret_cast(Data.data()); uintptr_t FDEPtr = reinterpret_cast(&*FDE); assert(DataPtr <= FDEPtr && FDEPtr < DataPtr + Data.size() && "Iterator does not belong to this object!"); return FDEPtr - DataPtr; } template static Error readArray(ArrayRef Data, uint64_t Count, uint64_t &Offset, SmallVectorImpl &Vec) { Expected> RawArray = getDataSliceAsArrayOf(Data, Offset, Count); if (!RawArray) return RawArray.takeError(); Offset += Count * sizeof(EndianT); Vec.resize(Count); llvm::copy(*RawArray, Vec.begin()); return Error::success(); } template static Error readFRE(ArrayRef Data, uint64_t &Offset, typename SFrameParser::FrameRowEntry &FRE) { Expected> RawFRE = getDataSliceAs>(Data, Offset); if (!RawFRE) return RawFRE.takeError(); Offset += sizeof(*RawFRE); FRE.StartAddress = RawFRE->StartAddress; FRE.Info.Info = RawFRE->Info.Info; switch (FRE.Info.getOffsetSize()) { case sframe::FREOffset::B1: return readArray>( Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets); case sframe::FREOffset::B2: return readArray>( Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets); case sframe::FREOffset::B4: return readArray>( Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets); } return createError(formatv("unsupported FRE offset size {0} at offset {1:x+}", static_cast(FRE.Info.getOffsetSize()), Offset)); } template Error SFrameParser::FallibleFREIterator::inc() { if (++Idx == Size) return Error::success(); switch (FREType) { case sframe::FREType::Addr1: return readFRE(Data, Offset, FRE); case sframe::FREType::Addr2: return readFRE(Data, Offset, FRE); case sframe::FREType::Addr4: return readFRE(Data, Offset, FRE); } return createError(formatv("unsupported FRE type {0} at offset {1:x+}", static_cast(FREType), Offset)); } template iterator_range::fre_iterator> SFrameParser::fres(const sframe::FuncDescEntry &FDE, Error &Err) const { uint64_t Offset = getFREBase() + FDE.StartFREOff; fre_iterator BeforeBegin = make_fallible_itr( FallibleFREIterator(Data, FDE.Info.getFREType(), -1, FDE.NumFREs, Offset), Err); fre_iterator End = make_fallible_end( FallibleFREIterator(Data, FDE.Info.getFREType(), FDE.NumFREs, FDE.NumFREs, /*Offset=*/0)); return {++BeforeBegin, End}; } static std::optional getOffset(ArrayRef Offsets, size_t Idx) { if (Offsets.size() > Idx) return Offsets[Idx]; return std::nullopt; } // The interpretation of offsets is ABI-specific. The implementation of this and // the following functions may need to be adjusted when adding support for a new // ABI. template std::optional SFrameParser::getCFAOffset(const FrameRowEntry &FRE) const { return getOffset(FRE.Offsets, 0); } template std::optional SFrameParser::getRAOffset(const FrameRowEntry &FRE) const { if (usesFixedRAOffset()) return Header.CFAFixedRAOffset; return getOffset(FRE.Offsets, 1); } template std::optional SFrameParser::getFPOffset(const FrameRowEntry &FRE) const { if (usesFixedFPOffset()) return Header.CFAFixedFPOffset; return getOffset(FRE.Offsets, usesFixedRAOffset() ? 1 : 2); } template ArrayRef SFrameParser::getExtraOffsets(const FrameRowEntry &FRE) const { size_t UsedOffsets = 1; // CFA if (!usesFixedRAOffset()) ++UsedOffsets; if (!usesFixedFPOffset()) ++UsedOffsets; if (FRE.Offsets.size() > UsedOffsets) return ArrayRef(FRE.Offsets).drop_front(UsedOffsets); return {}; } template class LLVM_EXPORT_TEMPLATE llvm::object::SFrameParser; template class LLVM_EXPORT_TEMPLATE llvm::object::SFrameParser;