Error writeArchive(StringRef ArcName, ArrayRef<NewArchiveMember> NewMembers, bool WriteSymtab, object::Archive::Kind Kind, bool Deterministic, bool Thin, std::unique_ptr<MemoryBuffer> OldArchiveBuf) { assert((!Thin || !isBSDLike(Kind)) && "Only the gnu format has a thin mode"); SmallString<0> SymNamesBuf; raw_svector_ostream SymNames(SymNamesBuf); SmallString<0> StringTableBuf; raw_svector_ostream StringTable(StringTableBuf); Expected<std::vector<MemberData>> DataOrErr = computeMemberData( StringTable, SymNames, Kind, Thin, Deterministic, NewMembers); if (Error E = DataOrErr.takeError()) return E; std::vector<MemberData> &Data = *DataOrErr; if (!StringTableBuf.empty()) Data.insert(Data.begin(), computeStringTable(StringTableBuf)); // We would like to detect if we need to switch to a 64-bit symbol table. if (WriteSymtab) { uint64_t MaxOffset = 0; uint64_t LastOffset = MaxOffset; for (const auto &M : Data) { // Record the start of the member's offset LastOffset = MaxOffset; // Account for the size of each part associated with the member. MaxOffset += M.Header.size() + M.Data.size() + M.Padding.size(); // We assume 32-bit symbols to see if 32-bit symbols are possible or not. MaxOffset += M.Symbols.size() * 4; } // The SYM64 format is used when an archive's member offsets are larger than // 32-bits can hold. The need for this shift in format is detected by // writeArchive. To test this we need to generate a file with a member that // has an offset larger than 32-bits but this demands a very slow test. To // speed the test up we use this environment variable to pretend like the // cutoff happens before 32-bits and instead happens at some much smaller // value. const char *Sym64Env = std::getenv("SYM64_THRESHOLD"); int Sym64Threshold = 32; if (Sym64Env) StringRef(Sym64Env).getAsInteger(10, Sym64Threshold); // If LastOffset isn't going to fit in a 32-bit varible we need to switch // to 64-bit. Note that the file can be larger than 4GB as long as the last // member starts before the 4GB offset. if (LastOffset >= (1ULL << Sym64Threshold)) { if (Kind == object::Archive::K_DARWIN) Kind = object::Archive::K_DARWIN64; else Kind = object::Archive::K_GNU64; } } Expected<sys::fs::TempFile> Temp = sys::fs::TempFile::create(ArcName + ".temp-archive-%%%%%%%.a"); if (!Temp) return Temp.takeError(); raw_fd_ostream Out(Temp->FD, false); if (Thin) Out << "!<thin>\n"; else Out << "!<arch>\n"; if (WriteSymtab) writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf); for (const MemberData &M : Data) Out << M.Header << M.Data << M.Padding; Out.flush(); // At this point, we no longer need whatever backing memory // was used to generate the NewMembers. On Windows, this buffer // could be a mapped view of the file we want to replace (if // we're updating an existing archive, say). In that case, the // rename would still succeed, but it would leave behind a // temporary file (actually the original file renamed) because // a file cannot be deleted while there's a handle open on it, // only renamed. So by freeing this buffer, this ensures that // the last open handle on the destination file, if any, is // closed before we attempt to rename. OldArchiveBuf.reset(); return Temp->keep(ArcName); }