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