xref: /llvm-project/llvm/lib/Support/DataExtractor.cpp (revision a16fffa3f6add51fe1c6ee975ace56aa06a3bea7)
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 *offset_ptr) const {
99   uint24_t ExtractedVal =
100       getU<uint24_t>(offset_ptr, this, IsLittleEndian, Data.data(), nullptr);
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) const {
178   if (!isValidOffsetForDataOfSize(*OffsetPtr, Length))
179     return StringRef();
180   StringRef Result = Data.substr(*OffsetPtr, Length);
181   *OffsetPtr += Length;
182   return Result;
183 }
184 
185 uint64_t DataExtractor::getULEB128(uint64_t *offset_ptr,
186                                    llvm::Error *Err) const {
187   assert(*offset_ptr <= Data.size());
188   ErrorAsOutParameter ErrAsOut(Err);
189   if (isError(Err))
190     return 0;
191 
192   const char *error;
193   unsigned bytes_read;
194   uint64_t result = decodeULEB128(
195       reinterpret_cast<const uint8_t *>(Data.data() + *offset_ptr), &bytes_read,
196       reinterpret_cast<const uint8_t *>(Data.data() + Data.size()), &error);
197   if (error) {
198     if (Err)
199       *Err = createStringError(errc::illegal_byte_sequence, error);
200     return 0;
201   }
202   *offset_ptr += bytes_read;
203   return result;
204 }
205 
206 int64_t DataExtractor::getSLEB128(uint64_t *offset_ptr) const {
207   assert(*offset_ptr <= Data.size());
208 
209   const char *error;
210   unsigned bytes_read;
211   int64_t result = decodeSLEB128(
212       reinterpret_cast<const uint8_t *>(Data.data() + *offset_ptr), &bytes_read,
213       reinterpret_cast<const uint8_t *>(Data.data() + Data.size()), &error);
214   if (error)
215     return 0;
216   *offset_ptr += bytes_read;
217   return result;
218 }
219 
220 void DataExtractor::skip(Cursor &C, uint64_t Length) const {
221   ErrorAsOutParameter ErrAsOut(&C.Err);
222   if (isError(&C.Err))
223     return;
224 
225   if (isValidOffsetForDataOfSize(C.Offset, Length))
226     C.Offset += Length;
227   else
228     unexpectedEndReached(&C.Err, C.Offset);
229 }
230