Skip to content

Commit b1f29ce

Browse files
committed
Add error handling to the DataExtractor class
Summary: This is motivated by D63591, where we realized that there isn't a really good way of telling whether a DataExtractor is reading actual data, or is it just returning default values because it reached the end of the buffer. This patch resolves that by providing a new "Cursor" class. A Cursor object encapsulates two things: - the current position/offset in the DataExtractor - an error object Storing the error object inside the Cursor enables one to use the same pattern as the std::{io}stream API, where one can blindly perform a sequence of reads and only check for errors once at the end of the operation. Similarly to the stream API, as soon as we encounter one error, all of the subsequent operations are skipped (return default values) too, even if the would suceed with clear error state. Unlike the std::stream API (but in line with other llvm APIs), we force the error state to be checked through usage of llvm::Error. Reviewers: probinson, dblaikie, JDevlieghere, aprantl, echristo Subscribers: kristina, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D63713 llvm-svn: 370042
1 parent 2535f04 commit b1f29ce

File tree

5 files changed

+388
-51
lines changed

5 files changed

+388
-51
lines changed

Diff for: llvm/include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,18 @@ class DWARFDataExtractor : public DataExtractor {
3636
/// Extracts a value and applies a relocation to the result if
3737
/// one exists for the given offset.
3838
uint64_t getRelocatedValue(uint32_t Size, uint64_t *Off,
39-
uint64_t *SectionIndex = nullptr) const;
39+
uint64_t *SectionIndex = nullptr,
40+
Error *Err = nullptr) const;
4041

4142
/// Extracts an address-sized value and applies a relocation to the result if
4243
/// one exists for the given offset.
4344
uint64_t getRelocatedAddress(uint64_t *Off, uint64_t *SecIx = nullptr) const {
4445
return getRelocatedValue(getAddressSize(), Off, SecIx);
4546
}
47+
uint64_t getRelocatedAddress(Cursor &C, uint64_t *SecIx = nullptr) const {
48+
return getRelocatedValue(getAddressSize(), &getOffset(C), SecIx,
49+
&getError(C));
50+
}
4651

4752
/// Extracts a DWARF-encoded pointer in \p Offset using \p Encoding.
4853
/// There is a DWARF encoding that uses a PC-relative adjustment.

Diff for: llvm/include/llvm/Support/DataExtractor.h

+149-6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "llvm/ADT/StringRef.h"
1313
#include "llvm/Support/DataTypes.h"
14+
#include "llvm/Support/Error.h"
1415

1516
namespace llvm {
1617

@@ -42,6 +43,38 @@ class DataExtractor {
4243
uint8_t IsLittleEndian;
4344
uint8_t AddressSize;
4445
public:
46+
/// A class representing a position in a DataExtractor, as well as any error
47+
/// encountered during extraction. It enables one to extract a sequence of
48+
/// values without error-checking and then checking for errors in bulk at the
49+
/// end. The class holds an Error object, so failing to check the result of
50+
/// the parse will result in a runtime error. The error flag is sticky and
51+
/// will cause all subsequent extraction functions to fail without even
52+
/// attempting to parse and without updating the Cursor offset. After clearing
53+
/// the error flag, one can again use the Cursor object for parsing.
54+
class Cursor {
55+
uint64_t Offset;
56+
Error Err;
57+
58+
friend class DataExtractor;
59+
60+
public:
61+
/// Construct a cursor for extraction from the given offset.
62+
explicit Cursor(uint64_t Offset) : Offset(Offset), Err(Error::success()) {}
63+
64+
/// Checks whether the cursor is valid (i.e. no errors were encountered). In
65+
/// case of errors, this does not clear the error flag -- one must call
66+
/// takeError() instead.
67+
explicit operator bool() { return !Err; }
68+
69+
/// Return the current position of this Cursor. In the error state this is
70+
/// the position of the Cursor before the first error was encountered.
71+
uint64_t tell() const { return Offset; }
72+
73+
/// Return error contained inside this Cursor, if any. Clears the internal
74+
/// Cursor state.
75+
Error takeError() { return std::move(Err); }
76+
};
77+
4578
/// Construct with a buffer that is owned by the caller.
4679
///
4780
/// This constructor allows us to use data that is owned by the
@@ -124,10 +157,24 @@ class DataExtractor {
124157
/// @param[in] byte_size
125158
/// The size in byte of the integer to extract.
126159
///
160+
/// @param[in,out] Err
161+
/// A pointer to an Error object. Upon return the Error object is set to
162+
/// indicate the result (success/failure) of the function. If the Error
163+
/// object is already set when calling this function, no extraction is
164+
/// performed.
165+
///
127166
/// @return
128167
/// The unsigned integer value that was extracted, or zero on
129168
/// failure.
130-
uint64_t getUnsigned(uint64_t *offset_ptr, uint32_t byte_size) const;
169+
uint64_t getUnsigned(uint64_t *offset_ptr, uint32_t byte_size,
170+
Error *Err = nullptr) const;
171+
172+
/// Extract an unsigned integer of the given size from the location given by
173+
/// the cursor. In case of an extraction error, or if the cursor is already in
174+
/// an error state, zero is returned.
175+
uint64_t getUnsigned(Cursor &C, uint32_t Size) const {
176+
return getUnsigned(&C.Offset, Size, &C.Err);
177+
}
131178

132179
/// Extract an signed integer of size \a byte_size from \a *offset_ptr.
133180
///
@@ -175,6 +222,11 @@ class DataExtractor {
175222
return getUnsigned(offset_ptr, AddressSize);
176223
}
177224

225+
/// Extract a pointer-sized unsigned integer from the location given by the
226+
/// cursor. In case of an extraction error, or if the cursor is already in
227+
/// an error state, zero is returned.
228+
uint64_t getAddress(Cursor &C) const { return getUnsigned(C, AddressSize); }
229+
178230
/// Extract a uint8_t value from \a *offset_ptr.
179231
///
180232
/// Extract a single uint8_t from the binary data at the offset
@@ -187,9 +239,20 @@ class DataExtractor {
187239
/// enough bytes to extract this value, the offset will be left
188240
/// unmodified.
189241
///
242+
/// @param[in,out] Err
243+
/// A pointer to an Error object. Upon return the Error object is set to
244+
/// indicate the result (success/failure) of the function. If the Error
245+
/// object is already set when calling this function, no extraction is
246+
/// performed.
247+
///
190248
/// @return
191249
/// The extracted uint8_t value.
192-
uint8_t getU8(uint64_t *offset_ptr) const;
250+
uint8_t getU8(uint64_t *offset_ptr, Error *Err = nullptr) const;
251+
252+
/// Extract a single uint8_t value from the location given by the cursor. In
253+
/// case of an extraction error, or if the cursor is already in an error
254+
/// state, zero is returned.
255+
uint8_t getU8(Cursor &C) const { return getU8(&C.Offset, &C.Err); }
193256

194257
/// Extract \a count uint8_t values from \a *offset_ptr.
195258
///
@@ -216,6 +279,26 @@ class DataExtractor {
216279
/// NULL otherise.
217280
uint8_t *getU8(uint64_t *offset_ptr, uint8_t *dst, uint32_t count) const;
218281

282+
/// Extract \a Count uint8_t values from the location given by the cursor and
283+
/// store them into the destination buffer. In case of an extraction error, or
284+
/// if the cursor is already in an error state, a nullptr is returned and the
285+
/// destination buffer is left unchanged.
286+
uint8_t *getU8(Cursor &C, uint8_t *Dst, uint32_t Count) const;
287+
288+
/// Extract \a Count uint8_t values from the location given by the cursor and
289+
/// store them into the destination vector. The vector is resized to fit the
290+
/// extracted data. In case of an extraction error, or if the cursor is
291+
/// already in an error state, the destination vector is left unchanged and
292+
/// cursor is placed into an error state.
293+
void getU8(Cursor &C, SmallVectorImpl<uint8_t> &Dst, uint32_t Count) const {
294+
if (isValidOffsetForDataOfSize(C.Offset, Count))
295+
Dst.resize(Count);
296+
297+
// This relies on the fact that getU8 will not attempt to write to the
298+
// buffer if isValidOffsetForDataOfSize(C.Offset, Count) is false.
299+
getU8(C, Dst.data(), Count);
300+
}
301+
219302
//------------------------------------------------------------------
220303
/// Extract a uint16_t value from \a *offset_ptr.
221304
///
@@ -229,10 +312,21 @@ class DataExtractor {
229312
/// enough bytes to extract this value, the offset will be left
230313
/// unmodified.
231314
///
315+
/// @param[in,out] Err
316+
/// A pointer to an Error object. Upon return the Error object is set to
317+
/// indicate the result (success/failure) of the function. If the Error
318+
/// object is already set when calling this function, no extraction is
319+
/// performed.
320+
///
232321
/// @return
233322
/// The extracted uint16_t value.
234323
//------------------------------------------------------------------
235-
uint16_t getU16(uint64_t *offset_ptr) const;
324+
uint16_t getU16(uint64_t *offset_ptr, Error *Err = nullptr) const;
325+
326+
/// Extract a single uint16_t value from the location given by the cursor. In
327+
/// case of an extraction error, or if the cursor is already in an error
328+
/// state, zero is returned.
329+
uint16_t getU16(Cursor &C) const { return getU16(&C.Offset, &C.Err); }
236330

237331
/// Extract \a count uint16_t values from \a *offset_ptr.
238332
///
@@ -288,9 +382,20 @@ class DataExtractor {
288382
/// enough bytes to extract this value, the offset will be left
289383
/// unmodified.
290384
///
385+
/// @param[in,out] Err
386+
/// A pointer to an Error object. Upon return the Error object is set to
387+
/// indicate the result (success/failure) of the function. If the Error
388+
/// object is already set when calling this function, no extraction is
389+
/// performed.
390+
///
291391
/// @return
292392
/// The extracted uint32_t value.
293-
uint32_t getU32(uint64_t *offset_ptr) const;
393+
uint32_t getU32(uint64_t *offset_ptr, Error *Err = nullptr) const;
394+
395+
/// Extract a single uint32_t value from the location given by the cursor. In
396+
/// case of an extraction error, or if the cursor is already in an error
397+
/// state, zero is returned.
398+
uint32_t getU32(Cursor &C) const { return getU32(&C.Offset, &C.Err); }
294399

295400
/// Extract \a count uint32_t values from \a *offset_ptr.
296401
///
@@ -329,9 +434,20 @@ class DataExtractor {
329434
/// enough bytes to extract this value, the offset will be left
330435
/// unmodified.
331436
///
437+
/// @param[in,out] Err
438+
/// A pointer to an Error object. Upon return the Error object is set to
439+
/// indicate the result (success/failure) of the function. If the Error
440+
/// object is already set when calling this function, no extraction is
441+
/// performed.
442+
///
332443
/// @return
333444
/// The extracted uint64_t value.
334-
uint64_t getU64(uint64_t *offset_ptr) const;
445+
uint64_t getU64(uint64_t *offset_ptr, Error *Err = nullptr) const;
446+
447+
/// Extract a single uint64_t value from the location given by the cursor. In
448+
/// case of an extraction error, or if the cursor is already in an error
449+
/// state, zero is returned.
450+
uint64_t getU64(Cursor &C) const { return getU64(&C.Offset, &C.Err); }
335451

336452
/// Extract \a count uint64_t values from \a *offset_ptr.
337453
///
@@ -390,9 +506,30 @@ class DataExtractor {
390506
/// enough bytes to extract this value, the offset will be left
391507
/// unmodified.
392508
///
509+
/// @param[in,out] Err
510+
/// A pointer to an Error object. Upon return the Error object is set to
511+
/// indicate the result (success/failure) of the function. If the Error
512+
/// object is already set when calling this function, no extraction is
513+
/// performed.
514+
///
393515
/// @return
394516
/// The extracted unsigned integer value.
395-
uint64_t getULEB128(uint64_t *offset_ptr) const;
517+
uint64_t getULEB128(uint64_t *offset_ptr, llvm::Error *Err = nullptr) const;
518+
519+
/// Extract an unsigned ULEB128 value from the location given by the cursor.
520+
/// In case of an extraction error, or if the cursor is already in an error
521+
/// state, zero is returned.
522+
uint64_t getULEB128(Cursor &C) const { return getULEB128(&C.Offset, &C.Err); }
523+
524+
/// Advance the Cursor position by the given number of bytes. No-op if the
525+
/// cursor is in an error state.
526+
void skip(Cursor &C, uint64_t Length) const;
527+
528+
/// Return true iff the cursor is at the end of the buffer, regardless of the
529+
/// error state of the cursor. The only way both eof and error states can be
530+
/// true is if one attempts a read while the cursor is at the very end of the
531+
/// data buffer.
532+
bool eof(const Cursor &C) const { return Data.size() == C.Offset; }
396533

397534
/// Test the validity of \a offset.
398535
///
@@ -420,6 +557,12 @@ class DataExtractor {
420557
bool isValidOffsetForAddress(uint64_t offset) const {
421558
return isValidOffsetForDataOfSize(offset, AddressSize);
422559
}
560+
561+
protected:
562+
// Make it possible for subclasses to access these fields without making them
563+
// public.
564+
static uint64_t &getOffset(Cursor &C) { return C.Offset; }
565+
static Error &getError(Cursor &C) { return C.Err; }
423566
};
424567

425568
} // namespace llvm

Diff for: llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@
1313
using namespace llvm;
1414

1515
uint64_t DWARFDataExtractor::getRelocatedValue(uint32_t Size, uint64_t *Off,
16-
uint64_t *SecNdx) const {
16+
uint64_t *SecNdx,
17+
Error *Err) const {
1718
if (SecNdx)
1819
*SecNdx = object::SectionedAddress::UndefSection;
1920
if (!Section)
20-
return getUnsigned(Off, Size);
21+
return getUnsigned(Off, Size, Err);
2122
Optional<RelocAddrEntry> E = Obj->find(*Section, *Off);
22-
uint64_t A = getUnsigned(Off, Size);
23+
uint64_t A = getUnsigned(Off, Size, Err);
2324
if (!E)
2425
return A;
2526
if (SecNdx)

0 commit comments

Comments
 (0)