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 uint32_t Symflags = S.getFlags(); 268 if (Symflags & object::SymbolRef::SF_FormatSpecific) 269 return false; 270 if (!(Symflags & object::SymbolRef::SF_Global)) 271 return false; 272 if (Symflags & object::SymbolRef::SF_Undefined) 273 return false; 274 return true; 275 } 276 277 static void printNBits(raw_ostream &Out, object::Archive::Kind Kind, 278 uint64_t Val) { 279 if (is64BitKind(Kind)) 280 print<uint64_t>(Out, Kind, Val); 281 else 282 print<uint32_t>(Out, Kind, Val); 283 } 284 285 static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind, 286 bool Deterministic, ArrayRef<MemberData> Members, 287 StringRef StringTable) { 288 // We don't write a symbol table on an archive with no members -- except on 289 // Darwin, where the linker will abort unless the archive has a symbol table. 290 if (StringTable.empty() && !isDarwin(Kind)) 291 return; 292 293 unsigned NumSyms = 0; 294 for (const MemberData &M : Members) 295 NumSyms += M.Symbols.size(); 296 297 unsigned Size = 0; 298 unsigned OffsetSize = is64BitKind(Kind) ? sizeof(uint64_t) : sizeof(uint32_t); 299 300 Size += OffsetSize; // Number of entries 301 if (isBSDLike(Kind)) 302 Size += NumSyms * OffsetSize * 2; // Table 303 else 304 Size += NumSyms * OffsetSize; // Table 305 if (isBSDLike(Kind)) 306 Size += OffsetSize; // byte count 307 Size += StringTable.size(); 308 // ld64 expects the members to be 8-byte aligned for 64-bit content and at 309 // least 4-byte aligned for 32-bit content. Opt for the larger encoding 310 // uniformly. 311 // We do this for all bsd formats because it simplifies aligning members. 312 const Align Alignment(isBSDLike(Kind) ? 8 : 2); 313 unsigned Pad = offsetToAlignment(Size, Alignment); 314 Size += Pad; 315 316 if (isBSDLike(Kind)) { 317 const char *Name = is64BitKind(Kind) ? "__.SYMDEF_64" : "__.SYMDEF"; 318 printBSDMemberHeader(Out, Out.tell(), Name, now(Deterministic), 0, 0, 0, 319 Size); 320 } else { 321 const char *Name = is64BitKind(Kind) ? "/SYM64" : ""; 322 printGNUSmallMemberHeader(Out, Name, now(Deterministic), 0, 0, 0, Size); 323 } 324 325 uint64_t Pos = Out.tell() + Size; 326 327 if (isBSDLike(Kind)) 328 printNBits(Out, Kind, NumSyms * 2 * OffsetSize); 329 else 330 printNBits(Out, Kind, NumSyms); 331 332 for (const MemberData &M : Members) { 333 for (unsigned StringOffset : M.Symbols) { 334 if (isBSDLike(Kind)) 335 printNBits(Out, Kind, StringOffset); 336 printNBits(Out, Kind, Pos); // member offset 337 } 338 Pos += M.Header.size() + M.Data.size() + M.Padding.size(); 339 } 340 341 if (isBSDLike(Kind)) 342 // byte count of the string table 343 printNBits(Out, Kind, StringTable.size()); 344 Out << StringTable; 345 346 while (Pad--) 347 Out.write(uint8_t(0)); 348 } 349 350 static Expected<std::vector<unsigned>> 351 getSymbols(MemoryBufferRef Buf, raw_ostream &SymNames, bool &HasObject) { 352 std::vector<unsigned> Ret; 353 354 // In the scenario when LLVMContext is populated SymbolicFile will contain a 355 // reference to it, thus SymbolicFile should be destroyed first. 356 LLVMContext Context; 357 std::unique_ptr<object::SymbolicFile> Obj; 358 if (identify_magic(Buf.getBuffer()) == file_magic::bitcode) { 359 auto ObjOrErr = object::SymbolicFile::createSymbolicFile( 360 Buf, file_magic::bitcode, &Context); 361 if (!ObjOrErr) { 362 // FIXME: check only for "not an object file" errors. 363 consumeError(ObjOrErr.takeError()); 364 return Ret; 365 } 366 Obj = std::move(*ObjOrErr); 367 } else { 368 auto ObjOrErr = object::SymbolicFile::createSymbolicFile(Buf); 369 if (!ObjOrErr) { 370 // FIXME: check only for "not an object file" errors. 371 consumeError(ObjOrErr.takeError()); 372 return Ret; 373 } 374 Obj = std::move(*ObjOrErr); 375 } 376 377 HasObject = true; 378 for (const object::BasicSymbolRef &S : Obj->symbols()) { 379 if (!isArchiveSymbol(S)) 380 continue; 381 Ret.push_back(SymNames.tell()); 382 if (Error E = S.printName(SymNames)) 383 return std::move(E); 384 SymNames << '\0'; 385 } 386 return Ret; 387 } 388 389 static Expected<std::vector<MemberData>> 390 computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames, 391 object::Archive::Kind Kind, bool Thin, bool Deterministic, 392 ArrayRef<NewArchiveMember> NewMembers) { 393 static char PaddingData[8] = {'\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n'}; 394 395 // This ignores the symbol table, but we only need the value mod 8 and the 396 // symbol table is aligned to be a multiple of 8 bytes 397 uint64_t Pos = 0; 398 399 std::vector<MemberData> Ret; 400 bool HasObject = false; 401 402 // Deduplicate long member names in the string table and reuse earlier name 403 // offsets. This especially saves space for COFF Import libraries where all 404 // members have the same name. 405 StringMap<uint64_t> MemberNames; 406 407 // UniqueTimestamps is a special case to improve debugging on Darwin: 408 // 409 // The Darwin linker does not link debug info into the final 410 // binary. Instead, it emits entries of type N_OSO in in the output 411 // binary's symbol table, containing references to the linked-in 412 // object files. Using that reference, the debugger can read the 413 // debug data directly from the object files. Alternatively, an 414 // invocation of 'dsymutil' will link the debug data from the object 415 // files into a dSYM bundle, which can be loaded by the debugger, 416 // instead of the object files. 417 // 418 // For an object file, the N_OSO entries contain the absolute path 419 // path to the file, and the file's timestamp. For an object 420 // included in an archive, the path is formatted like 421 // "/absolute/path/to/archive.a(member.o)", and the timestamp is the 422 // archive member's timestamp, rather than the archive's timestamp. 423 // 424 // However, this doesn't always uniquely identify an object within 425 // an archive -- an archive file can have multiple entries with the 426 // same filename. (This will happen commonly if the original object 427 // files started in different directories.) The only way they get 428 // distinguished, then, is via the timestamp. But this process is 429 // unable to find the correct object file in the archive when there 430 // are two files of the same name and timestamp. 431 // 432 // Additionally, timestamp==0 is treated specially, and causes the 433 // timestamp to be ignored as a match criteria. 434 // 435 // That will "usually" work out okay when creating an archive not in 436 // deterministic timestamp mode, because the objects will probably 437 // have been created at different timestamps. 438 // 439 // To ameliorate this problem, in deterministic archive mode (which 440 // is the default), on Darwin we will emit a unique non-zero 441 // timestamp for each entry with a duplicated name. This is still 442 // deterministic: the only thing affecting that timestamp is the 443 // order of the files in the resultant archive. 444 // 445 // See also the functions that handle the lookup: 446 // in lldb: ObjectContainerBSDArchive::Archive::FindObject() 447 // in llvm/tools/dsymutil: BinaryHolder::GetArchiveMemberBuffers(). 448 bool UniqueTimestamps = Deterministic && isDarwin(Kind); 449 std::map<StringRef, unsigned> FilenameCount; 450 if (UniqueTimestamps) { 451 for (const NewArchiveMember &M : NewMembers) 452 FilenameCount[M.MemberName]++; 453 for (auto &Entry : FilenameCount) 454 Entry.second = Entry.second > 1 ? 1 : 0; 455 } 456 457 for (const NewArchiveMember &M : NewMembers) { 458 std::string Header; 459 raw_string_ostream Out(Header); 460 461 MemoryBufferRef Buf = M.Buf->getMemBufferRef(); 462 StringRef Data = Thin ? "" : Buf.getBuffer(); 463 464 // ld64 expects the members to be 8-byte aligned for 64-bit content and at 465 // least 4-byte aligned for 32-bit content. Opt for the larger encoding 466 // uniformly. This matches the behaviour with cctools and ensures that ld64 467 // is happy with archives that we generate. 468 unsigned MemberPadding = 469 isDarwin(Kind) ? offsetToAlignment(Data.size(), Align(8)) : 0; 470 unsigned TailPadding = 471 offsetToAlignment(Data.size() + MemberPadding, Align(2)); 472 StringRef Padding = StringRef(PaddingData, MemberPadding + TailPadding); 473 474 sys::TimePoint<std::chrono::seconds> ModTime; 475 if (UniqueTimestamps) 476 // Increment timestamp for each file of a given name. 477 ModTime = sys::toTimePoint(FilenameCount[M.MemberName]++); 478 else 479 ModTime = M.ModTime; 480 481 uint64_t Size = Buf.getBufferSize() + MemberPadding; 482 if (Size > object::Archive::MaxMemberSize) { 483 std::string StringMsg = 484 "File " + M.MemberName.str() + " exceeds size limit"; 485 return make_error<object::GenericBinaryError>( 486 std::move(StringMsg), object::object_error::parse_failed); 487 } 488 489 printMemberHeader(Out, Pos, StringTable, MemberNames, Kind, Thin, M, 490 ModTime, Size); 491 Out.flush(); 492 493 Expected<std::vector<unsigned>> Symbols = 494 getSymbols(Buf, SymNames, HasObject); 495 if (auto E = Symbols.takeError()) 496 return std::move(E); 497 498 Pos += Header.size() + Data.size() + Padding.size(); 499 Ret.push_back({std::move(*Symbols), std::move(Header), Data, Padding}); 500 } 501 // If there are no symbols, emit an empty symbol table, to satisfy Solaris 502 // tools, older versions of which expect a symbol table in a non-empty 503 // archive, regardless of whether there are any symbols in it. 504 if (HasObject && SymNames.tell() == 0) 505 SymNames << '\0' << '\0' << '\0'; 506 return Ret; 507 } 508 509 namespace llvm { 510 511 static ErrorOr<SmallString<128>> canonicalizePath(StringRef P) { 512 SmallString<128> Ret = P; 513 std::error_code Err = sys::fs::make_absolute(Ret); 514 if (Err) 515 return Err; 516 sys::path::remove_dots(Ret, /*removedotdot*/ true); 517 return Ret; 518 } 519 520 // Compute the relative path from From to To. 521 Expected<std::string> computeArchiveRelativePath(StringRef From, StringRef To) { 522 ErrorOr<SmallString<128>> PathToOrErr = canonicalizePath(To); 523 ErrorOr<SmallString<128>> DirFromOrErr = canonicalizePath(From); 524 if (!PathToOrErr || !DirFromOrErr) 525 return errorCodeToError(std::error_code(errno, std::generic_category())); 526 527 const SmallString<128> &PathTo = *PathToOrErr; 528 const SmallString<128> &DirFrom = sys::path::parent_path(*DirFromOrErr); 529 530 // Can't construct a relative path between different roots 531 if (sys::path::root_name(PathTo) != sys::path::root_name(DirFrom)) 532 return sys::path::convert_to_slash(PathTo); 533 534 // Skip common prefixes 535 auto FromTo = 536 std::mismatch(sys::path::begin(DirFrom), sys::path::end(DirFrom), 537 sys::path::begin(PathTo)); 538 auto FromI = FromTo.first; 539 auto ToI = FromTo.second; 540 541 // Construct relative path 542 SmallString<128> Relative; 543 for (auto FromE = sys::path::end(DirFrom); FromI != FromE; ++FromI) 544 sys::path::append(Relative, sys::path::Style::posix, ".."); 545 546 for (auto ToE = sys::path::end(PathTo); ToI != ToE; ++ToI) 547 sys::path::append(Relative, sys::path::Style::posix, *ToI); 548 549 return std::string(Relative.str()); 550 } 551 552 Error writeArchive(StringRef ArcName, ArrayRef<NewArchiveMember> NewMembers, 553 bool WriteSymtab, object::Archive::Kind Kind, 554 bool Deterministic, bool Thin, 555 std::unique_ptr<MemoryBuffer> OldArchiveBuf) { 556 assert((!Thin || !isBSDLike(Kind)) && "Only the gnu format has a thin mode"); 557 558 SmallString<0> SymNamesBuf; 559 raw_svector_ostream SymNames(SymNamesBuf); 560 SmallString<0> StringTableBuf; 561 raw_svector_ostream StringTable(StringTableBuf); 562 563 Expected<std::vector<MemberData>> DataOrErr = computeMemberData( 564 StringTable, SymNames, Kind, Thin, Deterministic, NewMembers); 565 if (Error E = DataOrErr.takeError()) 566 return E; 567 std::vector<MemberData> &Data = *DataOrErr; 568 569 if (!StringTableBuf.empty()) 570 Data.insert(Data.begin(), computeStringTable(StringTableBuf)); 571 572 // We would like to detect if we need to switch to a 64-bit symbol table. 573 if (WriteSymtab) { 574 uint64_t MaxOffset = 0; 575 uint64_t LastOffset = MaxOffset; 576 for (const auto &M : Data) { 577 // Record the start of the member's offset 578 LastOffset = MaxOffset; 579 // Account for the size of each part associated with the member. 580 MaxOffset += M.Header.size() + M.Data.size() + M.Padding.size(); 581 // We assume 32-bit symbols to see if 32-bit symbols are possible or not. 582 MaxOffset += M.Symbols.size() * 4; 583 } 584 585 // The SYM64 format is used when an archive's member offsets are larger than 586 // 32-bits can hold. The need for this shift in format is detected by 587 // writeArchive. To test this we need to generate a file with a member that 588 // has an offset larger than 32-bits but this demands a very slow test. To 589 // speed the test up we use this environment variable to pretend like the 590 // cutoff happens before 32-bits and instead happens at some much smaller 591 // value. 592 const char *Sym64Env = std::getenv("SYM64_THRESHOLD"); 593 int Sym64Threshold = 32; 594 if (Sym64Env) 595 StringRef(Sym64Env).getAsInteger(10, Sym64Threshold); 596 597 // If LastOffset isn't going to fit in a 32-bit varible we need to switch 598 // to 64-bit. Note that the file can be larger than 4GB as long as the last 599 // member starts before the 4GB offset. 600 if (LastOffset >= (1ULL << Sym64Threshold)) { 601 if (Kind == object::Archive::K_DARWIN) 602 Kind = object::Archive::K_DARWIN64; 603 else 604 Kind = object::Archive::K_GNU64; 605 } 606 } 607 608 Expected<sys::fs::TempFile> Temp = 609 sys::fs::TempFile::create(ArcName + ".temp-archive-%%%%%%%.a"); 610 if (!Temp) 611 return Temp.takeError(); 612 613 raw_fd_ostream Out(Temp->FD, false); 614 if (Thin) 615 Out << "!<thin>\n"; 616 else 617 Out << "!<arch>\n"; 618 619 if (WriteSymtab) 620 writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf); 621 622 for (const MemberData &M : Data) 623 Out << M.Header << M.Data << M.Padding; 624 625 Out.flush(); 626 627 // At this point, we no longer need whatever backing memory 628 // was used to generate the NewMembers. On Windows, this buffer 629 // could be a mapped view of the file we want to replace (if 630 // we're updating an existing archive, say). In that case, the 631 // rename would still succeed, but it would leave behind a 632 // temporary file (actually the original file renamed) because 633 // a file cannot be deleted while there's a handle open on it, 634 // only renamed. So by freeing this buffer, this ensures that 635 // the last open handle on the destination file, if any, is 636 // closed before we attempt to rename. 637 OldArchiveBuf.reset(); 638 639 return Temp->keep(ArcName); 640 } 641 642 } // namespace llvm 643