llvm.org GIT mirror llvm / 9524e3f
[Support] Add WritableMemoryBuffer class Summary: The motivation here is LLDB, where we need to fixup relocations in mmapped files before their contents can be read correctly. The MemoryBuffer class does exactly what we need, *except* that it maps the file in read-only mode. WritableMemoryBuffer reuses the existing machinery for opening and mmapping a file. The only difference is in the argument to the mapped_file_region constructor -- we create a private copy-on-write mapping, so that we can make changes to the mapped data, but the changes aren't carried over to the underlying file. This patch is based on an initial version by Zachary Turner. Reviewers: mehdi_amini, rnk, rafael, dblaikie, zturner Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D40291 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@321071 91177308-0d34-0410-b5e6-96231b3b80d8 Pavel Labath 2 years ago
3 changed file(s) with 194 addition(s) and 65 deletion(s). Raw diff Collapse all Expand all
1414 #define LLVM_SUPPORT_MEMORYBUFFER_H
1515
1616 #include "llvm-c/Types.h"
17 #include "llvm/ADT/ArrayRef.h"
1718 #include "llvm/ADT/StringRef.h"
1819 #include "llvm/ADT/Twine.h"
1920 #include "llvm/Support/CBindingWrapping.h"
4647
4748 void init(const char *BufStart, const char *BufEnd,
4849 bool RequiresNullTerminator);
50
51 static constexpr bool Writable = false;
52
4953 public:
5054 MemoryBuffer(const MemoryBuffer &) = delete;
5155 MemoryBuffer &operator=(const MemoryBuffer &) = delete;
121125 /// Allocate a new MemoryBuffer of the specified size that is not initialized.
122126 /// Note that the caller should initialize the memory allocated by this
123127 /// method. The memory is owned by the MemoryBuffer object.
128 //
129 // TODO: Remove this and migrate callers to
130 // WritableMemoryBuffer::getNewUninitMemBuffer
124131 static std::unique_ptr
125132 getNewUninitMemBuffer(size_t Size, const Twine &BufferName = "");
126133
153160 virtual BufferKind getBufferKind() const = 0;
154161
155162 MemoryBufferRef getMemBufferRef() const;
163 };
164
165 /// This class is an extension of MemoryBuffer, which allows writing to the
166 /// underlying contents. It only supports creation methods that are guaranteed
167 /// to produce a writable buffer. For example, mapping a file read-only is not
168 /// supported.
169 class WritableMemoryBuffer : public MemoryBuffer {
170 protected:
171 WritableMemoryBuffer() = default;
172
173 static constexpr bool Writable = true;
174
175 public:
176 using MemoryBuffer::getBuffer;
177 using MemoryBuffer::getBufferEnd;
178 using MemoryBuffer::getBufferStart;
179
180 // const_cast is well-defined here, because the underlying buffer is
181 // guaranteed to have been initialized with a mutable buffer.
182 char *getBufferStart() {
183 return const_cast(MemoryBuffer::getBufferStart());
184 }
185 char *getBufferEnd() {
186 return const_cast(MemoryBuffer::getBufferEnd());
187 }
188 MutableArrayRef getBuffer() {
189 return {getBufferStart(), getBufferEnd()};
190 }
191
192 static ErrorOr>
193 getFile(const Twine &Filename, int64_t FileSize = -1,
194 bool IsVolatile = false);
195
196 /// Map a subrange of the specified file as a WritableMemoryBuffer.
197 static ErrorOr>
198 getFileSlice(const Twine &Filename, uint64_t MapSize, uint64_t Offset,
199 bool IsVolatile = false);
200
201 static std::unique_ptr
202 getNewUninitMemBuffer(size_t Size, const Twine &BufferName = "");
203
204 private:
205 // Hide these base class factory function so one can't write
206 // WritableMemoryBuffer::getXXX()
207 // and be surprised that he got a read-only Buffer.
208 using MemoryBuffer::getFileAsStream;
209 using MemoryBuffer::getFileOrSTDIN;
210 using MemoryBuffer::getMemBuffer;
211 using MemoryBuffer::getMemBufferCopy;
212 using MemoryBuffer::getNewMemBuffer;
213 using MemoryBuffer::getOpenFile;
214 using MemoryBuffer::getOpenFileSlice;
215 using MemoryBuffer::getSTDIN;
156216 };
157217
158218 class MemoryBufferRef {
7979
8080 namespace {
8181 /// MemoryBufferMem - Named MemoryBuffer pointing to a block of memory.
82 class MemoryBufferMem : public MemoryBuffer {
82 template
83 class MemoryBufferMem : public MB {
8384 public:
8485 MemoryBufferMem(StringRef InputData, bool RequiresNullTerminator) {
85 init(InputData.begin(), InputData.end(), RequiresNullTerminator);
86 MemoryBuffer::init(InputData.begin(), InputData.end(),
87 RequiresNullTerminator);
8688 }
8789
8890 /// Disable sized deallocation for MemoryBufferMem, because it has
9496 return StringRef(reinterpret_cast(this + 1));
9597 }
9698
97 BufferKind getBufferKind() const override {
98 return MemoryBuffer_Malloc;
99 MemoryBuffer::BufferKind getBufferKind() const override {
100 return MemoryBuffer::MemoryBuffer_Malloc;
99101 }
100102 };
101103 }
102104
103 static ErrorOr>
104 getFileAux(const Twine &Filename, int64_t FileSize, uint64_t MapSize,
105 template
106 static ErrorOr>
107 getFileAux(const Twine &Filename, int64_t FileSize, uint64_t MapSize,
105108 uint64_t Offset, bool RequiresNullTerminator, bool IsVolatile);
106109
107110 std::unique_ptr
108111 MemoryBuffer::getMemBuffer(StringRef InputData, StringRef BufferName,
109112 bool RequiresNullTerminator) {
110113 auto *Ret = new (NamedBufferAlloc(BufferName))
111 MemoryBufferMem(InputData, RequiresNullTerminator);
114 MemoryBufferMem(InputData, RequiresNullTerminator);
112115 return std::unique_ptr(Ret);
113116 }
114117
118121 Ref.getBuffer(), Ref.getBufferIdentifier(), RequiresNullTerminator));
119122 }
120123
124 static ErrorOr>
125 getMemBufferCopyImpl(StringRef InputData, const Twine &BufferName) {
126 auto Buf = WritableMemoryBuffer::getNewUninitMemBuffer(InputData.size(), BufferName);
127 if (!Buf)
128 return make_error_code(errc::not_enough_memory);
129 memcpy(Buf->getBufferStart(), InputData.data(), InputData.size());
130 return std::move(Buf);
131 }
132
121133 std::unique_ptr
122134 MemoryBuffer::getMemBufferCopy(StringRef InputData, const Twine &BufferName) {
123 std::unique_ptr Buf =
124 getNewUninitMemBuffer(InputData.size(), BufferName);
125 if (!Buf)
126 return nullptr;
127 memcpy(const_cast(Buf->getBufferStart()), InputData.data(),
128 InputData.size());
129 return Buf;
135 auto Buf = getMemBufferCopyImpl(InputData, BufferName);
136 if (Buf)
137 return std::move(*Buf);
138 return nullptr;
130139 }
131140
132141 std::unique_ptr
133142 MemoryBuffer::getNewUninitMemBuffer(size_t Size, const Twine &BufferName) {
134 // Allocate space for the MemoryBuffer, the data and the name. It is important
135 // that MemoryBuffer and data are aligned so PointerIntPair works with them.
136 // TODO: Is 16-byte alignment enough? We copy small object files with large
137 // alignment expectations into this buffer.
138 SmallString<256> NameBuf;
139 StringRef NameRef = BufferName.toStringRef(NameBuf);
140 size_t AlignedStringLen =
141 alignTo(sizeof(MemoryBufferMem) + NameRef.size() + 1, 16);
142 size_t RealLen = AlignedStringLen + Size + 1;
143 char *Mem = static_cast(operator new(RealLen, std::nothrow));
144 if (!Mem)
145 return nullptr;
146
147 // The name is stored after the class itself.
148 CopyStringRef(Mem + sizeof(MemoryBufferMem), NameRef);
149
150 // The buffer begins after the name and must be aligned.
151 char *Buf = Mem + AlignedStringLen;
152 Buf[Size] = 0; // Null terminate buffer.
153
154 auto *Ret = new (Mem) MemoryBufferMem(StringRef(Buf, Size), true);
155 return std::unique_ptr(Ret);
143 return WritableMemoryBuffer::getNewUninitMemBuffer(Size, BufferName);
156144 }
157145
158146 std::unique_ptr
178166 ErrorOr>
179167 MemoryBuffer::getFileSlice(const Twine &FilePath, uint64_t MapSize,
180168 uint64_t Offset, bool IsVolatile) {
181 return getFileAux(FilePath, -1, MapSize, Offset, false, IsVolatile);
182 }
183
169 return getFileAux(FilePath, -1, MapSize, Offset, false,
170 IsVolatile);
171 }
184172
185173 //===----------------------------------------------------------------------===//
186174 // MemoryBuffer::getFile implementation.
190178 /// \brief Memory maps a file descriptor using sys::fs::mapped_file_region.
191179 ///
192180 /// This handles converting the offset into a legal offset on the platform.
193 class MemoryBufferMMapFile : public MemoryBuffer {
181 template
182 class MemoryBufferMMapFile : public MB {
194183 sys::fs::mapped_file_region MFR;
195184
196185 static uint64_t getLegalMapOffset(uint64_t Offset) {
208197 public:
209198 MemoryBufferMMapFile(bool RequiresNullTerminator, int FD, uint64_t Len,
210199 uint64_t Offset, std::error_code &EC)
211 : MFR(FD, sys::fs::mapped_file_region::readonly,
200 : MFR(FD,
201 MB::Writable ? sys::fs::mapped_file_region::priv
202 : sys::fs::mapped_file_region::readonly,
212203 getLegalMapSize(Len, Offset), getLegalMapOffset(Offset), EC) {
213204 if (!EC) {
214205 const char *Start = getStart(Len, Offset);
215 init(Start, Start + Len, RequiresNullTerminator);
206 MemoryBuffer::init(Start, Start + Len, RequiresNullTerminator);
216207 }
217208 }
218209
225216 return StringRef(reinterpret_cast(this + 1));
226217 }
227218
228 BufferKind getBufferKind() const override {
229 return MemoryBuffer_MMap;
219 MemoryBuffer::BufferKind getBufferKind() const override {
220 return MemoryBuffer::MemoryBuffer_MMap;
230221 }
231222 };
232223 }
233224
234 static ErrorOrMemoryBuffer>>
225 static ErrorOrWritableMemoryBuffer>>
235226 getMemoryBufferForStream(int FD, const Twine &BufferName) {
236227 const ssize_t ChunkSize = 4096*4;
237228 SmallString Buffer;
245236 Buffer.set_size(Buffer.size() + ReadBytes);
246237 } while (ReadBytes != 0);
247238
248 return MemoryBuffer::getMemBufferCopy(Buffer, BufferName);
239 return getMemBufferCopyImpl(Buffer, BufferName);
249240 }
250241
251242
252243 ErrorOr>
253244 MemoryBuffer::getFile(const Twine &Filename, int64_t FileSize,
254245 bool RequiresNullTerminator, bool IsVolatile) {
255 return getFileAux(Filename, FileSize, FileSize, 0,
256 RequiresNullTerminator, IsVolatile);
257 }
258
259 static ErrorOr>
246 return getFileAux(Filename, FileSize, FileSize, 0,
247 RequiresNullTerminator, IsVolatile);
248 }
249
250 template
251 static ErrorOr>
260252 getOpenFileImpl(int FD, const Twine &Filename, uint64_t FileSize,
261253 uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator,
262254 bool IsVolatile);
263255
264 static ErrorOr>
256 template >
257 static ErrorOr>
265258 getFileAux(const Twine &Filename, int64_t FileSize, uint64_t MapSize,
266259 uint64_t Offset, bool RequiresNullTerminator, bool IsVolatile) {
267260 int FD;
268261 std::error_code EC = sys::fs::openFileForRead(Filename, FD);
262
269263 if (EC)
270264 return EC;
271265
272 ErrorOr> Ret =
273 getOpenFileImpl(FD, Filename, FileSize, MapSize, Offset,
274 RequiresNullTerminator, IsVolatile);
266 auto Ret = getOpenFileImpl(FD, Filename, FileSize, MapSize, Offset,
267 RequiresNullTerminator, IsVolatile);
275268 close(FD);
276269 return Ret;
270 }
271
272 ErrorOr>
273 WritableMemoryBuffer::getFile(const Twine &Filename, int64_t FileSize,
274 bool IsVolatile) {
275 return getFileAux(Filename, FileSize, FileSize, 0,
276 /*RequiresNullTerminator*/ false,
277 IsVolatile);
278 }
279
280 ErrorOr>
281 WritableMemoryBuffer::getFileSlice(const Twine &Filename, uint64_t MapSize,
282 uint64_t Offset, bool IsVolatile) {
283 return getFileAux(Filename, -1, MapSize, Offset, false,
284 IsVolatile);
285 }
286
287 std::unique_ptr
288 WritableMemoryBuffer::getNewUninitMemBuffer(size_t Size, const Twine &BufferName) {
289 using MemBuffer = MemoryBufferMem;
290 // Allocate space for the MemoryBuffer, the data and the name. It is important
291 // that MemoryBuffer and data are aligned so PointerIntPair works with them.
292 // TODO: Is 16-byte alignment enough? We copy small object files with large
293 // alignment expectations into this buffer.
294 SmallString<256> NameBuf;
295 StringRef NameRef = BufferName.toStringRef(NameBuf);
296 size_t AlignedStringLen = alignTo(sizeof(MemBuffer) + NameRef.size() + 1, 16);
297 size_t RealLen = AlignedStringLen + Size + 1;
298 char *Mem = static_cast(operator new(RealLen, std::nothrow));
299 if (!Mem)
300 return nullptr;
301
302 // The name is stored after the class itself.
303 CopyStringRef(Mem + sizeof(MemBuffer), NameRef);
304
305 // The buffer begins after the name and must be aligned.
306 char *Buf = Mem + AlignedStringLen;
307 Buf[Size] = 0; // Null terminate buffer.
308
309 auto *Ret = new (Mem) MemBuffer(StringRef(Buf, Size), true);
310 return std::unique_ptr(Ret);
277311 }
278312
279313 static bool shouldUseMmap(int FD,
331365 return true;
332366 }
333367
334 static ErrorOr>
368 template >
369 static ErrorOr>
335370 getOpenFileImpl(int FD, const Twine &Filename, uint64_t FileSize,
336371 uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator,
337372 bool IsVolatile) {
363398 if (shouldUseMmap(FD, FileSize, MapSize, Offset, RequiresNullTerminator,
364399 PageSize, IsVolatile)) {
365400 std::error_code EC;
366 std::unique_ptr Result(
367 new (NamedBufferAlloc(Filename))
368 MemoryBufferMMapFile(RequiresNullTerminator, FD, MapSize, Offset, EC));
401 std::unique_ptr Result(
402 new (NamedBufferAlloc(Filename)) MemoryBufferMMapFile(
403 RequiresNullTerminator, FD, MapSize, Offset, EC));
369404 if (!EC)
370405 return std::move(Result);
371406 }
372407
373 std::unique_ptr Buf =
374 MemoryBuffer::getNewUninitMemBuffer(MapSize, Filename);
408 auto Buf = WritableMemoryBuffer::getNewUninitMemBuffer(MapSize, Filename);
375409 if (!Buf) {
376410 // Failed to create a buffer. The only way it can fail is if
377411 // new(std::nothrow) returns 0.
378412 return make_error_code(errc::not_enough_memory);
379413 }
380414
381 char *BufPtr = const_cast(Buf->getBufferStart());
415 char *BufPtr = Buf.get()->getBufferStart();
382416
383417 size_t BytesLeft = MapSize;
384418 #ifndef HAVE_PREAD
411445 ErrorOr>
412446 MemoryBuffer::getOpenFile(int FD, const Twine &Filename, uint64_t FileSize,
413447 bool RequiresNullTerminator, bool IsVolatile) {
414 return getOpenFileImpl(FD, Filename, FileSize, FileSize, 0,
448 return getOpenFileImpl(FD, Filename, FileSize, FileSize, 0,
415449 RequiresNullTerminator, IsVolatile);
416450 }
417451
419453 MemoryBuffer::getOpenFileSlice(int FD, const Twine &Filename, uint64_t MapSize,
420454 int64_t Offset, bool IsVolatile) {
421455 assert(MapSize != uint64_t(-1));
422 return getOpenFileImpl(FD, Filename, -1, MapSize, Offset, false, IsVolatile);
456 return getOpenFileImpl(FD, Filename, -1, MapSize, Offset, false,
457 IsVolatile);
423458 }
424459
425460 ErrorOr> MemoryBuffer::getSTDIN() {
1414 #include "llvm/Support/FileSystem.h"
1515 #include "llvm/Support/FileUtilities.h"
1616 #include "llvm/Support/raw_ostream.h"
17 #include "llvm/Testing/Support/Error.h"
1718 #include "gtest/gtest.h"
1819
1920 using namespace llvm;
225226 EXPECT_TRUE(BufData2.substr(0x1800,8).equals("abcdefgh"));
226227 EXPECT_TRUE(BufData2.substr(0x2FF8,8).equals("abcdefgh"));
227228 }
228 }
229
230 TEST_F(MemoryBufferTest, writableSlice) {
231 // Create a file initialized with some data
232 int FD;
233 SmallString<64> TestPath;
234 sys::fs::createTemporaryFile("MemoryBufferTest_WritableSlice", "temp", FD,
235 TestPath);
236 FileRemover Cleanup(TestPath);
237 raw_fd_ostream OF(FD, true);
238 for (unsigned i = 0; i < 0x1000; ++i)
239 OF << "0123456789abcdef";
240 OF.close();
241
242 {
243 auto MBOrError =
244 WritableMemoryBuffer::getFileSlice(TestPath.str(), 0x6000, 0x2000);
245 ASSERT_FALSE(MBOrError.getError());
246 // Write some data. It should be mapped private, so that upon completion
247 // the original file contents are not modified.
248 WritableMemoryBuffer &MB = **MBOrError;
249 ASSERT_EQ(0x6000u, MB.getBufferSize());
250 char *Start = MB.getBufferStart();
251 ASSERT_EQ(MB.getBufferEnd(), MB.getBufferStart() + MB.getBufferSize());
252 ::memset(Start, 'x', MB.getBufferSize());
253 }
254
255 auto MBOrError = MemoryBuffer::getFile(TestPath);
256 ASSERT_FALSE(MBOrError.getError());
257 auto &MB = **MBOrError;
258 ASSERT_EQ(0x10000u, MB.getBufferSize());
259 for (size_t i = 0; i < MB.getBufferSize(); i += 0x10)
260 EXPECT_EQ("0123456789abcdef", MB.getBuffer().substr(i, 0x10)) << "i: " << i;
261 }
262 }