Line data Source code
1 : //===- ArchiveWriter.cpp - ar File Format implementation --------*- 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 : // This file defines the writeArchive function.
11 : //
12 : //===----------------------------------------------------------------------===//
13 :
14 : #include "llvm/Object/ArchiveWriter.h"
15 : #include "llvm/ADT/ArrayRef.h"
16 : #include "llvm/ADT/StringRef.h"
17 : #include "llvm/BinaryFormat/Magic.h"
18 : #include "llvm/IR/LLVMContext.h"
19 : #include "llvm/Object/Archive.h"
20 : #include "llvm/Object/ObjectFile.h"
21 : #include "llvm/Object/SymbolicFile.h"
22 : #include "llvm/Support/EndianStream.h"
23 : #include "llvm/Support/Errc.h"
24 : #include "llvm/Support/ErrorHandling.h"
25 : #include "llvm/Support/Format.h"
26 : #include "llvm/Support/Path.h"
27 : #include "llvm/Support/ToolOutputFile.h"
28 : #include "llvm/Support/raw_ostream.h"
29 :
30 : #include <map>
31 :
32 : #if !defined(_MSC_VER) && !defined(__MINGW32__)
33 : #include <unistd.h>
34 : #else
35 : #include <io.h>
36 : #endif
37 :
38 : using namespace llvm;
39 :
40 659 : NewArchiveMember::NewArchiveMember(MemoryBufferRef BufRef)
41 : : Buf(MemoryBuffer::getMemBuffer(BufRef, false)),
42 659 : MemberName(BufRef.getBufferIdentifier()) {}
43 :
44 : Expected<NewArchiveMember>
45 74 : NewArchiveMember::getOldMember(const object::Archive::Child &OldMember,
46 : bool Deterministic) {
47 74 : Expected<llvm::MemoryBufferRef> BufOrErr = OldMember.getMemoryBufferRef();
48 74 : if (!BufOrErr)
49 : return BufOrErr.takeError();
50 :
51 74 : NewArchiveMember M;
52 : assert(M.IsNew == false);
53 74 : M.Buf = MemoryBuffer::getMemBuffer(*BufOrErr, false);
54 74 : M.MemberName = M.Buf->getBufferIdentifier();
55 74 : if (!Deterministic) {
56 : auto ModTimeOrErr = OldMember.getLastModified();
57 1 : if (!ModTimeOrErr)
58 : return ModTimeOrErr.takeError();
59 1 : M.ModTime = ModTimeOrErr.get();
60 : Expected<unsigned> UIDOrErr = OldMember.getUID();
61 1 : if (!UIDOrErr)
62 : return UIDOrErr.takeError();
63 1 : M.UID = UIDOrErr.get();
64 : Expected<unsigned> GIDOrErr = OldMember.getGID();
65 1 : if (!GIDOrErr)
66 : return GIDOrErr.takeError();
67 1 : M.GID = GIDOrErr.get();
68 : Expected<sys::fs::perms> AccessModeOrErr = OldMember.getAccessMode();
69 1 : if (!AccessModeOrErr)
70 : return AccessModeOrErr.takeError();
71 1 : M.Perms = AccessModeOrErr.get();
72 : }
73 : return std::move(M);
74 : }
75 :
76 305 : Expected<NewArchiveMember> NewArchiveMember::getFile(StringRef FileName,
77 : bool Deterministic) {
78 305 : sys::fs::file_status Status;
79 : int FD;
80 305 : if (auto EC = sys::fs::openFileForRead(FileName, FD))
81 1 : return errorCodeToError(EC);
82 : assert(FD != -1);
83 :
84 304 : if (auto EC = sys::fs::status(FD, Status))
85 0 : return errorCodeToError(EC);
86 :
87 : // Opening a directory doesn't make sense. Let it fail.
88 : // Linux cannot open directories with open(2), although
89 : // cygwin and *bsd can.
90 304 : if (Status.type() == sys::fs::file_type::directory_file)
91 1 : return errorCodeToError(make_error_code(errc::is_a_directory));
92 :
93 : ErrorOr<std::unique_ptr<MemoryBuffer>> MemberBufferOrErr =
94 606 : MemoryBuffer::getOpenFile(FD, FileName, Status.getSize(), false);
95 303 : if (!MemberBufferOrErr)
96 0 : return errorCodeToError(MemberBufferOrErr.getError());
97 :
98 303 : if (close(FD) != 0)
99 0 : return errorCodeToError(std::error_code(errno, std::generic_category()));
100 :
101 : NewArchiveMember M;
102 : M.IsNew = true;
103 : M.Buf = std::move(*MemberBufferOrErr);
104 303 : M.MemberName = M.Buf->getBufferIdentifier();
105 303 : if (!Deterministic) {
106 : M.ModTime = std::chrono::time_point_cast<std::chrono::seconds>(
107 20 : Status.getLastModificationTime());
108 20 : M.UID = Status.getUser();
109 20 : M.GID = Status.getGroup();
110 20 : M.Perms = Status.permissions();
111 : }
112 : return std::move(M);
113 : }
114 :
115 : template <typename T>
116 8646 : static void printWithSpacePadding(raw_ostream &OS, T Data, unsigned Size) {
117 : uint64_t OldPos = OS.tell();
118 3739 : OS << Data;
119 8646 : unsigned SizeSoFar = OS.tell() - OldPos;
120 : assert(SizeSoFar <= Size && "Data doesn't fit in Size");
121 8646 : OS.indent(Size - SizeSoFar);
122 8646 : }
123 279 :
124 : static bool isDarwin(object::Archive::Kind Kind) {
125 279 : return Kind == object::Archive::K_DARWIN ||
126 279 : Kind == object::Archive::K_DARWIN64;
127 : }
128 279 :
129 279 : static bool isBSDLike(object::Archive::Kind Kind) {
130 764 : switch (Kind) {
131 : case object::Archive::K_GNU:
132 764 : case object::Archive::K_GNU64:
133 764 : return false;
134 : case object::Archive::K_BSD:
135 764 : case object::Archive::K_DARWIN:
136 764 : case object::Archive::K_DARWIN64:
137 584 : return true;
138 : case object::Archive::K_COFF:
139 : break;
140 584 : }
141 : llvm_unreachable("not supported for writting");
142 584 : }
143 584 :
144 1348 : template <class T>
145 : static void print(raw_ostream &Out, object::Archive::Kind Kind, T Val) {
146 1348 : support::endian::write(Out, Val,
147 1348 : isBSDLike(Kind) ? support::little : support::big);
148 : }
149 1348 :
150 1348 : static void printRestOfMemberHeader(
151 4323 : raw_ostream &Out, const sys::TimePoint<std::chrono::seconds> &ModTime,
152 : unsigned UID, unsigned GID, unsigned Perms, unsigned Size) {
153 : printWithSpacePadding(Out, sys::toTimeT(ModTime), 12);
154 4323 :
155 : // The format has only 6 chars for uid and gid. Truncate if the provided
156 4323 : // values don't fit.
157 4323 : printWithSpacePadding(Out, UID % 1000000, 6);
158 1348 : printWithSpacePadding(Out, GID % 1000000, 6);
159 :
160 1348 : printWithSpacePadding(Out, format("%o", Perms), 8);
161 1348 : printWithSpacePadding(Out, Size, 10);
162 : Out << "`\n";
163 1348 : }
164 1348 :
165 : static void
166 : printGNUSmallMemberHeader(raw_ostream &Out, StringRef Name,
167 1423 : const sys::TimePoint<std::chrono::seconds> &ModTime,
168 : unsigned UID, unsigned GID, unsigned Perms,
169 : unsigned Size) {
170 : printWithSpacePadding(Out, Twine(Name) + "/", 16);
171 : printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size);
172 954 : }
173 :
174 : static void
175 : printBSDMemberHeader(raw_ostream &Out, uint64_t Pos, StringRef Name,
176 : const sys::TimePoint<std::chrono::seconds> &ModTime,
177 : unsigned UID, unsigned GID, unsigned Perms,
178 : unsigned Size) {
179 : uint64_t PosAfterHeader = Pos + 60 + Name.size();
180 : // Pad so that even 64 bit object files are aligned.
181 : unsigned Pad = OffsetToAlignment(PosAfterHeader, 8);
182 : unsigned NameWithPadding = Name.size() + Pad;
183 0 : printWithSpacePadding(Out, Twine("#1/") + Twine(NameWithPadding), 16);
184 : printRestOfMemberHeader(Out, ModTime, UID, GID, Perms,
185 : NameWithPadding + Size);
186 : Out << Name;
187 4850 : while (Pad--)
188 4850 : Out.write(uint8_t(0));
189 : }
190 4850 :
191 4838 : static bool useStringTable(bool Thin, StringRef Name) {
192 4838 : return Thin || Name.size() >= 16 || Name.contains('/');
193 : }
194 4838 :
195 12 : // Compute the relative path from From to To.
196 12 : static std::string computeRelativePath(StringRef From, StringRef To) {
197 : if (sys::path::is_absolute(From) || sys::path::is_absolute(To))
198 12 : return To;
199 :
200 1348 : StringRef DirFrom = sys::path::parent_path(From);
201 : auto FromI = sys::path::begin(DirFrom);
202 : auto ToI = sys::path::begin(To);
203 1348 : while (*FromI == *ToI) {
204 : ++FromI;
205 : ++ToI;
206 : }
207 1348 :
208 1348 : SmallString<128> Relative;
209 : for (auto FromE = sys::path::end(DirFrom); FromI != FromE; ++FromI)
210 1348 : sys::path::append(Relative, "..");
211 1348 :
212 1348 : for (auto ToE = sys::path::end(To); ToI != ToE; ++ToI)
213 1348 : sys::path::append(Relative, *ToI);
214 :
215 : #ifdef _WIN32
216 533 : // Replace backslashes with slashes so that the path is portable between *nix
217 : // and Windows.
218 : std::replace(Relative.begin(), Relative.end(), '\\', '/');
219 : #endif
220 533 :
221 533 : return Relative.str();
222 533 : }
223 :
224 : static bool is64BitKind(object::Archive::Kind Kind) {
225 51 : switch (Kind) {
226 : case object::Archive::K_GNU:
227 : case object::Archive::K_BSD:
228 : case object::Archive::K_DARWIN:
229 51 : case object::Archive::K_COFF:
230 : return false;
231 51 : case object::Archive::K_DARWIN64:
232 51 : case object::Archive::K_GNU64:
233 51 : return true;
234 51 : }
235 : llvm_unreachable("not supported for writting");
236 51 : }
237 219 :
238 168 : static void addToStringTable(raw_ostream &Out, StringRef ArcName,
239 51 : const NewArchiveMember &M, bool Thin) {
240 : StringRef ID = M.Buf->getBufferIdentifier();
241 : if (Thin) {
242 1200 : if (M.IsNew)
243 : Out << computeRelativePath(ArcName, ID);
244 : else
245 : Out << ID;
246 25 : } else
247 25 : Out << M.MemberName;
248 : Out << "/\n";
249 : }
250 10 :
251 10 : static void printMemberHeader(raw_ostream &Out, uint64_t Pos,
252 10 : raw_ostream &StringTable,
253 3 : object::Archive::Kind Kind, bool Thin,
254 3 : StringRef ArcName, const NewArchiveMember &M,
255 3 : sys::TimePoint<std::chrono::seconds> ModTime,
256 : unsigned Size) {
257 :
258 : if (isBSDLike(Kind))
259 24 : return printBSDMemberHeader(Out, Pos, M.MemberName, ModTime, M.UID, M.GID,
260 4 : M.Perms, Size);
261 : if (!useStringTable(Thin, M.MemberName))
262 32 : return printGNUSmallMemberHeader(Out, M.MemberName, ModTime, M.UID, M.GID,
263 12 : M.Perms, Size);
264 : Out << '/';
265 : uint64_t NamePos = StringTable.tell();
266 : addToStringTable(StringTable, ArcName, M, Thin);
267 : printWithSpacePadding(Out, NamePos, 15);
268 : printRestOfMemberHeader(Out, ModTime, M.UID, M.GID, M.Perms, Size);
269 : }
270 :
271 : namespace {
272 : struct MemberData {
273 : std::vector<unsigned> Symbols;
274 : std::string Header;
275 : StringRef Data;
276 : StringRef Padding;
277 : };
278 : } // namespace
279 :
280 : static MemberData computeStringTable(StringRef Names) {
281 : unsigned Size = Names.size();
282 : unsigned Pad = OffsetToAlignment(Size, 2);
283 : std::string Header;
284 : raw_string_ostream Out(Header);
285 0 : printWithSpacePadding(Out, "//", 48);
286 : printWithSpacePadding(Out, Size + Pad, 10);
287 : Out << "`\n";
288 764 : Out.flush();
289 : return {{}, std::move(Header), Names, Pad ? "\n" : ""};
290 764 : }
291 764 :
292 30 : static sys::TimePoint<std::chrono::seconds> now(bool Deterministic) {
293 50 : using namespace std::chrono;
294 :
295 5 : if (!Deterministic)
296 : return time_point_cast<seconds>(system_clock::now());
297 734 : return sys::TimePoint<seconds>();
298 764 : }
299 764 :
300 : static bool isArchiveSymbol(const object::BasicSymbolRef &S) {
301 1030 : uint32_t Symflags = S.getFlags();
302 : if (Symflags & object::SymbolRef::SF_FormatSpecific)
303 : return false;
304 : if (!(Symflags & object::SymbolRef::SF_Global))
305 : return false;
306 : if (Symflags & object::SymbolRef::SF_Undefined)
307 : return false;
308 : return true;
309 34 : }
310 34 :
311 1730 : static void printNBits(raw_ostream &Out, object::Archive::Kind Kind,
312 232 : uint64_t Val) {
313 232 : if (is64BitKind(Kind))
314 : print<uint64_t>(Out, Kind, Val);
315 : else
316 764 : print<uint32_t>(Out, Kind, Val);
317 764 : }
318 764 :
319 : static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind,
320 : bool Deterministic, ArrayRef<MemberData> Members,
321 : StringRef StringTable) {
322 5008 : // We don't write a symbol table on an archive with no members -- except on
323 : // Darwin, where the linker will abort unless the archive has a symbol table.
324 : if (StringTable.empty() && !isDarwin(Kind))
325 : return;
326 :
327 : unsigned NumSyms = 0;
328 : for (const MemberData &M : Members)
329 : NumSyms += M.Symbols.size();
330 279 :
331 279 : unsigned Size = 0;
332 279 : unsigned OffsetSize = is64BitKind(Kind) ? sizeof(uint64_t) : sizeof(uint32_t);
333 :
334 279 : Size += OffsetSize; // Number of entries
335 279 : if (isBSDLike(Kind))
336 279 : Size += NumSyms * OffsetSize * 2; // Table
337 279 : else
338 : Size += NumSyms * OffsetSize; // Table
339 279 : if (isBSDLike(Kind))
340 : Size += OffsetSize; // byte count
341 : Size += StringTable.size();
342 : // ld64 expects the members to be 8-byte aligned for 64-bit content and at
343 : // least 4-byte aligned for 32-bit content. Opt for the larger encoding
344 : // uniformly.
345 318 : // We do this for all bsd formats because it simplifies aligning members.
346 7 : unsigned Alignment = isBSDLike(Kind) ? 8 : 2;
347 : unsigned Pad = OffsetToAlignment(Size, Alignment);
348 : Size += Pad;
349 :
350 11232 : if (isBSDLike(Kind)) {
351 11232 : const char *Name = is64BitKind(Kind) ? "__.SYMDEF_64" : "__.SYMDEF";
352 11232 : printBSDMemberHeader(Out, Out.tell(), Name, now(Deterministic), 0, 0, 0,
353 : Size);
354 8207 : } else {
355 : const char *Name = is64BitKind(Kind) ? "/SYM64" : "";
356 5807 : printGNUSmallMemberHeader(Out, Name, now(Deterministic), 0, 0, 0, Size);
357 1292 : }
358 :
359 : uint64_t Pos = Out.tell() + Size;
360 :
361 4850 : if (isBSDLike(Kind))
362 : printNBits(Out, Kind, NumSyms * 2 * OffsetSize);
363 : else
364 12 : printNBits(Out, Kind, NumSyms);
365 :
366 4838 : for (const MemberData &M : Members) {
367 4850 : for (unsigned StringOffset : M.Symbols) {
368 : if (isBSDLike(Kind))
369 356 : printNBits(Out, Kind, StringOffset);
370 : printNBits(Out, Kind, Pos); // member offset
371 : }
372 : Pos += M.Header.size() + M.Data.size() + M.Padding.size();
373 : }
374 356 :
375 : if (isBSDLike(Kind))
376 : // byte count of the string table
377 : printNBits(Out, Kind, StringTable.size());
378 1530 : Out << StringTable;
379 2424 :
380 : while (Pad--)
381 : Out.write(uint8_t(0));
382 : }
383 :
384 : static Expected<std::vector<unsigned>>
385 : getSymbols(MemoryBufferRef Buf, raw_ostream &SymNames, bool &HasObject) {
386 17 : std::vector<unsigned> Ret;
387 :
388 301 : // In the scenario when LLVMContext is populated SymbolicFile will contain a
389 : // reference to it, thus SymbolicFile should be destroyed first.
390 17 : LLVMContext Context;
391 318 : std::unique_ptr<object::SymbolicFile> Obj;
392 : if (identify_magic(Buf.getBuffer()) == file_magic::bitcode) {
393 : auto ObjOrErr = object::SymbolicFile::createSymbolicFile(
394 : Buf, file_magic::bitcode, &Context);
395 : if (!ObjOrErr) {
396 : // FIXME: check only for "not an object file" errors.
397 318 : consumeError(ObjOrErr.takeError());
398 318 : return Ret;
399 : }
400 : Obj = std::move(*ObjOrErr);
401 : } else {
402 17 : auto ObjOrErr = object::SymbolicFile::createSymbolicFile(Buf);
403 : if (!ObjOrErr) {
404 : // FIXME: check only for "not an object file" errors.
405 : consumeError(ObjOrErr.takeError());
406 602 : return Ret;
407 : }
408 : Obj = std::move(*ObjOrErr);
409 318 : }
410 :
411 : HasObject = true;
412 17 : for (const object::BasicSymbolRef &S : Obj->symbols()) {
413 : if (!isArchiveSymbol(S))
414 301 : continue;
415 : Ret.push_back(SymNames.tell());
416 1530 : if (auto EC = S.printName(SymNames))
417 5714 : return errorCodeToError(EC);
418 : SymNames << '\0';
419 13 : }
420 4502 : return Ret;
421 : }
422 3636 :
423 : static Expected<std::vector<MemberData>>
424 : computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames,
425 : object::Archive::Kind Kind, bool Thin, StringRef ArcName,
426 : bool Deterministic, ArrayRef<NewArchiveMember> NewMembers) {
427 17 : static char PaddingData[8] = {'\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n'};
428 318 :
429 : // This ignores the symbol table, but we only need the value mod 8 and the
430 455 : // symbol table is aligned to be a multiple of 8 bytes
431 137 : uint64_t Pos = 0;
432 :
433 : std::vector<MemberData> Ret;
434 : bool HasObject = false;
435 1030 :
436 : // UniqueTimestamps is a special case to improve debugging on Darwin:
437 : //
438 : // The Darwin linker does not link debug info into the final
439 : // binary. Instead, it emits entries of type N_OSO in in the output
440 2060 : // binary's symbol table, containing references to the linked-in
441 : // object files. Using that reference, the debugger can read the
442 1030 : // debug data directly from the object files. Alternatively, an
443 : // invocation of 'dsymutil' will link the debug data from the object
444 29 : // files into a dSYM bundle, which can be loaded by the debugger,
445 29 : // instead of the object files.
446 : //
447 0 : // For an object file, the N_OSO entries contain the absolute path
448 0 : // path to the file, and the file's timestamp. For an object
449 : // included in an archive, the path is formatted like
450 : // "/absolute/path/to/archive.a(member.o)", and the timestamp is the
451 : // archive member's timestamp, rather than the archive's timestamp.
452 1001 : //
453 1001 : // However, this doesn't always uniquely identify an object within
454 : // an archive -- an archive file can have multiple entries with the
455 190 : // same filename. (This will happen commonly if the original object
456 95 : // files started in different directories.) The only way they get
457 : // distinguished, then, is via the timestamp. But this process is
458 : // unable to find the correct object file in the archive when there
459 : // are two files of the same name and timestamp.
460 : //
461 935 : // Additionally, timestamp==0 is treated specially, and causes the
462 12167 : // timestamp to be ignored as a match criteria.
463 11232 : //
464 : // That will "usually" work out okay when creating an archive not in
465 4515 : // deterministic timestamp mode, because the objects will probably
466 4515 : // have been created at different timestamps.
467 0 : //
468 : // To ameliorate this problem, in deterministic archive mode (which
469 : // is the default), on Darwin we will emit a unique non-zero
470 : // timestamp for each entry with a duplicated name. This is still
471 : // deterministic: the only thing affecting that timestamp is the
472 : // order of the files in the resultant archive.
473 : //
474 365 : // See also the functions that handle the lookup:
475 : // in lldb: ObjectContainerBSDArchive::Archive::FindObject()
476 : // in llvm/tools/dsymutil: BinaryHolder::GetArchiveMemberBuffers().
477 : bool UniqueTimestamps = Deterministic && isDarwin(Kind);
478 : std::map<StringRef, unsigned> FilenameCount;
479 : if (UniqueTimestamps) {
480 : for (const NewArchiveMember &M : NewMembers)
481 : FilenameCount[M.MemberName]++;
482 : for (auto &Entry : FilenameCount)
483 365 : Entry.second = Entry.second > 1 ? 1 : 0;
484 365 : }
485 :
486 : for (const NewArchiveMember &M : NewMembers) {
487 : std::string Header;
488 : raw_string_ostream Out(Header);
489 :
490 : MemoryBufferRef Buf = M.Buf->getMemBufferRef();
491 : StringRef Data = Thin ? "" : Buf.getBuffer();
492 :
493 : // ld64 expects the members to be 8-byte aligned for 64-bit content and at
494 : // least 4-byte aligned for 32-bit content. Opt for the larger encoding
495 : // uniformly. This matches the behaviour with cctools and ensures that ld64
496 : // is happy with archives that we generate.
497 : unsigned MemberPadding =
498 : isDarwin(Kind) ? OffsetToAlignment(Data.size(), 8) : 0;
499 : unsigned TailPadding = OffsetToAlignment(Data.size() + MemberPadding, 2);
500 : StringRef Padding = StringRef(PaddingData, MemberPadding + TailPadding);
501 :
502 : sys::TimePoint<std::chrono::seconds> ModTime;
503 : if (UniqueTimestamps)
504 : // Increment timestamp for each file of a given name.
505 : ModTime = sys::toTimePoint(FilenameCount[M.MemberName]++);
506 : else
507 : ModTime = M.ModTime;
508 : printMemberHeader(Out, Pos, StringTable, Kind, Thin, ArcName, M, ModTime,
509 : Buf.getBufferSize() + MemberPadding);
510 : Out.flush();
511 :
512 : Expected<std::vector<unsigned>> Symbols =
513 : getSymbols(Buf, SymNames, HasObject);
514 : if (auto E = Symbols.takeError())
515 : return std::move(E);
516 :
517 : Pos += Header.size() + Data.size() + Padding.size();
518 : Ret.push_back({std::move(*Symbols), std::move(Header), Data, Padding});
519 : }
520 : // If there are no symbols, emit an empty symbol table, to satisfy Solaris
521 : // tools, older versions of which expect a symbol table in a non-empty
522 : // archive, regardless of whether there are any symbols in it.
523 : if (HasObject && SymNames.tell() == 0)
524 : SymNames << '\0' << '\0' << '\0';
525 : return Ret;
526 : }
527 365 :
528 : Error llvm::writeArchive(StringRef ArcName,
529 365 : ArrayRef<NewArchiveMember> NewMembers,
530 18 : bool WriteSymtab, object::Archive::Kind Kind,
531 10 : bool Deterministic, bool Thin,
532 17 : std::unique_ptr<MemoryBuffer> OldArchiveBuf) {
533 17 : assert((!Thin || !isBSDLike(Kind)) && "Only the gnu format has a thin mode");
534 :
535 : SmallString<0> SymNamesBuf;
536 1395 : raw_svector_ostream SymNames(SymNamesBuf);
537 : SmallString<0> StringTableBuf;
538 1030 : raw_svector_ostream StringTable(StringTableBuf);
539 :
540 1030 : Expected<std::vector<MemberData>> DataOrErr = computeMemberData(
541 1030 : StringTable, SymNames, Kind, Thin, ArcName, Deterministic, NewMembers);
542 : if (Error E = DataOrErr.takeError())
543 : return E;
544 : std::vector<MemberData> &Data = *DataOrErr;
545 :
546 : if (!StringTableBuf.empty())
547 : Data.insert(Data.begin(), computeStringTable(StringTableBuf));
548 1030 :
549 1030 : // We would like to detect if we need to switch to a 64-bit symbol table.
550 1030 : if (WriteSymtab) {
551 : uint64_t MaxOffset = 0;
552 : uint64_t LastOffset = MaxOffset;
553 1030 : for (const auto &M : Data) {
554 : // Record the start of the member's offset
555 10 : LastOffset = MaxOffset;
556 : // Account for the size of each part associated with the member.
557 1020 : MaxOffset += M.Header.size() + M.Data.size() + M.Padding.size();
558 1030 : // We assume 32-bit symbols to see if 32-bit symbols are possible or not.
559 : MaxOffset += M.Symbols.size() * 4;
560 : }
561 :
562 : // The SYM64 format is used when an archive's member offsets are larger than
563 1030 : // 32-bits can hold. The need for this shift in format is detected by
564 1030 : // writeArchive. To test this we need to generate a file with a member that
565 : // has an offset larger than 32-bits but this demands a very slow test. To
566 : // speed the test up we use this environment variable to pretend like the
567 1030 : // cutoff happens before 32-bits and instead happens at some much smaller
568 1030 : // value.
569 : const char *Sym64Env = std::getenv("SYM64_THRESHOLD");
570 : int Sym64Threshold = 32;
571 : if (Sym64Env)
572 : StringRef(Sym64Env).getAsInteger(10, Sym64Threshold);
573 687 :
574 : // If LastOffset isn't going to fit in a 32-bit varible we need to switch
575 : // to 64-bit. Note that the file can be larger than 4GB as long as the last
576 : // member starts before the 4GB offset.
577 : if (LastOffset >= (1ULL << Sym64Threshold)) {
578 365 : if (Kind == object::Archive::K_DARWIN)
579 : Kind = object::Archive::K_DARWIN64;
580 : else
581 : Kind = object::Archive::K_GNU64;
582 : }
583 : }
584 :
585 : Expected<sys::fs::TempFile> Temp =
586 : sys::fs::TempFile::create(ArcName + ".temp-archive-%%%%%%%.a");
587 : if (!Temp)
588 : return Temp.takeError();
589 :
590 : raw_fd_ostream Out(Temp->FD, false);
591 730 : if (Thin)
592 365 : Out << "!<thin>\n";
593 : else
594 : Out << "!<arch>\n";
595 :
596 365 : if (WriteSymtab)
597 558 : writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf);
598 :
599 : for (const MemberData &M : Data)
600 365 : Out << M.Header << M.Data << M.Padding;
601 :
602 : Out.flush();
603 1645 :
604 : // At this point, we no longer need whatever backing memory
605 : // was used to generate the NewMembers. On Windows, this buffer
606 : // could be a mapped view of the file we want to replace (if
607 2578 : // we're updating an existing archive, say). In that case, the
608 : // rename would still succeed, but it would leave behind a
609 2578 : // temporary file (actually the original file renamed) because
610 : // a file cannot be deleted while there's a handle open on it,
611 : // only renamed. So by freeing this buffer, this ensures that
612 : // the last open handle on the destination file, if any, is
613 : // closed before we attempt to rename.
614 : OldArchiveBuf.reset();
615 :
616 : return Temp->keep(ArcName);
617 : }
|