xref: /llvm-project/llvm/lib/Support/DataExtractor.cpp (revision 8d9070e040d0aa916b3b63c319eabdf3e4a5f9df)
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/Host.h"
13 #include "llvm/Support/LEB128.h"
14 #include "llvm/Support/SwapByteOrder.h"
15 
16 using namespace llvm;
17 
18 static void unexpectedEndReached(Error *E, uint64_t Offset) {
19   if (E)
20     *E = createStringError(errc::illegal_byte_sequence,
21                            "unexpected end of data at offset 0x%" PRIx64,
22                            Offset);
23 }
24 
25 static bool isError(Error *E) { return E && *E; }
26 
27 template <typename T>
28 static T getU(uint64_t *offset_ptr, const DataExtractor *de,
29               bool isLittleEndian, const char *Data, llvm::Error *Err) {
30   ErrorAsOutParameter ErrAsOut(Err);
31   T val = 0;
32   if (isError(Err))
33     return val;
34 
35   uint64_t offset = *offset_ptr;
36   if (!de->isValidOffsetForDataOfSize(offset, sizeof(T))) {
37     unexpectedEndReached(Err, offset);
38     return val;
39   }
40   std::memcpy(&val, &Data[offset], sizeof(val));
41   if (sys::IsLittleEndianHost != isLittleEndian)
42     sys::swapByteOrder(val);
43 
44   // Advance the offset
45   *offset_ptr += sizeof(val);
46   return val;
47 }
48 
49 template <typename T>
50 static T *getUs(uint64_t *offset_ptr, T *dst, uint32_t count,
51                 const DataExtractor *de, bool isLittleEndian, const char *Data,
52                 llvm::Error *Err) {
53   ErrorAsOutParameter ErrAsOut(Err);
54   if (isError(Err))
55     return nullptr;
56 
57   uint64_t offset = *offset_ptr;
58 
59   if (!de->isValidOffsetForDataOfSize(offset, sizeof(*dst) * count)) {
60     unexpectedEndReached(Err, offset);
61     return nullptr;
62   }
63   for (T *value_ptr = dst, *end = dst + count; value_ptr != end;
64        ++value_ptr, offset += sizeof(*dst))
65     *value_ptr = getU<T>(offset_ptr, de, isLittleEndian, Data, Err);
66   // Advance the offset
67   *offset_ptr = offset;
68   // Return a non-NULL pointer to the converted data as an indicator of
69   // success
70   return dst;
71 }
72 
73 uint8_t DataExtractor::getU8(uint64_t *offset_ptr, llvm::Error *Err) const {
74   return getU<uint8_t>(offset_ptr, this, IsLittleEndian, Data.data(), Err);
75 }
76 
77 uint8_t *
78 DataExtractor::getU8(uint64_t *offset_ptr, uint8_t *dst, uint32_t count) const {
79   return getUs<uint8_t>(offset_ptr, dst, count, this, IsLittleEndian,
80                         Data.data(), nullptr);
81 }
82 
83 uint8_t *DataExtractor::getU8(Cursor &C, uint8_t *Dst, uint32_t Count) const {
84   return getUs<uint8_t>(&C.Offset, Dst, Count, this, IsLittleEndian,
85                         Data.data(), &C.Err);
86 }
87 
88 uint16_t DataExtractor::getU16(uint64_t *offset_ptr, llvm::Error *Err) const {
89   return getU<uint16_t>(offset_ptr, this, IsLittleEndian, Data.data(), Err);
90 }
91 
92 uint16_t *DataExtractor::getU16(uint64_t *offset_ptr, uint16_t *dst,
93                                 uint32_t count) const {
94   return getUs<uint16_t>(offset_ptr, dst, count, this, IsLittleEndian,
95                          Data.data(), nullptr);
96 }
97 
98 uint32_t DataExtractor::getU24(uint64_t *OffsetPtr, Error *Err) const {
99   uint24_t ExtractedVal =
100       getU<uint24_t>(OffsetPtr, this, IsLittleEndian, Data.data(), Err);
101   // The 3 bytes are in the correct byte order for the host.
102   return ExtractedVal.getAsUint32(sys::IsLittleEndianHost);
103 }
104 
105 uint32_t DataExtractor::getU32(uint64_t *offset_ptr, llvm::Error *Err) const {
106   return getU<uint32_t>(offset_ptr, this, IsLittleEndian, Data.data(), Err);
107 }
108 
109 uint32_t *DataExtractor::getU32(uint64_t *offset_ptr, uint32_t *dst,
110                                 uint32_t count) const {
111   return getUs<uint32_t>(offset_ptr, dst, count, this, IsLittleEndian,
112                          Data.data(), nullptr);
113 }
114 
115 uint64_t DataExtractor::getU64(uint64_t *offset_ptr, llvm::Error *Err) const {
116   return getU<uint64_t>(offset_ptr, this, IsLittleEndian, Data.data(), Err);
117 }
118 
119 uint64_t *DataExtractor::getU64(uint64_t *offset_ptr, uint64_t *dst,
120                                 uint32_t count) const {
121   return getUs<uint64_t>(offset_ptr, dst, count, this, IsLittleEndian,
122                          Data.data(), 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   unexpectedEndReached(Err, Start);
167   return StringRef();
168 }
169 
170 StringRef DataExtractor::getFixedLengthString(uint64_t *OffsetPtr,
171                                               uint64_t Length,
172                                               StringRef TrimChars) const {
173   StringRef Bytes(getBytes(OffsetPtr, Length));
174   return Bytes.trim(TrimChars);
175 }
176 
177 StringRef DataExtractor::getBytes(uint64_t *OffsetPtr, uint64_t Length,
178                                   Error *Err) const {
179   ErrorAsOutParameter ErrAsOut(Err);
180   if (isError(Err))
181     return StringRef();
182 
183   if (!isValidOffsetForDataOfSize(*OffsetPtr, Length)) {
184     unexpectedEndReached(Err, *OffsetPtr);
185     return StringRef();
186   }
187 
188   StringRef Result = Data.substr(*OffsetPtr, Length);
189   *OffsetPtr += Length;
190   return Result;
191 }
192 
193 template <typename T>
194 static T getLEB128(StringRef Data, uint64_t *OffsetPtr, Error *Err,
195                    T (&Decoder)(const uint8_t *p, unsigned *n,
196                                 const uint8_t *end, const char **error)) {
197   ArrayRef<uint8_t> Bytes = arrayRefFromStringRef(Data);
198   assert(*OffsetPtr <= Bytes.size());
199   ErrorAsOutParameter ErrAsOut(Err);
200   if (isError(Err))
201     return T();
202 
203   const char *error;
204   unsigned bytes_read;
205   T result =
206       Decoder(Bytes.data() + *OffsetPtr, &bytes_read, Bytes.end(), &error);
207   if (error) {
208     if (Err)
209       *Err = createStringError(errc::illegal_byte_sequence,
210                                "unable to decode LEB128 at offset 0x%8.8" PRIx64
211                                ": %s",
212                                *OffsetPtr, error);
213     return T();
214   }
215   *OffsetPtr += bytes_read;
216   return result;
217 }
218 
219 uint64_t DataExtractor::getULEB128(uint64_t *offset_ptr, Error *Err) const {
220   return getLEB128(Data, offset_ptr, Err, decodeULEB128);
221 }
222 
223 int64_t DataExtractor::getSLEB128(uint64_t *offset_ptr, Error *Err) const {
224   return getLEB128(Data, offset_ptr, Err, decodeSLEB128);
225 }
226 
227 void DataExtractor::skip(Cursor &C, uint64_t Length) const {
228   ErrorAsOutParameter ErrAsOut(&C.Err);
229   if (isError(&C.Err))
230     return;
231 
232   if (isValidOffsetForDataOfSize(C.Offset, Length))
233     C.Offset += Length;
234   else
235     unexpectedEndReached(&C.Err, C.Offset);
236 }
237