From 3d0200eec2812b1411744eb46899a3f8d8519822 Mon Sep 17 00:00:00 2001 From: Lang Hames Date: Wed, 17 Apr 2019 15:38:27 +0000 Subject: [PATCH] [Support] Add LEB128 support to BinaryStreamReader/Writer. Summary: This patch adds support for ULEB128 and SLEB128 encoding and decoding to BinaryStreamWriter and BinaryStreamReader respectively. Support for ULEB128/SLEB128 will be used for eh-frame parsing in the JITLink library currently under development (see https://reviews.llvm.org/D58704). Reviewers: zturner, dblaikie Subscribers: kristina, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D60810 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@358584 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Support/BinaryStreamReader.h | 12 ++++ include/llvm/Support/BinaryStreamWriter.h | 14 +++++ lib/Support/BinaryStreamReader.cpp | 33 ++++++++++- lib/Support/BinaryStreamWriter.cpp | 13 +++++ unittests/Support/BinaryStreamTest.cpp | 71 +++++++++++++++++++++++ 5 files changed, 142 insertions(+), 1 deletion(-) diff --git a/include/llvm/Support/BinaryStreamReader.h b/include/llvm/Support/BinaryStreamReader.h index 32e57284482..d8fddde66bf 100644 --- a/include/llvm/Support/BinaryStreamReader.h +++ b/include/llvm/Support/BinaryStreamReader.h @@ -96,6 +96,18 @@ public: return Error::success(); } + /// Read an unsigned LEB128 encoded value. + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + Error readULEB128(uint64_t &Dest); + + /// Read a signed LEB128 encoded value. + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + Error readSLEB128(int64_t &Dest); + /// Read a null terminated string from \p Dest. Whether a copy occurs depends /// on the implementation of the underlying stream. Updates the stream's /// offset to point after the newly read data. diff --git a/include/llvm/Support/BinaryStreamWriter.h b/include/llvm/Support/BinaryStreamWriter.h index ae019c81e15..86d2389d918 100644 --- a/include/llvm/Support/BinaryStreamWriter.h +++ b/include/llvm/Support/BinaryStreamWriter.h @@ -79,6 +79,20 @@ public: return writeInteger(static_cast(Num)); } + /// Write the unsigned integer Value to the underlying stream using ULEB128 + /// encoding. + /// + /// \returns a success error code if the data was successfully written, + /// otherwise returns an appropriate error code. + Error writeULEB128(uint64_t Value); + + /// Write the unsigned integer Value to the underlying stream using ULEB128 + /// encoding. + /// + /// \returns a success error code if the data was successfully written, + /// otherwise returns an appropriate error code. + Error writeSLEB128(int64_t Value); + /// Write the string \p Str to the underlying stream followed by a null /// terminator. On success, updates the offset so that subsequent writes /// occur at the next unwritten position. \p Str need not be null terminated diff --git a/lib/Support/BinaryStreamReader.cpp b/lib/Support/BinaryStreamReader.cpp index 6f691bf4dcd..b17786593bd 100644 --- a/lib/Support/BinaryStreamReader.cpp +++ b/lib/Support/BinaryStreamReader.cpp @@ -10,6 +10,7 @@ #include "llvm/Support/BinaryStreamError.h" #include "llvm/Support/BinaryStreamRef.h" +#include "llvm/Support/LEB128.h" using namespace llvm; using endianness = llvm::support::endianness; @@ -40,6 +41,36 @@ Error BinaryStreamReader::readBytes(ArrayRef &Buffer, uint32_t Size) { return Error::success(); } +Error BinaryStreamReader::readULEB128(uint64_t &Dest) { + SmallVector EncodedBytes; + ArrayRef NextByte; + + // Copy the encoded ULEB into the buffer. + do { + if (auto Err = readBytes(NextByte, 1)) + return Err; + EncodedBytes.push_back(NextByte[0]); + } while (NextByte[0] & 0x80); + + Dest = decodeULEB128(EncodedBytes.begin(), nullptr, EncodedBytes.end()); + return Error::success(); +} + +Error BinaryStreamReader::readSLEB128(int64_t &Dest) { + SmallVector EncodedBytes; + ArrayRef NextByte; + + // Copy the encoded ULEB into the buffer. + do { + if (auto Err = readBytes(NextByte, 1)) + return Err; + EncodedBytes.push_back(NextByte[0]); + } while (NextByte[0] & 0x80); + + Dest = decodeSLEB128(EncodedBytes.begin(), nullptr, EncodedBytes.end()); + return Error::success(); +} + Error BinaryStreamReader::readCString(StringRef &Dest) { uint32_t OriginalOffset = getOffset(); uint32_t FoundOffset = 0; @@ -145,4 +176,4 @@ BinaryStreamReader::split(uint32_t Off) const { BinaryStreamReader W1{First}; BinaryStreamReader W2{Second}; return std::make_pair(W1, W2); -} \ No newline at end of file +} diff --git a/lib/Support/BinaryStreamWriter.cpp b/lib/Support/BinaryStreamWriter.cpp index 929968bf2e4..986e18da281 100644 --- a/lib/Support/BinaryStreamWriter.cpp +++ b/lib/Support/BinaryStreamWriter.cpp @@ -11,6 +11,7 @@ #include "llvm/Support/BinaryStreamError.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/BinaryStreamRef.h" +#include "llvm/Support/LEB128.h" using namespace llvm; @@ -31,6 +32,18 @@ Error BinaryStreamWriter::writeBytes(ArrayRef Buffer) { return Error::success(); } +Error BinaryStreamWriter::writeULEB128(uint64_t Value) { + uint8_t EncodedBytes[10] = {0}; + unsigned Size = encodeULEB128(Value, &EncodedBytes[0]); + return writeBytes({EncodedBytes, Size}); +} + +Error BinaryStreamWriter::writeSLEB128(int64_t Value) { + uint8_t EncodedBytes[10] = {0}; + unsigned Size = encodeSLEB128(Value, &EncodedBytes[0]); + return writeBytes({EncodedBytes, Size}); +} + Error BinaryStreamWriter::writeCString(StringRef Str) { if (auto EC = writeFixedString(Str)) return EC; diff --git a/unittests/Support/BinaryStreamTest.cpp b/unittests/Support/BinaryStreamTest.cpp index fe574072a11..5291a31013c 100644 --- a/unittests/Support/BinaryStreamTest.cpp +++ b/unittests/Support/BinaryStreamTest.cpp @@ -610,6 +610,77 @@ TEST_F(BinaryStreamTest, StreamReaderEnum) { } } +TEST_F(BinaryStreamTest, StreamReaderULEB128) { + std::vector TestValues = { + 0, // Zero + 0x7F, // One byte + 0xFF, // One byte, all-ones + 0xAAAA, // Two bytes + 0xAAAAAAAA, // Four bytes + 0xAAAAAAAAAAAAAAAA, // Eight bytes + 0xffffffffffffffff // Eight bytess, all-ones + }; + + // Conservatively assume a 10-byte encoding for each of our LEB128s, with no + // alignment requirement. + initializeOutput(10 * TestValues.size(), 1); + initializeInputFromOutput(1); + + for (auto &Stream : Streams) { + // Write fields. + BinaryStreamWriter Writer(*Stream.Output); + for (const auto &Value : TestValues) + ASSERT_THAT_ERROR(Writer.writeULEB128(Value), Succeeded()); + + // Read fields. + BinaryStreamReader Reader(*Stream.Input); + std::vector Results; + Results.resize(TestValues.size()); + for (unsigned I = 0; I != TestValues.size(); ++I) + ASSERT_THAT_ERROR(Reader.readULEB128(Results[I]), Succeeded()); + + for (unsigned I = 0; I != TestValues.size(); ++I) + EXPECT_EQ(TestValues[I], Results[I]); + } +} + +TEST_F(BinaryStreamTest, StreamReaderSLEB128) { + std::vector TestValues = { + 0, // Zero + 0x7F, // One byte + -0x7F, // One byte, negative + 0xFF, // One byte, all-ones + 0xAAAA, // Two bytes + -0xAAAA, // Two bytes, negative + 0xAAAAAAAA, // Four bytes + -0xAAAAAAAA, // Four bytes, negative + 0x2AAAAAAAAAAAAAAA, // Eight bytes + -0x7ffffffffffffff // Eight bytess, negative + }; + + // Conservatively assume a 10-byte encoding for each of our LEB128s, with no + // alignment requirement. + initializeOutput(10 * TestValues.size(), 1); + initializeInputFromOutput(1); + + for (auto &Stream : Streams) { + // Write fields. + BinaryStreamWriter Writer(*Stream.Output); + for (const auto &Value : TestValues) + ASSERT_THAT_ERROR(Writer.writeSLEB128(Value), Succeeded()); + + // Read fields. + BinaryStreamReader Reader(*Stream.Input); + std::vector Results; + Results.resize(TestValues.size()); + for (unsigned I = 0; I != TestValues.size(); ++I) + ASSERT_THAT_ERROR(Reader.readSLEB128(Results[I]), Succeeded()); + + for (unsigned I = 0; I != TestValues.size(); ++I) + EXPECT_EQ(TestValues[I], Results[I]); + } +} + TEST_F(BinaryStreamTest, StreamReaderObject) { struct Foo { int X; -- 2.50.1