1//===- SFrameParser.cpp ---------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/Object/SFrameParser.h"
10#include "llvm/BinaryFormat/SFrame.h"
11#include "llvm/Object/Error.h"
12#include "llvm/Support/FormatVariadic.h"
13#include "llvm/Support/MathExtras.h"
14
15using namespace llvm;
16using namespace llvm::object;
17
18static Expected<ArrayRef<uint8_t>>
19getDataSlice(ArrayRef<uint8_t> Data, uint64_t Offset, uint64_t Size) {
20 uint64_t End = SaturatingAdd(X: Offset, Y: Size);
21 // Data.size() cannot be UINT64_MAX, as it would occupy the whole address
22 // space.
23 if (End > Data.size()) {
24 return createStringError(
25 Msg: formatv(Fmt: "unexpected end of data at offset {0:x} while reading [{1:x}, "
26 "{2:x})",
27 Vals: Data.size(), Vals&: Offset, Vals&: End)
28 .str(),
29 EC: object_error::unexpected_eof);
30 }
31 return Data.slice(N: Offset, M: Size);
32}
33
34template <typename T>
35static Expected<ArrayRef<T>>
36getDataSliceAsArrayOf(ArrayRef<uint8_t> Data, uint64_t Offset, uint64_t Count) {
37 static_assert(std::is_trivial_v<T>);
38 Expected<ArrayRef<uint8_t>> Slice =
39 getDataSlice(Data, Offset, Size: sizeof(T) * Count);
40 if (!Slice)
41 return Slice.takeError();
42
43 return ArrayRef(reinterpret_cast<const T *>(Slice->data()), Count);
44}
45
46template <typename T>
47static Expected<const T &> getDataSliceAs(ArrayRef<uint8_t> Data,
48 uint64_t Offset) {
49 Expected<ArrayRef<T>> Array = getDataSliceAsArrayOf<T>(Data, Offset, 1);
50 if (!Array)
51 return Array.takeError();
52
53 return Array->front();
54}
55
56template <endianness E>
57Expected<SFrameParser<E>> SFrameParser<E>::create(ArrayRef<uint8_t> Contents,
58 uint64_t SectionAddress) {
59 Expected<const sframe::Preamble<E> &> Preamble =
60 getDataSliceAs<sframe::Preamble<E>>(Contents, 0);
61 if (!Preamble)
62 return Preamble.takeError();
63
64 if (Preamble->Magic != sframe::Magic)
65 return createError(
66 formatv("invalid magic number ({0:x+4})", Preamble->Magic.value()));
67 if (Preamble->Version != sframe::Version::V2)
68 return createError(
69 Err: formatv(Fmt: "invalid/unsupported version number ({0})",
70 Vals: static_cast<unsigned>(Preamble->Version.value())));
71
72 Expected<const sframe::Header<E> &> Header =
73 getDataSliceAs<sframe::Header<E>>(Contents, 0);
74 if (!Header)
75 return Header.takeError();
76 return SFrameParser(Contents, SectionAddress, *Header);
77}
78
79template <endianness E>
80Expected<ArrayRef<uint8_t>> SFrameParser<E>::getAuxHeader() const {
81 return getDataSlice(Data, sizeof(Header), Header.AuxHdrLen);
82}
83
84template <endianness E>
85Expected<ArrayRef<sframe::FuncDescEntry<E>>> SFrameParser<E>::fdes() const {
86 Expected<ArrayRef<uint8_t>> Slice = getDataSlice(
87 Data, getFDEBase(), Header.NumFDEs * sizeof(sframe::FuncDescEntry<E>));
88 if (!Slice)
89 return Slice.takeError();
90 return ArrayRef(
91 reinterpret_cast<const sframe::FuncDescEntry<E> *>(Slice->data()),
92 Header.NumFDEs);
93}
94
95template <endianness E>
96uint64_t SFrameParser<E>::getAbsoluteStartAddress(
97 typename FDERange::iterator FDE) const {
98 uint64_t Result = SectionAddress + FDE->StartAddress;
99
100 if ((getPreamble().Flags.value() & sframe::Flags::FDEFuncStartPCRel) ==
101 sframe::Flags::FDEFuncStartPCRel)
102 Result += offsetOf(FDE);
103
104 return Result;
105}
106
107template <endianness E>
108uint64_t SFrameParser<E>::offsetOf(typename FDERange::iterator FDE) const {
109 uintptr_t DataPtr = reinterpret_cast<uintptr_t>(Data.data());
110 uintptr_t FDEPtr = reinterpret_cast<uintptr_t>(&*FDE);
111
112 assert(DataPtr <= FDEPtr && FDEPtr < DataPtr + Data.size() &&
113 "Iterator does not belong to this object!");
114 return FDEPtr - DataPtr;
115}
116
117template <typename EndianT>
118static Error readArray(ArrayRef<uint8_t> Data, uint64_t Count, uint64_t &Offset,
119 SmallVectorImpl<int32_t> &Vec) {
120 Expected<ArrayRef<EndianT>> RawArray =
121 getDataSliceAsArrayOf<EndianT>(Data, Offset, Count);
122 if (!RawArray)
123 return RawArray.takeError();
124 Offset += Count * sizeof(EndianT);
125 Vec.resize(N: Count);
126 llvm::copy(*RawArray, Vec.begin());
127 return Error::success();
128}
129
130template <typename T, endianness E>
131static Error readFRE(ArrayRef<uint8_t> Data, uint64_t &Offset,
132 typename SFrameParser<E>::FrameRowEntry &FRE) {
133 Expected<sframe::FrameRowEntry<T, E>> RawFRE =
134 getDataSliceAs<sframe::FrameRowEntry<T, E>>(Data, Offset);
135 if (!RawFRE)
136 return RawFRE.takeError();
137
138 Offset += sizeof(*RawFRE);
139 FRE.StartAddress = RawFRE->StartAddress;
140 FRE.Info.Info = RawFRE->Info.Info;
141
142 switch (FRE.Info.getOffsetSize()) {
143 case sframe::FREOffset::B1:
144 return readArray<sframe::detail::packed<int8_t, E>>(
145 Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets);
146 case sframe::FREOffset::B2:
147 return readArray<sframe::detail::packed<int16_t, E>>(
148 Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets);
149 case sframe::FREOffset::B4:
150 return readArray<sframe::detail::packed<int32_t, E>>(
151 Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets);
152 }
153 return createError(Err: formatv(Fmt: "unsupported FRE offset size {0} at offset {1:x+}",
154 Vals: static_cast<unsigned>(FRE.Info.getOffsetSize()),
155 Vals&: Offset));
156}
157
158template <endianness E> Error SFrameParser<E>::FallibleFREIterator::inc() {
159 if (++Idx == Size)
160 return Error::success();
161
162 switch (FREType) {
163 case sframe::FREType::Addr1:
164 return readFRE<uint8_t, E>(Data, Offset, FRE);
165 case sframe::FREType::Addr2:
166 return readFRE<uint16_t, E>(Data, Offset, FRE);
167 case sframe::FREType::Addr4:
168 return readFRE<uint32_t, E>(Data, Offset, FRE);
169 }
170 return createError(Err: formatv(Fmt: "unsupported FRE type {0} at offset {1:x+}",
171 Vals: static_cast<unsigned>(FREType), Vals&: Offset));
172}
173
174template <endianness E>
175iterator_range<typename SFrameParser<E>::fre_iterator>
176SFrameParser<E>::fres(const sframe::FuncDescEntry<E> &FDE, Error &Err) const {
177 uint64_t Offset = getFREBase() + FDE.StartFREOff;
178 fre_iterator BeforeBegin = make_fallible_itr(
179 FallibleFREIterator(Data, FDE.Info.getFREType(), -1, FDE.NumFREs, Offset),
180 Err);
181 fre_iterator End = make_fallible_end(
182 FallibleFREIterator(Data, FDE.Info.getFREType(), FDE.NumFREs, FDE.NumFREs,
183 /*Offset=*/0));
184 return {++BeforeBegin, End};
185}
186
187static std::optional<int32_t> getOffset(ArrayRef<int32_t> Offsets, size_t Idx) {
188 if (Offsets.size() > Idx)
189 return Offsets[Idx];
190 return std::nullopt;
191}
192
193// The interpretation of offsets is ABI-specific. The implementation of this and
194// the following functions may need to be adjusted when adding support for a new
195// ABI.
196template <endianness E>
197std::optional<int32_t>
198SFrameParser<E>::getCFAOffset(const FrameRowEntry &FRE) const {
199 return getOffset(FRE.Offsets, 0);
200}
201
202template <endianness E>
203std::optional<int32_t>
204SFrameParser<E>::getRAOffset(const FrameRowEntry &FRE) const {
205 if (usesFixedRAOffset())
206 return Header.CFAFixedRAOffset;
207 return getOffset(FRE.Offsets, 1);
208}
209
210template <endianness E>
211std::optional<int32_t>
212SFrameParser<E>::getFPOffset(const FrameRowEntry &FRE) const {
213 if (usesFixedFPOffset())
214 return Header.CFAFixedFPOffset;
215 return getOffset(FRE.Offsets, usesFixedRAOffset() ? 1 : 2);
216}
217
218template <endianness E>
219ArrayRef<int32_t>
220SFrameParser<E>::getExtraOffsets(const FrameRowEntry &FRE) const {
221 size_t UsedOffsets = 1; // CFA
222 if (!usesFixedRAOffset())
223 ++UsedOffsets;
224 if (!usesFixedFPOffset())
225 ++UsedOffsets;
226 if (FRE.Offsets.size() > UsedOffsets)
227 return ArrayRef<int32_t>(FRE.Offsets).drop_front(N: UsedOffsets);
228 return {};
229}
230
231template class LLVM_EXPORT_TEMPLATE llvm::object::SFrameParser<endianness::big>;
232template class LLVM_EXPORT_TEMPLATE
233 llvm::object::SFrameParser<endianness::little>;
234