summaryrefslogtreecommitdiffstats
path: root/llvm/lib
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib')
-rw-r--r--llvm/lib/DebugInfo/CodeView/ByteStream.cpp52
-rw-r--r--llvm/lib/DebugInfo/CodeView/CMakeLists.txt1
-rw-r--r--llvm/lib/DebugInfo/CodeView/CodeViewError.cpp2
-rw-r--r--llvm/lib/DebugInfo/CodeView/StreamReader.cpp7
-rw-r--r--llvm/lib/DebugInfo/CodeView/StreamWriter.cpp77
-rw-r--r--llvm/lib/DebugInfo/CodeView/TypeDumper.cpp2
-rw-r--r--llvm/lib/DebugInfo/PDB/Raw/MappedBlockStream.cpp163
-rw-r--r--llvm/lib/DebugInfo/PDB/Raw/PDBFile.cpp11
-rw-r--r--llvm/lib/DebugInfo/PDB/Raw/RawError.cpp4
9 files changed, 302 insertions, 17 deletions
diff --git a/llvm/lib/DebugInfo/CodeView/ByteStream.cpp b/llvm/lib/DebugInfo/CodeView/ByteStream.cpp
index c0ac0b7a8ff..83b2f8ec307 100644
--- a/llvm/lib/DebugInfo/CodeView/ByteStream.cpp
+++ b/llvm/lib/DebugInfo/CodeView/ByteStream.cpp
@@ -15,23 +15,61 @@
using namespace llvm;
using namespace llvm::codeview;
-ByteStream::ByteStream() {}
+static Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Src,
+ ArrayRef<uint8_t> Dest) {
+ return make_error<CodeViewError>(cv_error_code::operation_unsupported,
+ "ByteStream is immutable.");
+}
-ByteStream::ByteStream(ArrayRef<uint8_t> Data) : Data(Data) {}
+static Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Src,
+ MutableArrayRef<uint8_t> Dest) {
+ if (Dest.size() < Src.size())
+ return make_error<CodeViewError>(cv_error_code::insufficient_buffer);
+ if (Offset > Src.size() - Dest.size())
+ return make_error<CodeViewError>(cv_error_code::insufficient_buffer);
-ByteStream::~ByteStream() {}
+ ::memcpy(Dest.data() + Offset, Src.data(), Src.size());
+ return Error::success();
+}
-Error ByteStream::readBytes(uint32_t Offset, uint32_t Size,
- ArrayRef<uint8_t> &Buffer) const {
+template <bool Writable>
+Error ByteStream<Writable>::readBytes(uint32_t Offset, uint32_t Size,
+ ArrayRef<uint8_t> &Buffer) const {
+ if (Offset > Data.size())
+ return make_error<CodeViewError>(cv_error_code::insufficient_buffer);
if (Data.size() < Size + Offset)
return make_error<CodeViewError>(cv_error_code::insufficient_buffer);
Buffer = Data.slice(Offset, Size);
return Error::success();
}
-uint32_t ByteStream::getLength() const { return Data.size(); }
+template <bool Writable>
+Error ByteStream<Writable>::readLongestContiguousChunk(
+ uint32_t Offset, ArrayRef<uint8_t> &Buffer) const {
+ if (Offset >= Data.size())
+ return make_error<CodeViewError>(cv_error_code::insufficient_buffer);
+ Buffer = Data.slice(Offset);
+ return Error::success();
+}
+
+template <bool Writable>
+Error ByteStream<Writable>::writeBytes(uint32_t Offset,
+ ArrayRef<uint8_t> Buffer) const {
+ return ::writeBytes(Offset, Buffer, Data);
+}
+
+template <bool Writable> uint32_t ByteStream<Writable>::getLength() const {
+ return Data.size();
+}
-StringRef ByteStream::str() const {
+template <bool Writable> StringRef ByteStream<Writable>::str() const {
const char *CharData = reinterpret_cast<const char *>(Data.data());
return StringRef(CharData, Data.size());
}
+
+namespace llvm {
+namespace codeview {
+template class ByteStream<true>;
+template class ByteStream<false>;
+}
+}
diff --git a/llvm/lib/DebugInfo/CodeView/CMakeLists.txt b/llvm/lib/DebugInfo/CodeView/CMakeLists.txt
index 6c4e09fe4e3..4393f9c52ee 100644
--- a/llvm/lib/DebugInfo/CodeView/CMakeLists.txt
+++ b/llvm/lib/DebugInfo/CodeView/CMakeLists.txt
@@ -11,6 +11,7 @@ add_llvm_library(LLVMDebugInfoCodeView
ModuleSubstreamVisitor.cpp
RecordSerialization.cpp
StreamReader.cpp
+ StreamWriter.cpp
SymbolDumper.cpp
TypeDumper.cpp
TypeRecord.cpp
diff --git a/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp b/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp
index 4db6b2941a6..aad1d8b25cd 100644
--- a/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp
+++ b/llvm/lib/DebugInfo/CodeView/CodeViewError.cpp
@@ -31,6 +31,8 @@ public:
"bytes.";
case cv_error_code::corrupt_record:
return "The CodeView record is corrupted.";
+ case cv_error_code::operation_unsupported:
+ return "The requested operation is not supported.";
}
llvm_unreachable("Unrecognized cv_error_code");
}
diff --git a/llvm/lib/DebugInfo/CodeView/StreamReader.cpp b/llvm/lib/DebugInfo/CodeView/StreamReader.cpp
index cc5cebc9c43..64e45487322 100644
--- a/llvm/lib/DebugInfo/CodeView/StreamReader.cpp
+++ b/llvm/lib/DebugInfo/CodeView/StreamReader.cpp
@@ -17,6 +17,13 @@ using namespace llvm::codeview;
StreamReader::StreamReader(StreamRef S) : Stream(S), Offset(0) {}
+Error StreamReader::readLongestContiguousChunk(ArrayRef<uint8_t> &Buffer) {
+ if (auto EC = Stream.readLongestContiguousChunk(Offset, Buffer))
+ return EC;
+ Offset += Buffer.size();
+ return Error::success();
+}
+
Error StreamReader::readBytes(ArrayRef<uint8_t> &Buffer, uint32_t Size) {
if (auto EC = Stream.readBytes(Offset, Size, Buffer))
return EC;
diff --git a/llvm/lib/DebugInfo/CodeView/StreamWriter.cpp b/llvm/lib/DebugInfo/CodeView/StreamWriter.cpp
new file mode 100644
index 00000000000..f61c6b522f5
--- /dev/null
+++ b/llvm/lib/DebugInfo/CodeView/StreamWriter.cpp
@@ -0,0 +1,77 @@
+//===- StreamWrite.cpp - Writes bytes and objects to a stream -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/CodeView/StreamWriter.h"
+
+#include "llvm/DebugInfo/CodeView/CodeViewError.h"
+#include "llvm/DebugInfo/CodeView/StreamReader.h"
+#include "llvm/DebugInfo/CodeView/StreamRef.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+
+StreamWriter::StreamWriter(StreamRef S) : Stream(S), Offset(0) {}
+
+Error StreamWriter::writeBytes(ArrayRef<uint8_t> Buffer) {
+ if (auto EC = Stream.writeBytes(Offset, Buffer))
+ return EC;
+ Offset += Buffer.size();
+ return Error::success();
+}
+
+Error StreamWriter::writeInteger(uint16_t Int) {
+ return writeObject(support::ulittle16_t(Int));
+}
+
+Error StreamWriter::writeInteger(uint32_t Int) {
+ return writeObject(support::ulittle32_t(Int));
+}
+
+Error StreamWriter::writeZeroString(StringRef Str) {
+ if (auto EC = writeFixedString(Str))
+ return EC;
+ if (auto EC = writeObject('\0'))
+ return EC;
+
+ return Error::success();
+}
+
+Error StreamWriter::writeFixedString(StringRef Str) {
+ ArrayRef<uint8_t> Bytes(Str.bytes_begin(), Str.bytes_end());
+ if (auto EC = Stream.writeBytes(Offset, Bytes))
+ return EC;
+
+ Offset += Str.size();
+ return Error::success();
+}
+
+Error StreamWriter::writeStreamRef(StreamRef Ref) {
+ if (auto EC = writeStreamRef(Ref, Ref.getLength()))
+ return EC;
+ Offset += Ref.getLength();
+ return Error::success();
+}
+
+Error StreamWriter::writeStreamRef(StreamRef Ref, uint32_t Length) {
+ Ref = Ref.slice(0, Length);
+
+ StreamReader SrcReader(Ref);
+ // This is a bit tricky. If we just call readBytes, we are requiring that it
+ // return us the entire stream as a contiguous buffer. For large streams this
+ // will allocate a huge amount of space from the pool. Instead, iterate over
+ // each contiguous chunk until we've consumed the entire stream.
+ while (SrcReader.bytesRemaining() > 0) {
+ ArrayRef<uint8_t> Chunk;
+ if (auto EC = SrcReader.readLongestContiguousChunk(Chunk))
+ return EC;
+ if (auto EC = writeBytes(Chunk))
+ return EC;
+ }
+ return Error::success();
+}
diff --git a/llvm/lib/DebugInfo/CodeView/TypeDumper.cpp b/llvm/lib/DebugInfo/CodeView/TypeDumper.cpp
index 2080a63d8bc..42cb0aa1fd2 100644
--- a/llvm/lib/DebugInfo/CodeView/TypeDumper.cpp
+++ b/llvm/lib/DebugInfo/CodeView/TypeDumper.cpp
@@ -692,7 +692,7 @@ bool CVTypeDumper::dump(const CVTypeArray &Types) {
}
bool CVTypeDumper::dump(ArrayRef<uint8_t> Data) {
- ByteStream Stream(Data);
+ ByteStream<> Stream(Data);
CVTypeArray Types;
StreamReader Reader(Stream);
if (auto EC = Reader.readArray(Types, Reader.getLength())) {
diff --git a/llvm/lib/DebugInfo/PDB/Raw/MappedBlockStream.cpp b/llvm/lib/DebugInfo/PDB/Raw/MappedBlockStream.cpp
index cae33768af6..86e799ca663 100644
--- a/llvm/lib/DebugInfo/PDB/Raw/MappedBlockStream.cpp
+++ b/llvm/lib/DebugInfo/PDB/Raw/MappedBlockStream.cpp
@@ -29,6 +29,12 @@ public:
};
}
+typedef std::pair<uint32_t, uint32_t> Interval;
+static Interval intersect(const Interval &I1, const Interval &I2) {
+ return std::make_pair(std::max(I1.first, I2.first),
+ std::min(I1.second, I2.second));
+}
+
MappedBlockStream::MappedBlockStream(std::unique_ptr<IPDBStreamData> Data,
const IPDBFile &Pdb)
: Pdb(Pdb), Data(std::move(Data)) {}
@@ -46,26 +52,99 @@ Error MappedBlockStream::readBytes(uint32_t Offset, uint32_t Size,
auto CacheIter = CacheMap.find(Offset);
if (CacheIter != CacheMap.end()) {
- // In a more general solution, we would need to guarantee that the
- // cached allocation is at least the requested size. In practice, since
- // these are CodeView / PDB records, we know they are always formatted
- // the same way and never change, so we should never be requesting two
- // allocations from the same address with different sizes.
- Buffer = ArrayRef<uint8_t>(CacheIter->second, Size);
+ // Try to find an alloc that was large enough for this request.
+ for (auto &Entry : CacheIter->second) {
+ if (Entry.size() >= Size) {
+ Buffer = Entry.slice(0, Size);
+ return Error::success();
+ }
+ }
+ }
+
+ // We couldn't find a buffer that started at the correct offset (the most
+ // common scenario). Try to see if there is a buffer that starts at some
+ // other offset but overlaps the desired range.
+ for (auto &CacheItem : CacheMap) {
+ Interval RequestExtent = std::make_pair(Offset, Offset + Size);
+
+ // We already checked this one on the fast path above.
+ if (CacheItem.first == Offset)
+ continue;
+ // If the initial extent of the cached item is beyond the ending extent
+ // of the request, there is no overlap.
+ if (CacheItem.first >= Offset + Size)
+ continue;
+
+ // We really only have to check the last item in the list, since we append
+ // in order of increasing length.
+ if (CacheItem.second.empty())
+ continue;
+
+ auto CachedAlloc = CacheItem.second.back();
+ // If the initial extent of the request is beyond the ending extent of
+ // the cached item, there is no overlap.
+ Interval CachedExtent =
+ std::make_pair(CacheItem.first, CacheItem.first + CachedAlloc.size());
+ if (RequestExtent.first >= CachedExtent.first + CachedExtent.second)
+ continue;
+
+ Interval Intersection = intersect(CachedExtent, RequestExtent);
+ // Only use this if the entire request extent is contained in the cached
+ // extent.
+ if (Intersection != RequestExtent)
+ continue;
+
+ uint32_t CacheRangeOffset =
+ AbsoluteDifference(CachedExtent.first, Intersection.first);
+ Buffer = CachedAlloc.slice(CacheRangeOffset, Size);
return Error::success();
}
// Otherwise allocate a large enough buffer in the pool, memcpy the data
- // into it, and return an ArrayRef to that.
+ // into it, and return an ArrayRef to that. Do not touch existing pool
+ // allocations, as existing clients may be holding a pointer which must
+ // not be invalidated.
uint8_t *WriteBuffer = Pool.Allocate<uint8_t>(Size);
-
if (auto EC = readBytes(Offset, MutableArrayRef<uint8_t>(WriteBuffer, Size)))
return EC;
- CacheMap.insert(std::make_pair(Offset, WriteBuffer));
+
+ if (CacheIter != CacheMap.end()) {
+ CacheIter->second.emplace_back(WriteBuffer, Size);
+ } else {
+ std::vector<CacheEntry> List;
+ List.emplace_back(WriteBuffer, Size);
+ CacheMap.insert(std::make_pair(Offset, List));
+ }
Buffer = ArrayRef<uint8_t>(WriteBuffer, Size);
return Error::success();
}
+Error MappedBlockStream::readLongestContiguousChunk(
+ uint32_t Offset, ArrayRef<uint8_t> &Buffer) const {
+ // Make sure we aren't trying to read beyond the end of the stream.
+ if (Offset >= Data->getLength())
+ return make_error<RawError>(raw_error_code::insufficient_buffer);
+ uint32_t First = Offset / Pdb.getBlockSize();
+ uint32_t Last = First;
+
+ auto BlockList = Data->getStreamBlocks();
+ while (Last < Pdb.getBlockCount() - 1) {
+ if (BlockList[Last] != BlockList[Last + 1] - 1)
+ break;
+ ++Last;
+ }
+
+ uint32_t OffsetInFirstBlock = Offset % Pdb.getBlockSize();
+ uint32_t BytesFromFirstBlock = Pdb.getBlockSize() - OffsetInFirstBlock;
+ uint32_t BlockSpan = Last - First + 1;
+ uint32_t ByteSpan =
+ BytesFromFirstBlock + (BlockSpan - 1) * Pdb.getBlockSize();
+ Buffer = Pdb.getBlockData(BlockList[First], Pdb.getBlockSize());
+ Buffer = Buffer.drop_front(OffsetInFirstBlock);
+ Buffer = ArrayRef<uint8_t>(Buffer.data(), ByteSpan);
+ return Error::success();
+}
+
uint32_t MappedBlockStream::getLength() const { return Data->getLength(); }
bool MappedBlockStream::tryReadContiguously(uint32_t Offset, uint32_t Size,
@@ -130,7 +209,73 @@ Error MappedBlockStream::readBytes(uint32_t Offset,
}
return Error::success();
+}
+Error MappedBlockStream::writeBytes(uint32_t Offset,
+ ArrayRef<uint8_t> Buffer) const {
+ // Make sure we aren't trying to write beyond the end of the stream.
+ if (Buffer.size() > Data->getLength())
+ return make_error<RawError>(raw_error_code::insufficient_buffer);
+
+ if (Offset > Data->getLength() - Buffer.size())
+ return make_error<RawError>(raw_error_code::insufficient_buffer);
+
+ uint32_t BlockNum = Offset / Pdb.getBlockSize();
+ uint32_t OffsetInBlock = Offset % Pdb.getBlockSize();
+
+ uint32_t BytesLeft = Buffer.size();
+ auto BlockList = Data->getStreamBlocks();
+ uint32_t BytesWritten = 0;
+ while (BytesLeft > 0) {
+ uint32_t StreamBlockAddr = BlockList[BlockNum];
+ uint32_t BytesToWriteInChunk =
+ std::min(BytesLeft, Pdb.getBlockSize() - OffsetInBlock);
+
+ const uint8_t *Chunk = Buffer.data() + BytesWritten;
+ ArrayRef<uint8_t> ChunkData(Chunk, BytesToWriteInChunk);
+ if (auto EC = Pdb.setBlockData(StreamBlockAddr, OffsetInBlock, ChunkData))
+ return EC;
+
+ BytesLeft -= BytesToWriteInChunk;
+ BytesWritten += BytesToWriteInChunk;
+ ++BlockNum;
+ OffsetInBlock = 0;
+ }
+
+ // If this write overlapped a read which previously came from the pool,
+ // someone may still be holding a pointer to that alloc which is now invalid.
+ // Compute the overlapping range and update the cache entry, so any
+ // outstanding buffers are automatically updated.
+ for (const auto &MapEntry : CacheMap) {
+ // If the end of the written extent precedes the beginning of the cached
+ // extent, ignore this map entry.
+ if (Offset + BytesWritten < MapEntry.first)
+ continue;
+ for (const auto &Alloc : MapEntry.second) {
+ // If the end of the cached extent precedes the beginning of the written
+ // extent, ignore this alloc.
+ if (MapEntry.first + Alloc.size() < Offset)
+ continue;
+
+ // If we get here, they are guaranteed to overlap.
+ Interval WriteInterval = std::make_pair(Offset, Offset + BytesWritten);
+ Interval CachedInterval =
+ std::make_pair(MapEntry.first, MapEntry.first + Alloc.size());
+ // If they overlap, we need to write the new data into the overlapping
+ // range.
+ auto Intersection = intersect(WriteInterval, CachedInterval);
+ assert(Intersection.first <= Intersection.second);
+
+ uint32_t Length = Intersection.second - Intersection.first;
+ uint32_t SrcOffset =
+ AbsoluteDifference(WriteInterval.first, Intersection.first);
+ uint32_t DestOffset =
+ AbsoluteDifference(CachedInterval.first, Intersection.first);
+ ::memcpy(Alloc.data() + DestOffset, Buffer.data() + SrcOffset, Length);
+ }
+ }
+
+ return Error::success();
}
uint32_t MappedBlockStream::getNumBytesCopied() const {
diff --git a/llvm/lib/DebugInfo/PDB/Raw/PDBFile.cpp b/llvm/lib/DebugInfo/PDB/Raw/PDBFile.cpp
index 086105b4ded..7426dae86e3 100644
--- a/llvm/lib/DebugInfo/PDB/Raw/PDBFile.cpp
+++ b/llvm/lib/DebugInfo/PDB/Raw/PDBFile.cpp
@@ -136,6 +136,17 @@ ArrayRef<uint8_t> PDBFile::getBlockData(uint32_t BlockIndex,
NumBytes);
}
+Error PDBFile::setBlockData(uint32_t BlockIndex, uint32_t Offset,
+ ArrayRef<uint8_t> Data) const {
+ if (BlockIndex >= getBlockCount())
+ return make_error<RawError>(raw_error_code::invalid_block_address);
+
+ if (Offset > getBlockSize() - Data.size())
+ return make_error<RawError>(raw_error_code::insufficient_buffer);
+
+ return make_error<RawError>(raw_error_code::not_writable);
+}
+
Error PDBFile::parseFileHeaders() {
std::error_code EC;
MemoryBufferRef BufferRef = *Context->Buffer;
diff --git a/llvm/lib/DebugInfo/PDB/Raw/RawError.cpp b/llvm/lib/DebugInfo/PDB/Raw/RawError.cpp
index bcfa55a8b01..07aebb7d739 100644
--- a/llvm/lib/DebugInfo/PDB/Raw/RawError.cpp
+++ b/llvm/lib/DebugInfo/PDB/Raw/RawError.cpp
@@ -28,6 +28,10 @@ public:
return "The specified stream could not be loaded.";
case raw_error_code::index_out_of_bounds:
return "The specified item does not exist in the array.";
+ case raw_error_code::invalid_block_address:
+ return "The specified block address is not valid.";
+ case raw_error_code::not_writable:
+ return "The PDB does not support writing.";
}
llvm_unreachable("Unrecognized raw_error_code");
}
OpenPOWER on IntegriCloud