blob: 8b09dc57cde64f96ca318726eb360c9e6ac967d0 [file] [log] [blame]
Zachary Turner0a43efe2016-04-25 17:38:08 +00001//===- PDBFile.cpp - Low level interface to a PDB file ----------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
Zachary Turnerd8447992016-06-07 05:28:55 +000011
Zachary Turner0a43efe2016-04-25 17:38:08 +000012#include "llvm/ADT/ArrayRef.h"
Zachary Turnerd8447992016-06-07 05:28:55 +000013#include "llvm/DebugInfo/CodeView/StreamArray.h"
Zachary Turnerb84faa82016-06-10 05:10:19 +000014#include "llvm/DebugInfo/CodeView/StreamInterface.h"
Zachary Turnerd8447992016-06-07 05:28:55 +000015#include "llvm/DebugInfo/CodeView/StreamReader.h"
Zachary Turnerab58ae82016-06-30 17:43:00 +000016#include "llvm/DebugInfo/CodeView/StreamWriter.h"
Zachary Turner2f09b502016-04-29 17:28:47 +000017#include "llvm/DebugInfo/PDB/Raw/DbiStream.h"
Zachary Turnera1657a92016-06-08 17:26:39 +000018#include "llvm/DebugInfo/PDB/Raw/DirectoryStreamData.h"
Zachary Turnerd8447992016-06-07 05:28:55 +000019#include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h"
Zachary Turner2f09b502016-04-29 17:28:47 +000020#include "llvm/DebugInfo/PDB/Raw/InfoStream.h"
Zachary Turner3df1bfa2016-06-03 05:52:57 +000021#include "llvm/DebugInfo/PDB/Raw/NameHashTable.h"
Rui Ueyama1f6b6e22016-05-13 21:21:53 +000022#include "llvm/DebugInfo/PDB/Raw/PublicsStream.h"
Zachary Turner819e77d2016-05-06 20:51:57 +000023#include "llvm/DebugInfo/PDB/Raw/RawError.h"
Rui Ueyama0fcd8262016-05-20 19:55:17 +000024#include "llvm/DebugInfo/PDB/Raw/SymbolStream.h"
Zachary Turnerf5c59652016-05-03 00:28:21 +000025#include "llvm/DebugInfo/PDB/Raw/TpiStream.h"
Zachary Turner0a43efe2016-04-25 17:38:08 +000026#include "llvm/Support/Endian.h"
Zachary Turner1dc9fd32016-06-14 20:48:36 +000027#include "llvm/Support/FileOutputBuffer.h"
Zachary Turner0a43efe2016-04-25 17:38:08 +000028#include "llvm/Support/MemoryBuffer.h"
29
30using namespace llvm;
Zachary Turnerb84faa82016-06-10 05:10:19 +000031using namespace llvm::codeview;
Zachary Turner2f09b502016-04-29 17:28:47 +000032using namespace llvm::pdb;
Zachary Turner0a43efe2016-04-25 17:38:08 +000033
34namespace {
Zachary Turnerb84faa82016-06-10 05:10:19 +000035typedef FixedStreamArray<support::ulittle32_t> ulittle_array;
Zachary Turner0a43efe2016-04-25 17:38:08 +000036}
37
Zachary Turnerb84faa82016-06-10 05:10:19 +000038PDBFile::PDBFile(std::unique_ptr<StreamInterface> PdbFileBuffer)
39 : Buffer(std::move(PdbFileBuffer)), SB(nullptr) {}
Zachary Turner0a43efe2016-04-25 17:38:08 +000040
41PDBFile::~PDBFile() {}
42
Zachary Turnerb84faa82016-06-10 05:10:19 +000043uint32_t PDBFile::getBlockSize() const { return SB->BlockSize; }
Zachary Turner0a43efe2016-04-25 17:38:08 +000044
Zachary Turnerb84faa82016-06-10 05:10:19 +000045uint32_t PDBFile::getUnknown0() const { return SB->Unknown0; }
Zachary Turner0a43efe2016-04-25 17:38:08 +000046
Zachary Turnerb84faa82016-06-10 05:10:19 +000047uint32_t PDBFile::getBlockCount() const { return SB->NumBlocks; }
Zachary Turner0a43efe2016-04-25 17:38:08 +000048
Zachary Turnerb84faa82016-06-10 05:10:19 +000049uint32_t PDBFile::getNumDirectoryBytes() const { return SB->NumDirectoryBytes; }
Zachary Turner0a43efe2016-04-25 17:38:08 +000050
Zachary Turnerb84faa82016-06-10 05:10:19 +000051uint32_t PDBFile::getBlockMapIndex() const { return SB->BlockMapAddr; }
Zachary Turner0a43efe2016-04-25 17:38:08 +000052
Zachary Turnerb84faa82016-06-10 05:10:19 +000053uint32_t PDBFile::getUnknown1() const { return SB->Unknown1; }
Zachary Turner0a43efe2016-04-25 17:38:08 +000054
55uint32_t PDBFile::getNumDirectoryBlocks() const {
Zachary Turnerfaa554b2016-07-15 22:16:56 +000056 return msf::bytesToBlocks(SB->NumDirectoryBytes, SB->BlockSize);
Zachary Turner0a43efe2016-04-25 17:38:08 +000057}
58
59uint64_t PDBFile::getBlockMapOffset() const {
Zachary Turnerb84faa82016-06-10 05:10:19 +000060 return (uint64_t)SB->BlockMapAddr * SB->BlockSize;
Zachary Turner0a43efe2016-04-25 17:38:08 +000061}
62
Zachary Turnerb84faa82016-06-10 05:10:19 +000063uint32_t PDBFile::getNumStreams() const { return StreamSizes.size(); }
Zachary Turner0a43efe2016-04-25 17:38:08 +000064
65uint32_t PDBFile::getStreamByteSize(uint32_t StreamIndex) const {
Zachary Turnerb84faa82016-06-10 05:10:19 +000066 return StreamSizes[StreamIndex];
Zachary Turner0a43efe2016-04-25 17:38:08 +000067}
68
Zachary Turnerd8447992016-06-07 05:28:55 +000069ArrayRef<support::ulittle32_t>
Zachary Turner0a43efe2016-04-25 17:38:08 +000070PDBFile::getStreamBlockList(uint32_t StreamIndex) const {
Zachary Turnerb84faa82016-06-10 05:10:19 +000071 return StreamMap[StreamIndex];
Zachary Turner0a43efe2016-04-25 17:38:08 +000072}
73
David Majnemer1b79e9a2016-07-10 05:32:05 +000074uint32_t PDBFile::getFileSize() const { return Buffer->getLength(); }
Zachary Turner1dc9fd32016-06-14 20:48:36 +000075
David Majnemer6211b1f2016-07-10 03:34:47 +000076Expected<ArrayRef<uint8_t>> PDBFile::getBlockData(uint32_t BlockIndex,
77 uint32_t NumBytes) const {
Zachary Turnerfaa554b2016-07-15 22:16:56 +000078 uint64_t StreamBlockOffset = msf::blockToOffset(BlockIndex, getBlockSize());
Zachary Turner0a43efe2016-04-25 17:38:08 +000079
Zachary Turnerb84faa82016-06-10 05:10:19 +000080 ArrayRef<uint8_t> Result;
81 if (auto EC = Buffer->readBytes(StreamBlockOffset, NumBytes, Result))
David Majnemer6211b1f2016-07-10 03:34:47 +000082 return std::move(EC);
Zachary Turnerb84faa82016-06-10 05:10:19 +000083 return Result;
Zachary Turner0a43efe2016-04-25 17:38:08 +000084}
85
Zachary Turner5acb4ac2016-06-10 05:09:12 +000086Error PDBFile::setBlockData(uint32_t BlockIndex, uint32_t Offset,
87 ArrayRef<uint8_t> Data) const {
Zachary Turnerb84faa82016-06-10 05:10:19 +000088 if (Offset >= getBlockSize())
89 return make_error<RawError>(
90 raw_error_code::invalid_block_address,
91 "setBlockData attempted to write out of block bounds.");
Zachary Turner8848a7a2016-07-06 18:05:57 +000092 if (Data.size() > getBlockSize() - Offset)
Zachary Turnerb84faa82016-06-10 05:10:19 +000093 return make_error<RawError>(
94 raw_error_code::invalid_block_address,
95 "setBlockData attempted to write out of block bounds.");
Zachary Turner5acb4ac2016-06-10 05:09:12 +000096
Zachary Turnerfaa554b2016-07-15 22:16:56 +000097 uint64_t StreamBlockOffset = msf::blockToOffset(BlockIndex, getBlockSize());
Zachary Turnerb84faa82016-06-10 05:10:19 +000098 StreamBlockOffset += Offset;
99 return Buffer->writeBytes(StreamBlockOffset, Data);
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000100}
101
Zachary Turner819e77d2016-05-06 20:51:57 +0000102Error PDBFile::parseFileHeaders() {
Zachary Turnerb84faa82016-06-10 05:10:19 +0000103 StreamReader Reader(*Buffer);
Zachary Turnerc59261c2016-05-25 03:53:16 +0000104
Zachary Turnerb84faa82016-06-10 05:10:19 +0000105 if (auto EC = Reader.readObject(SB)) {
106 consumeError(std::move(EC));
Zachary Turner819e77d2016-05-06 20:51:57 +0000107 return make_error<RawError>(raw_error_code::corrupt_file,
108 "Does not contain superblock");
Zachary Turnerb84faa82016-06-10 05:10:19 +0000109 }
Zachary Turner0a43efe2016-04-25 17:38:08 +0000110
Zachary Turnerab58ae82016-06-30 17:43:00 +0000111 if (auto EC = setSuperBlock(SB))
112 return EC;
Zachary Turner819e77d2016-05-06 20:51:57 +0000113
Zachary Turnerab58ae82016-06-30 17:43:00 +0000114 Reader.setOffset(getBlockMapOffset());
115 if (auto EC = Reader.readArray(DirectoryBlocks, getNumDirectoryBlocks()))
116 return EC;
Zachary Turner0a43efe2016-04-25 17:38:08 +0000117
Zachary Turner819e77d2016-05-06 20:51:57 +0000118 return Error::success();
Zachary Turner0a43efe2016-04-25 17:38:08 +0000119}
120
Zachary Turner819e77d2016-05-06 20:51:57 +0000121Error PDBFile::parseStreamData() {
Zachary Turnerb84faa82016-06-10 05:10:19 +0000122 assert(SB);
Zachary Turnerd8447992016-06-07 05:28:55 +0000123 if (DirectoryStream)
124 return Error::success();
Zachary Turner0a43efe2016-04-25 17:38:08 +0000125
Zachary Turner0a43efe2016-04-25 17:38:08 +0000126 uint32_t NumStreams = 0;
Zachary Turner0a43efe2016-04-25 17:38:08 +0000127
Zachary Turnerd8447992016-06-07 05:28:55 +0000128 // Normally you can't use a MappedBlockStream without having fully parsed the
129 // PDB file, because it accesses the directory and various other things, which
130 // is exactly what we are attempting to parse. By specifying a custom
131 // subclass of IPDBStreamData which only accesses the fields that have already
132 // been parsed, we can avoid this and reuse MappedBlockStream.
Zachary Turnera1657a92016-06-08 17:26:39 +0000133 auto DS = MappedBlockStream::createDirectoryStream(*this);
134 if (!DS)
135 return DS.takeError();
Zachary Turnerb84faa82016-06-10 05:10:19 +0000136 StreamReader Reader(**DS);
Zachary Turnerd8447992016-06-07 05:28:55 +0000137 if (auto EC = Reader.readInteger(NumStreams))
138 return EC;
Zachary Turner0a43efe2016-04-25 17:38:08 +0000139
Zachary Turnerb84faa82016-06-10 05:10:19 +0000140 if (auto EC = Reader.readArray(StreamSizes, NumStreams))
Zachary Turnerd8447992016-06-07 05:28:55 +0000141 return EC;
142 for (uint32_t I = 0; I < NumStreams; ++I) {
Reid Kleckner5aba52f2016-06-22 22:42:24 +0000143 uint32_t StreamSize = getStreamByteSize(I);
144 // FIXME: What does StreamSize ~0U mean?
David Majnemer9efba742016-05-27 16:16:48 +0000145 uint64_t NumExpectedStreamBlocks =
Zachary Turnerfaa554b2016-07-15 22:16:56 +0000146 StreamSize == UINT32_MAX ? 0 : msf::bytesToBlocks(StreamSize,
147 SB->BlockSize);
Zachary Turnerb84faa82016-06-10 05:10:19 +0000148
149 // For convenience, we store the block array contiguously. This is because
150 // if someone calls setStreamMap(), it is more convenient to be able to call
151 // it with an ArrayRef instead of setting up a StreamRef. Since the
152 // DirectoryStream is cached in the class and thus lives for the life of the
153 // class, we can be guaranteed that readArray() will return a stable
154 // reference, even if it has to allocate from its internal pool.
155 ArrayRef<support::ulittle32_t> Blocks;
Zachary Turnerd8447992016-06-07 05:28:55 +0000156 if (auto EC = Reader.readArray(Blocks, NumExpectedStreamBlocks))
157 return EC;
David Majnemer1b79e9a2016-07-10 05:32:05 +0000158 for (uint32_t Block : Blocks) {
159 uint64_t BlockEndOffset = (uint64_t)(Block + 1) * SB->BlockSize;
160 if (BlockEndOffset > getFileSize())
161 return make_error<RawError>(raw_error_code::corrupt_file,
162 "Stream block map is corrupt.");
163 }
Zachary Turnerb84faa82016-06-10 05:10:19 +0000164 StreamMap.push_back(Blocks);
David Majnemer9efba742016-05-27 16:16:48 +0000165 }
166
Zachary Turner0a43efe2016-04-25 17:38:08 +0000167 // We should have read exactly SB->NumDirectoryBytes bytes.
Zachary Turnerd8447992016-06-07 05:28:55 +0000168 assert(Reader.bytesRemaining() == 0);
Zachary Turnera1657a92016-06-08 17:26:39 +0000169 DirectoryStream = std::move(*DS);
Zachary Turner819e77d2016-05-06 20:51:57 +0000170 return Error::success();
Zachary Turner0a43efe2016-04-25 17:38:08 +0000171}
172
Zachary Turnerd8447992016-06-07 05:28:55 +0000173llvm::ArrayRef<support::ulittle32_t> PDBFile::getDirectoryBlockArray() const {
Zachary Turnerab58ae82016-06-30 17:43:00 +0000174 return DirectoryBlocks;
Zachary Turner0a43efe2016-04-25 17:38:08 +0000175}
Zachary Turner53a65ba2016-04-26 18:42:34 +0000176
Zachary Turner819e77d2016-05-06 20:51:57 +0000177Expected<InfoStream &> PDBFile::getPDBInfoStream() {
Zachary Turner2f09b502016-04-29 17:28:47 +0000178 if (!Info) {
Zachary Turnera1657a92016-06-08 17:26:39 +0000179 auto InfoS = MappedBlockStream::createIndexedStream(StreamPDB, *this);
180 if (!InfoS)
181 return InfoS.takeError();
182 auto TempInfo = llvm::make_unique<InfoStream>(std::move(*InfoS));
183 if (auto EC = TempInfo->reload())
Zachary Turner819e77d2016-05-06 20:51:57 +0000184 return std::move(EC);
Zachary Turnera1657a92016-06-08 17:26:39 +0000185 Info = std::move(TempInfo);
Zachary Turner53a65ba2016-04-26 18:42:34 +0000186 }
Zachary Turner2f09b502016-04-29 17:28:47 +0000187 return *Info;
Zachary Turner53a65ba2016-04-26 18:42:34 +0000188}
189
Zachary Turner819e77d2016-05-06 20:51:57 +0000190Expected<DbiStream &> PDBFile::getPDBDbiStream() {
Zachary Turner2f09b502016-04-29 17:28:47 +0000191 if (!Dbi) {
Zachary Turnera1657a92016-06-08 17:26:39 +0000192 auto DbiS = MappedBlockStream::createIndexedStream(StreamDBI, *this);
193 if (!DbiS)
194 return DbiS.takeError();
195 auto TempDbi = llvm::make_unique<DbiStream>(*this, std::move(*DbiS));
196 if (auto EC = TempDbi->reload())
Zachary Turner819e77d2016-05-06 20:51:57 +0000197 return std::move(EC);
Zachary Turnera1657a92016-06-08 17:26:39 +0000198 Dbi = std::move(TempDbi);
Zachary Turner53a65ba2016-04-26 18:42:34 +0000199 }
Zachary Turner2f09b502016-04-29 17:28:47 +0000200 return *Dbi;
Zachary Turner53a65ba2016-04-26 18:42:34 +0000201}
Zachary Turnerf5c59652016-05-03 00:28:21 +0000202
Zachary Turner819e77d2016-05-06 20:51:57 +0000203Expected<TpiStream &> PDBFile::getPDBTpiStream() {
Zachary Turnerf5c59652016-05-03 00:28:21 +0000204 if (!Tpi) {
Zachary Turnera1657a92016-06-08 17:26:39 +0000205 auto TpiS = MappedBlockStream::createIndexedStream(StreamTPI, *this);
206 if (!TpiS)
207 return TpiS.takeError();
208 auto TempTpi = llvm::make_unique<TpiStream>(*this, std::move(*TpiS));
209 if (auto EC = TempTpi->reload())
Zachary Turner819e77d2016-05-06 20:51:57 +0000210 return std::move(EC);
Zachary Turnera1657a92016-06-08 17:26:39 +0000211 Tpi = std::move(TempTpi);
Zachary Turnerf5c59652016-05-03 00:28:21 +0000212 }
213 return *Tpi;
214}
Rui Ueyama1f6b6e22016-05-13 21:21:53 +0000215
Zachary Turnerc9972c62016-05-25 04:35:22 +0000216Expected<TpiStream &> PDBFile::getPDBIpiStream() {
217 if (!Ipi) {
Zachary Turnera1657a92016-06-08 17:26:39 +0000218 auto IpiS = MappedBlockStream::createIndexedStream(StreamIPI, *this);
219 if (!IpiS)
220 return IpiS.takeError();
221 auto TempIpi = llvm::make_unique<TpiStream>(*this, std::move(*IpiS));
222 if (auto EC = TempIpi->reload())
Zachary Turnerc9972c62016-05-25 04:35:22 +0000223 return std::move(EC);
Zachary Turnera1657a92016-06-08 17:26:39 +0000224 Ipi = std::move(TempIpi);
Zachary Turnerc9972c62016-05-25 04:35:22 +0000225 }
226 return *Ipi;
227}
228
Rui Ueyama1f6b6e22016-05-13 21:21:53 +0000229Expected<PublicsStream &> PDBFile::getPDBPublicsStream() {
230 if (!Publics) {
231 auto DbiS = getPDBDbiStream();
Zachary Turnera1657a92016-06-08 17:26:39 +0000232 if (!DbiS)
233 return DbiS.takeError();
234
Rui Ueyama1f6b6e22016-05-13 21:21:53 +0000235 uint32_t PublicsStreamNum = DbiS->getPublicSymbolStreamIndex();
236
Zachary Turnera1657a92016-06-08 17:26:39 +0000237 auto PublicS =
238 MappedBlockStream::createIndexedStream(PublicsStreamNum, *this);
239 if (!PublicS)
240 return PublicS.takeError();
241 auto TempPublics =
242 llvm::make_unique<PublicsStream>(*this, std::move(*PublicS));
243 if (auto EC = TempPublics->reload())
Rui Ueyama1f6b6e22016-05-13 21:21:53 +0000244 return std::move(EC);
Zachary Turnera1657a92016-06-08 17:26:39 +0000245 Publics = std::move(TempPublics);
Rui Ueyama1f6b6e22016-05-13 21:21:53 +0000246 }
247 return *Publics;
248}
Rui Ueyama0fcd8262016-05-20 19:55:17 +0000249
250Expected<SymbolStream &> PDBFile::getPDBSymbolStream() {
251 if (!Symbols) {
252 auto DbiS = getPDBDbiStream();
Zachary Turnera1657a92016-06-08 17:26:39 +0000253 if (!DbiS)
254 return DbiS.takeError();
255
Rui Ueyama0fcd8262016-05-20 19:55:17 +0000256 uint32_t SymbolStreamNum = DbiS->getSymRecordStreamIndex();
257
Zachary Turnera1657a92016-06-08 17:26:39 +0000258 auto SymbolS =
259 MappedBlockStream::createIndexedStream(SymbolStreamNum, *this);
260 if (!SymbolS)
261 return SymbolS.takeError();
262 auto TempSymbols = llvm::make_unique<SymbolStream>(std::move(*SymbolS));
263 if (auto EC = TempSymbols->reload())
Rui Ueyama0fcd8262016-05-20 19:55:17 +0000264 return std::move(EC);
Zachary Turnera1657a92016-06-08 17:26:39 +0000265 Symbols = std::move(TempSymbols);
Rui Ueyama0fcd8262016-05-20 19:55:17 +0000266 }
267 return *Symbols;
268}
Zachary Turner3df1bfa2016-06-03 05:52:57 +0000269
270Expected<NameHashTable &> PDBFile::getStringTable() {
271 if (!StringTable || !StringTableStream) {
Zachary Turnera1657a92016-06-08 17:26:39 +0000272 auto IS = getPDBInfoStream();
273 if (!IS)
274 return IS.takeError();
275
276 uint32_t NameStreamIndex = IS->getNamedStreamIndex("/names");
Zachary Turner3df1bfa2016-06-03 05:52:57 +0000277
278 if (NameStreamIndex == 0)
279 return make_error<RawError>(raw_error_code::no_stream);
Zachary Turnerd2b2bfe2016-06-08 00:25:08 +0000280 if (NameStreamIndex >= getNumStreams())
281 return make_error<RawError>(raw_error_code::no_stream);
282
Zachary Turnera1657a92016-06-08 17:26:39 +0000283 auto NS = MappedBlockStream::createIndexedStream(NameStreamIndex, *this);
284 if (!NS)
285 return NS.takeError();
286
Zachary Turnerb84faa82016-06-10 05:10:19 +0000287 StreamReader Reader(**NS);
Zachary Turner3df1bfa2016-06-03 05:52:57 +0000288 auto N = llvm::make_unique<NameHashTable>();
289 if (auto EC = N->load(Reader))
290 return std::move(EC);
291 StringTable = std::move(N);
Zachary Turnera1657a92016-06-08 17:26:39 +0000292 StringTableStream = std::move(*NS);
Zachary Turner3df1bfa2016-06-03 05:52:57 +0000293 }
294 return *StringTable;
295}
Zachary Turner1dc9fd32016-06-14 20:48:36 +0000296
Zachary Turnerfaa554b2016-07-15 22:16:56 +0000297Error PDBFile::setSuperBlock(const msf::SuperBlock *Block) {
298 if (auto EC = msf::validateSuperBlock(*Block))
299 return EC;
Zachary Turnerab58ae82016-06-30 17:43:00 +0000300
301 if (Buffer->getLength() % SB->BlockSize != 0)
302 return make_error<RawError>(raw_error_code::corrupt_file,
303 "File size is not a multiple of block size");
304
Zachary Turnerfaa554b2016-07-15 22:16:56 +0000305 SB = Block;
Zachary Turnerab58ae82016-06-30 17:43:00 +0000306 return Error::success();
307}
Zachary Turner1dc9fd32016-06-14 20:48:36 +0000308
Zachary Turnerab58ae82016-06-30 17:43:00 +0000309Error PDBFile::commit() {
310 StreamWriter Writer(*Buffer);
311
312 if (auto EC = Writer.writeObject(*SB))
313 return EC;
314 Writer.setOffset(getBlockMapOffset());
315 if (auto EC = Writer.writeArray(DirectoryBlocks))
316 return EC;
317
318 auto DS = MappedBlockStream::createDirectoryStream(*this);
319 if (!DS)
320 return DS.takeError();
321 auto DirStream = std::move(*DS);
322 StreamWriter DW(*DirStream);
323 if (auto EC = DW.writeInteger(this->getNumStreams()))
324 return EC;
325
326 if (auto EC = DW.writeArray(StreamSizes))
327 return EC;
328
329 for (const auto &Blocks : StreamMap) {
330 if (auto EC = DW.writeArray(Blocks))
331 return EC;
332 }
333
Zachary Turner8848a7a2016-07-06 18:05:57 +0000334 if (Info) {
335 if (auto EC = Info->commit())
336 return EC;
337 }
338
339 if (Dbi) {
340 if (auto EC = Dbi->commit())
341 return EC;
342 }
343
344 if (Symbols) {
345 if (auto EC = Symbols->commit())
346 return EC;
347 }
348
349 if (Publics) {
350 if (auto EC = Publics->commit())
351 return EC;
352 }
353
354 if (Tpi) {
355 if (auto EC = Tpi->commit())
356 return EC;
357 }
358
359 if (Ipi) {
360 if (auto EC = Ipi->commit())
361 return EC;
362 }
363
Zachary Turnerab58ae82016-06-30 17:43:00 +0000364 return Buffer->commit();
David Majnemer6211b1f2016-07-10 03:34:47 +0000365}