1 //===-- DataExtractor.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/Support/DataExtractor.h"
10 #include "llvm/Support/Errc.h"
11 #include "llvm/Support/ErrorHandling.h"
12 #include "llvm/Support/LEB128.h"
13 #include "llvm/Support/SwapByteOrder.h"
14 
15 using namespace llvm;
16 
17 bool DataExtractor::prepareRead(uint64_t Offset, uint64_t Size,
18                                 Error *E) const {
19   if (isValidOffsetForDataOfSize(Offset, Size))
20     return true;
21   if (E) {
22     if (Offset <= Data.size())
23       *E = createStringError(
24           errc::illegal_byte_sequence,
25           "unexpected end of data at offset 0x%zx while reading [0x%" PRIx64
26           ", 0x%" PRIx64 ")",
27           Data.size(), Offset, Offset + Size);
28     else
29       *E = createStringError(errc::invalid_argument,
30                              "offset 0x%" PRIx64
31                              " is beyond the end of data at 0x%zx",
32                              Offset, Data.size());
33   }
34   return false;
35 }
36 
37 static bool isError(Error *E) { return E && *E; }
38 
39 template <typename T>
40 T DataExtractor::getU(uint64_t *offset_ptr, Error *Err) const {
41   ErrorAsOutParameter ErrAsOut(Err);
42   T val = 0;
43   if (isError(Err))
44     return val;
45 
46   uint64_t offset = *offset_ptr;
47   if (!prepareRead(offset, sizeof(T), Err))
48     return val;
49   std::memcpy(&val, &Data.data()[offset], sizeof(val));
50   if (sys::IsLittleEndianHost != IsLittleEndian)
51     sys::swapByteOrder(val);
52 
53   // Advance the offset
54   *offset_ptr += sizeof(val);
55   return val;
56 }
57 
58 template <typename T>
59 T *DataExtractor::getUs(uint64_t *offset_ptr, T *dst, uint32_t count,
60                         Error *Err) const {
61   ErrorAsOutParameter ErrAsOut(Err);
62   if (isError(Err))
63     return nullptr;
64 
65   uint64_t offset = *offset_ptr;
66 
67   if (!prepareRead(offset, sizeof(*dst) * count, Err))
68     return nullptr;
69   for (T *value_ptr = dst, *end = dst + count; value_ptr != end;
70        ++value_ptr, offset += sizeof(*dst))
71     *value_ptr = getU<T>(offset_ptr, Err);
72   // Advance the offset
73   *offset_ptr = offset;
74   // Return a non-NULL pointer to the converted data as an indicator of
75   // success
76   return dst;
77 }
78 
79 uint8_t DataExtractor::getU8(uint64_t *offset_ptr, llvm::Error *Err) const {
80   return getU<uint8_t>(offset_ptr, Err);
81 }
82 
83 uint8_t *DataExtractor::getU8(uint64_t *offset_ptr, uint8_t *dst,
84                               uint32_t count) const {
85   return getUs<uint8_t>(offset_ptr, dst, count, nullptr);
86 }
87 
88 uint8_t *DataExtractor::getU8(Cursor &C, uint8_t *Dst, uint32_t Count) const {
89   return getUs<uint8_t>(&C.Offset, Dst, Count, &C.Err);
90 }
91 
92 uint16_t DataExtractor::getU16(uint64_t *offset_ptr, llvm::Error *Err) const {
93   return getU<uint16_t>(offset_ptr, Err);
94 }
95 
96 uint16_t *DataExtractor::getU16(uint64_t *offset_ptr, uint16_t *dst,
97                                 uint32_t count) const {
98   return getUs<uint16_t>(offset_ptr, dst, count, nullptr);
99 }
100 
101 uint32_t DataExtractor::getU24(uint64_t *OffsetPtr, Error *Err) const {
102   uint24_t ExtractedVal = getU<uint24_t>(OffsetPtr, Err);
103   // The 3 bytes are in the correct byte order for the host.
104   return ExtractedVal.getAsUint32(sys::IsLittleEndianHost);
105 }
106 
107 uint32_t DataExtractor::getU32(uint64_t *offset_ptr, llvm::Error *Err) const {
108   return getU<uint32_t>(offset_ptr, Err);
109 }
110 
111 uint32_t *DataExtractor::getU32(uint64_t *offset_ptr, uint32_t *dst,
112                                 uint32_t count) const {
113   return getUs<uint32_t>(offset_ptr, dst, count, nullptr);
114 }
115 
116 uint64_t DataExtractor::getU64(uint64_t *offset_ptr, llvm::Error *Err) const {
117   return getU<uint64_t>(offset_ptr, Err);
118 }
119 
120 uint64_t *DataExtractor::getU64(uint64_t *offset_ptr, uint64_t *dst,
121                                 uint32_t count) const {
122   return getUs<uint64_t>(offset_ptr, dst, count, nullptr);
123 }
124 
125 uint64_t DataExtractor::getUnsigned(uint64_t *offset_ptr, uint32_t byte_size,
126                                     llvm::Error *Err) const {
127   switch (byte_size) {
128   case 1:
129     return getU8(offset_ptr, Err);
130   case 2:
131     return getU16(offset_ptr, Err);
132   case 4:
133     return getU32(offset_ptr, Err);
134   case 8:
135     return getU64(offset_ptr, Err);
136   }
137   llvm_unreachable("getUnsigned unhandled case!");
138 }
139 
140 int64_t
141 DataExtractor::getSigned(uint64_t *offset_ptr, uint32_t byte_size) const {
142   switch (byte_size) {
143   case 1:
144     return (int8_t)getU8(offset_ptr);
145   case 2:
146     return (int16_t)getU16(offset_ptr);
147   case 4:
148     return (int32_t)getU32(offset_ptr);
149   case 8:
150     return (int64_t)getU64(offset_ptr);
151   }
152   llvm_unreachable("getSigned unhandled case!");
153 }
154 
155 StringRef DataExtractor::getCStrRef(uint64_t *OffsetPtr, Error *Err) const {
156   ErrorAsOutParameter ErrAsOut(Err);
157   if (isError(Err))
158     return StringRef();
159 
160   uint64_t Start = *OffsetPtr;
161   StringRef::size_type Pos = Data.find('\0', Start);
162   if (Pos != StringRef::npos) {
163     *OffsetPtr = Pos + 1;
164     return StringRef(Data.data() + Start, Pos - Start);
165   }
166   if (Err)
167     *Err = createStringError(errc::illegal_byte_sequence,
168                              "no null terminated string at offset 0x%" PRIx64,
169                              Start);
170   return StringRef();
171 }
172 
173 StringRef DataExtractor::getFixedLengthString(uint64_t *OffsetPtr,
174                                               uint64_t Length,
175                                               StringRef TrimChars) const {
176   StringRef Bytes(getBytes(OffsetPtr, Length));
177   return Bytes.trim(TrimChars);
178 }
179 
180 StringRef DataExtractor::getBytes(uint64_t *OffsetPtr, uint64_t Length,
181                                   Error *Err) const {
182   ErrorAsOutParameter ErrAsOut(Err);
183   if (isError(Err))
184     return StringRef();
185 
186   if (!prepareRead(*OffsetPtr, Length, Err))
187     return StringRef();
188 
189   StringRef Result = Data.substr(*OffsetPtr, Length);
190   *OffsetPtr += Length;
191   return Result;
192 }
193 
194 template <typename T>
195 static T getLEB128(StringRef Data, uint64_t *OffsetPtr, Error *Err,
196                    T (&Decoder)(const uint8_t *p, unsigned *n,
197                                 const uint8_t *end, const char **error)) {
198   ArrayRef<uint8_t> Bytes = arrayRefFromStringRef(Data);
199   assert(*OffsetPtr <= Bytes.size());
200   ErrorAsOutParameter ErrAsOut(Err);
201   if (isError(Err))
202     return T();
203 
204   const char *error;
205   unsigned bytes_read;
206   T result =
207       Decoder(Bytes.data() + *OffsetPtr, &bytes_read, Bytes.end(), &error);
208   if (error) {
209     if (Err)
210       *Err = createStringError(errc::illegal_byte_sequence,
211                                "unable to decode LEB128 at offset 0x%8.8" PRIx64
212                                ": %s",
213                                *OffsetPtr, error);
214     return T();
215   }
216   *OffsetPtr += bytes_read;
217   return result;
218 }
219 
220 uint64_t DataExtractor::getULEB128(uint64_t *offset_ptr, Error *Err) const {
221   return getLEB128(Data, offset_ptr, Err, decodeULEB128);
222 }
223 
224 int64_t DataExtractor::getSLEB128(uint64_t *offset_ptr, Error *Err) const {
225   return getLEB128(Data, offset_ptr, Err, decodeSLEB128);
226 }
227 
228 void DataExtractor::skip(Cursor &C, uint64_t Length) const {
229   ErrorAsOutParameter ErrAsOut(&C.Err);
230   if (isError(&C.Err))
231     return;
232 
233   if (prepareRead(C.Offset, Length, &C.Err))
234     C.Offset += Length;
235 }
236