#include "llvm/Object/ArchiveWriter.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/Error.h"
#include "llvm/Object/IRObjectFile.h"
#include "llvm/Object/MachO.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/SymbolicFile.h"
#include "llvm/Object/XCOFFObjectFile.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SmallVectorMemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include <map>
#if !defined(_MSC_VER) && !defined(__MINGW32__)
#include <unistd.h>
#else
#include <io.h>
#endif
using namespace llvm;
NewArchiveMember::NewArchiveMember(MemoryBufferRef BufRef)
: Buf(MemoryBuffer::getMemBuffer(BufRef, false)),
MemberName(BufRef.getBufferIdentifier()) {}
object::Archive::Kind NewArchiveMember::detectKindFromObject() const {
auto MemBufferRef = this->Buf->getMemBufferRef();
Expected<std::unique_ptr<object::ObjectFile>> OptionalObject =
object::ObjectFile::createObjectFile(MemBufferRef);
if (OptionalObject)
return isa<object::MachOObjectFile>(**OptionalObject)
? object::Archive::K_DARWIN
: (isa<object::XCOFFObjectFile>(**OptionalObject)
? object::Archive::K_AIXBIG
: object::Archive::K_GNU);
consumeError(OptionalObject.takeError());
LLVMContext Context;
if (identify_magic(MemBufferRef.getBuffer()) == file_magic::bitcode) {
if (auto ObjOrErr = object::SymbolicFile::createSymbolicFile(
MemBufferRef, file_magic::bitcode, &Context)) {
auto &IRObject = cast<object::IRObjectFile>(**ObjOrErr);
return Triple(IRObject.getTargetTriple()).isOSDarwin()
? object::Archive::K_DARWIN
: object::Archive::K_GNU;
} else {
consumeError(ObjOrErr.takeError());
}
}
return object::Archive::getDefaultKindForHost();
}
Expected<NewArchiveMember>
NewArchiveMember::getOldMember(const object::Archive::Child &OldMember,
bool Deterministic) {
Expected<llvm::MemoryBufferRef> BufOrErr = OldMember.getMemoryBufferRef();
if (!BufOrErr)
return BufOrErr.takeError();
NewArchiveMember M;
M.Buf = MemoryBuffer::getMemBuffer(*BufOrErr, false);
M.MemberName = M.Buf->getBufferIdentifier();
if (!Deterministic) {
auto ModTimeOrErr = OldMember.getLastModified();
if (!ModTimeOrErr)
return ModTimeOrErr.takeError();
M.ModTime = ModTimeOrErr.get();
Expected<unsigned> UIDOrErr = OldMember.getUID();
if (!UIDOrErr)
return UIDOrErr.takeError();
M.UID = UIDOrErr.get();
Expected<unsigned> GIDOrErr = OldMember.getGID();
if (!GIDOrErr)
return GIDOrErr.takeError();
M.GID = GIDOrErr.get();
Expected<sys::fs::perms> AccessModeOrErr = OldMember.getAccessMode();
if (!AccessModeOrErr)
return AccessModeOrErr.takeError();
M.Perms = AccessModeOrErr.get();
}
return std::move(M);
}
Expected<NewArchiveMember> NewArchiveMember::getFile(StringRef FileName,
bool Deterministic) {
sys::fs::file_status Status;
auto FDOrErr = sys::fs::openNativeFileForRead(FileName);
if (!FDOrErr)
return FDOrErr.takeError();
sys::fs::file_t FD = *FDOrErr;
assert(FD != sys::fs::kInvalidFile);
if (auto EC = sys::fs::status(FD, Status))
return errorCodeToError(EC);
if (Status.type() == sys::fs::file_type::directory_file)
return errorCodeToError(make_error_code(errc::is_a_directory));
ErrorOr<std::unique_ptr<MemoryBuffer>> MemberBufferOrErr =
MemoryBuffer::getOpenFile(FD, FileName, Status.getSize(), false);
if (!MemberBufferOrErr)
return errorCodeToError(MemberBufferOrErr.getError());
if (auto EC = sys::fs::closeFile(FD))
return errorCodeToError(EC);
NewArchiveMember M;
M.Buf = std::move(*MemberBufferOrErr);
M.MemberName = M.Buf->getBufferIdentifier();
if (!Deterministic) {
M.ModTime = std::chrono::time_point_cast<std::chrono::seconds>(
Status.getLastModificationTime());
M.UID = Status.getUser();
M.GID = Status.getGroup();
M.Perms = Status.permissions();
}
return std::move(M);
}
template <typename T>
static void printWithSpacePadding(raw_ostream &OS, T Data, unsigned Size) {
uint64_t OldPos = OS.tell();
OS << Data;
unsigned SizeSoFar = OS.tell() - OldPos;
assert(SizeSoFar <= Size && "Data doesn't fit in Size");
OS.indent(Size - SizeSoFar);
}
static bool isDarwin(object::Archive::Kind Kind) {
return Kind == object::Archive::K_DARWIN ||
Kind == object::Archive::K_DARWIN64;
}
static bool isAIXBigArchive(object::Archive::Kind Kind) {
return Kind == object::Archive::K_AIXBIG;
}
static bool isBSDLike(object::Archive::Kind Kind) {
switch (Kind) {
case object::Archive::K_GNU:
case object::Archive::K_GNU64:
case object::Archive::K_AIXBIG:
return false;
case object::Archive::K_BSD:
case object::Archive::K_DARWIN:
case object::Archive::K_DARWIN64:
return true;
case object::Archive::K_COFF:
break;
}
llvm_unreachable("not supported for writting");
}
template <class T>
static void print(raw_ostream &Out, object::Archive::Kind Kind, T Val) {
support::endian::write(Out, Val,
isBSDLike(Kind) ? support::little : support::big);
}
static void printRestOfMemberHeader(
raw_ostream &Out, const sys::TimePoint<std::chrono::seconds> &ModTime,
unsigned UID, unsigned GID, unsigned Perms, uint64_t Size) {
printWithSpacePadding(Out, sys::toTimeT(ModTime), 12);
printWithSpacePadding(Out, UID % 1000000, 6);
printWithSpacePadding(Out, GID % 1000000, 6);
printWithSpacePadding(Out, format("%o", Perms), 8);
printWithSpacePadding(Out, Size, 10);
Out << "`\n";
}
static void
printGNUSmallMemberHeader(raw_ostream &Out, StringRef Name,
const sys::TimePoint<std::chrono::seconds> &ModTime,
unsigned UID, unsigned GID, unsigned Perms,
uint64_t Size) {
printWithSpacePadding(Out, Twine(Name) + "/", 16);
printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size);
}
static void
printBSDMemberHeader(raw_ostream &Out, uint64_t Pos, StringRef Name,
const sys::TimePoint<std::chrono::seconds> &ModTime,
unsigned UID, unsigned GID, unsigned Perms, uint64_t Size) {
uint64_t PosAfterHeader = Pos + 60 + Name.size();
unsigned Pad = offsetToAlignment(PosAfterHeader, Align(8));
unsigned NameWithPadding = Name.size() + Pad;
printWithSpacePadding(Out, Twine("#1/") + Twine(NameWithPadding), 16);
printRestOfMemberHeader(Out, ModTime, UID, GID, Perms,
NameWithPadding + Size);
Out << Name;
while (Pad--)
Out.write(uint8_t(0));
}
static void
printBigArchiveMemberHeader(raw_ostream &Out, StringRef Name,
const sys::TimePoint<std::chrono::seconds> &ModTime,
unsigned UID, unsigned GID, unsigned Perms,
uint64_t Size, unsigned PrevOffset,
unsigned NextOffset) {
unsigned NameLen = Name.size();
printWithSpacePadding(Out, Size, 20); printWithSpacePadding(Out, NextOffset, 20); printWithSpacePadding(Out, PrevOffset, 20); printWithSpacePadding(Out, sys::toTimeT(ModTime), 12); printWithSpacePadding(Out, UID % 1000000000000, 12); printWithSpacePadding(Out, GID % 1000000000000, 12); printWithSpacePadding(Out, format("%o", Perms), 12); printWithSpacePadding(Out, NameLen, 4); if (NameLen) {
printWithSpacePadding(Out, Name, NameLen); if (NameLen % 2)
Out.write(uint8_t(0)); }
Out << "`\n"; }
static bool useStringTable(bool Thin, StringRef Name) {
return Thin || Name.size() >= 16 || Name.contains('/');
}
static bool is64BitKind(object::Archive::Kind Kind) {
switch (Kind) {
case object::Archive::K_GNU:
case object::Archive::K_BSD:
case object::Archive::K_DARWIN:
case object::Archive::K_COFF:
return false;
case object::Archive::K_AIXBIG:
case object::Archive::K_DARWIN64:
case object::Archive::K_GNU64:
return true;
}
llvm_unreachable("not supported for writting");
}
static void
printMemberHeader(raw_ostream &Out, uint64_t Pos, raw_ostream &StringTable,
StringMap<uint64_t> &MemberNames, object::Archive::Kind Kind,
bool Thin, const NewArchiveMember &M,
sys::TimePoint<std::chrono::seconds> ModTime, uint64_t Size) {
if (isBSDLike(Kind))
return printBSDMemberHeader(Out, Pos, M.MemberName, ModTime, M.UID, M.GID,
M.Perms, Size);
if (!useStringTable(Thin, M.MemberName))
return printGNUSmallMemberHeader(Out, M.MemberName, ModTime, M.UID, M.GID,
M.Perms, Size);
Out << '/';
uint64_t NamePos;
if (Thin) {
NamePos = StringTable.tell();
StringTable << M.MemberName << "/\n";
} else {
auto Insertion = MemberNames.insert({M.MemberName, uint64_t(0)});
if (Insertion.second) {
Insertion.first->second = StringTable.tell();
StringTable << M.MemberName << "/\n";
}
NamePos = Insertion.first->second;
}
printWithSpacePadding(Out, NamePos, 15);
printRestOfMemberHeader(Out, ModTime, M.UID, M.GID, M.Perms, Size);
}
namespace {
struct MemberData {
std::vector<unsigned> Symbols;
std::string Header;
StringRef Data;
StringRef Padding;
};
}
static MemberData computeStringTable(StringRef Names) {
unsigned Size = Names.size();
unsigned Pad = offsetToAlignment(Size, Align(2));
std::string Header;
raw_string_ostream Out(Header);
printWithSpacePadding(Out, "//", 48);
printWithSpacePadding(Out, Size + Pad, 10);
Out << "`\n";
Out.flush();
return {{}, std::move(Header), Names, Pad ? "\n" : ""};
}
static sys::TimePoint<std::chrono::seconds> now(bool Deterministic) {
using namespace std::chrono;
if (!Deterministic)
return time_point_cast<seconds>(system_clock::now());
return sys::TimePoint<seconds>();
}
static bool isArchiveSymbol(const object::BasicSymbolRef &S) {
Expected<uint32_t> SymFlagsOrErr = S.getFlags();
if (!SymFlagsOrErr)
report_fatal_error(SymFlagsOrErr.takeError());
if (*SymFlagsOrErr & object::SymbolRef::SF_FormatSpecific)
return false;
if (!(*SymFlagsOrErr & object::SymbolRef::SF_Global))
return false;
if (*SymFlagsOrErr & object::SymbolRef::SF_Undefined)
return false;
return true;
}
static void printNBits(raw_ostream &Out, object::Archive::Kind Kind,
uint64_t Val) {
if (is64BitKind(Kind))
print<uint64_t>(Out, Kind, Val);
else
print<uint32_t>(Out, Kind, Val);
}
static uint64_t computeSymbolTableSize(object::Archive::Kind Kind,
uint64_t NumSyms, uint64_t OffsetSize,
StringRef StringTable,
uint32_t *Padding = nullptr) {
assert((OffsetSize == 4 || OffsetSize == 8) && "Unsupported OffsetSize");
uint64_t Size = OffsetSize; if (isBSDLike(Kind))
Size += NumSyms * OffsetSize * 2; else
Size += NumSyms * OffsetSize; if (isBSDLike(Kind))
Size += OffsetSize; Size += StringTable.size();
uint32_t Pad = isAIXBigArchive(Kind)
? 0
: offsetToAlignment(Size, Align(isBSDLike(Kind) ? 8 : 2));
Size += Pad;
if (Padding)
*Padding = Pad;
return Size;
}
static void writeSymbolTableHeader(raw_ostream &Out, object::Archive::Kind Kind,
bool Deterministic, uint64_t Size,
uint64_t PrevMemberOffset = 0) {
if (isBSDLike(Kind)) {
const char *Name = is64BitKind(Kind) ? "__.SYMDEF_64" : "__.SYMDEF";
printBSDMemberHeader(Out, Out.tell(), Name, now(Deterministic), 0, 0, 0,
Size);
} else if (isAIXBigArchive(Kind)) {
printBigArchiveMemberHeader(Out, "", now(Deterministic), 0, 0,
0, Size, PrevMemberOffset, 0);
} else {
const char *Name = is64BitKind(Kind) ? "/SYM64" : "";
printGNUSmallMemberHeader(Out, Name, now(Deterministic), 0, 0, 0, Size);
}
}
static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind,
bool Deterministic, ArrayRef<MemberData> Members,
StringRef StringTable,
uint64_t PrevMemberOffset = 0) {
if (StringTable.empty() && !isDarwin(Kind))
return;
unsigned NumSyms = 0;
for (const MemberData &M : Members)
NumSyms += M.Symbols.size();
uint64_t OffsetSize = is64BitKind(Kind) ? 8 : 4;
uint32_t Pad;
uint64_t Size = computeSymbolTableSize(Kind, NumSyms, OffsetSize, StringTable, &Pad);
writeSymbolTableHeader(Out, Kind, Deterministic, Size, PrevMemberOffset);
uint64_t Pos = isAIXBigArchive(Kind) ? sizeof(object::BigArchive::FixLenHdr)
: Out.tell() + Size;
if (isBSDLike(Kind))
printNBits(Out, Kind, NumSyms * 2 * OffsetSize);
else
printNBits(Out, Kind, NumSyms);
for (const MemberData &M : Members) {
for (unsigned StringOffset : M.Symbols) {
if (isBSDLike(Kind))
printNBits(Out, Kind, StringOffset);
printNBits(Out, Kind, Pos); }
Pos += M.Header.size() + M.Data.size() + M.Padding.size();
}
if (isBSDLike(Kind))
printNBits(Out, Kind, StringTable.size());
Out << StringTable;
while (Pad--)
Out.write(uint8_t(0));
}
static Expected<std::vector<unsigned>>
getSymbols(MemoryBufferRef Buf, raw_ostream &SymNames, bool &HasObject) {
std::vector<unsigned> Ret;
LLVMContext Context;
std::unique_ptr<object::SymbolicFile> Obj;
const file_magic Type = identify_magic(Buf.getBuffer());
if (!object::SymbolicFile::isSymbolicFile(Type, &Context))
return Ret;
if (Type == file_magic::bitcode) {
auto ObjOrErr = object::SymbolicFile::createSymbolicFile(
Buf, file_magic::bitcode, &Context);
if (!ObjOrErr)
return ObjOrErr.takeError();
Obj = std::move(*ObjOrErr);
} else {
auto ObjOrErr = object::SymbolicFile::createSymbolicFile(Buf);
if (!ObjOrErr)
return ObjOrErr.takeError();
Obj = std::move(*ObjOrErr);
}
HasObject = true;
for (const object::BasicSymbolRef &S : Obj->symbols()) {
if (!isArchiveSymbol(S))
continue;
Ret.push_back(SymNames.tell());
if (Error E = S.printName(SymNames))
return std::move(E);
SymNames << '\0';
}
return Ret;
}
static Expected<std::vector<MemberData>>
computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames,
object::Archive::Kind Kind, bool Thin, bool Deterministic,
bool NeedSymbols, ArrayRef<NewArchiveMember> NewMembers) {
static char PaddingData[8] = {'\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n'};
uint64_t Pos =
isAIXBigArchive(Kind) ? sizeof(object::BigArchive::FixLenHdr) : 0;
std::vector<MemberData> Ret;
bool HasObject = false;
StringMap<uint64_t> MemberNames;
bool UniqueTimestamps = Deterministic && isDarwin(Kind);
std::map<StringRef, unsigned> FilenameCount;
if (UniqueTimestamps) {
for (const NewArchiveMember &M : NewMembers)
FilenameCount[M.MemberName]++;
for (auto &Entry : FilenameCount)
Entry.second = Entry.second > 1 ? 1 : 0;
}
unsigned PrevOffset = 0;
for (const NewArchiveMember &M : NewMembers) {
std::string Header;
raw_string_ostream Out(Header);
MemoryBufferRef Buf = M.Buf->getMemBufferRef();
StringRef Data = Thin ? "" : Buf.getBuffer();
unsigned MemberPadding =
isDarwin(Kind) ? offsetToAlignment(Data.size(), Align(8)) : 0;
unsigned TailPadding =
offsetToAlignment(Data.size() + MemberPadding, Align(2));
StringRef Padding = StringRef(PaddingData, MemberPadding + TailPadding);
sys::TimePoint<std::chrono::seconds> ModTime;
if (UniqueTimestamps)
ModTime = sys::toTimePoint(FilenameCount[M.MemberName]++);
else
ModTime = M.ModTime;
uint64_t Size = Buf.getBufferSize() + MemberPadding;
if (Size > object::Archive::MaxMemberSize) {
std::string StringMsg =
"File " + M.MemberName.str() + " exceeds size limit";
return make_error<object::GenericBinaryError>(
std::move(StringMsg), object::object_error::parse_failed);
}
if (isAIXBigArchive(Kind)) {
unsigned NextOffset = Pos + sizeof(object::BigArMemHdrType) +
alignTo(M.MemberName.size(), 2) + alignTo(Size, 2);
printBigArchiveMemberHeader(Out, M.MemberName, ModTime, M.UID, M.GID,
M.Perms, Size, PrevOffset, NextOffset);
PrevOffset = Pos;
} else {
printMemberHeader(Out, Pos, StringTable, MemberNames, Kind, Thin, M,
ModTime, Size);
}
Out.flush();
std::vector<unsigned> Symbols;
if (NeedSymbols) {
Expected<std::vector<unsigned>> SymbolsOrErr =
getSymbols(Buf, SymNames, HasObject);
if (auto E = SymbolsOrErr.takeError())
return std::move(E);
Symbols = std::move(*SymbolsOrErr);
}
Pos += Header.size() + Data.size() + Padding.size();
Ret.push_back({std::move(Symbols), std::move(Header), Data, Padding});
}
if (HasObject && SymNames.tell() == 0)
SymNames << '\0' << '\0' << '\0';
return Ret;
}
namespace llvm {
static ErrorOr<SmallString<128>> canonicalizePath(StringRef P) {
SmallString<128> Ret = P;
std::error_code Err = sys::fs::make_absolute(Ret);
if (Err)
return Err;
sys::path::remove_dots(Ret, true);
return Ret;
}
Expected<std::string> computeArchiveRelativePath(StringRef From, StringRef To) {
ErrorOr<SmallString<128>> PathToOrErr = canonicalizePath(To);
ErrorOr<SmallString<128>> DirFromOrErr = canonicalizePath(From);
if (!PathToOrErr || !DirFromOrErr)
return errorCodeToError(std::error_code(errno, std::generic_category()));
const SmallString<128> &PathTo = *PathToOrErr;
const SmallString<128> &DirFrom = sys::path::parent_path(*DirFromOrErr);
if (sys::path::root_name(PathTo) != sys::path::root_name(DirFrom))
return sys::path::convert_to_slash(PathTo);
auto FromTo =
std::mismatch(sys::path::begin(DirFrom), sys::path::end(DirFrom),
sys::path::begin(PathTo));
auto FromI = FromTo.first;
auto ToI = FromTo.second;
SmallString<128> Relative;
for (auto FromE = sys::path::end(DirFrom); FromI != FromE; ++FromI)
sys::path::append(Relative, sys::path::Style::posix, "..");
for (auto ToE = sys::path::end(PathTo); ToI != ToE; ++ToI)
sys::path::append(Relative, sys::path::Style::posix, *ToI);
return std::string(Relative.str());
}
static Error writeArchiveToStream(raw_ostream &Out,
ArrayRef<NewArchiveMember> NewMembers,
bool WriteSymtab, object::Archive::Kind Kind,
bool Deterministic, bool Thin) {
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,
WriteSymtab, NewMembers);
if (Error E = DataOrErr.takeError())
return E;
std::vector<MemberData> &Data = *DataOrErr;
if (!StringTableBuf.empty() && !isAIXBigArchive(Kind))
Data.insert(Data.begin(), computeStringTable(StringTableBuf));
uint64_t LastMemberEndOffset =
isAIXBigArchive(Kind) ? sizeof(object::BigArchive::FixLenHdr) : 8;
uint64_t LastMemberHeaderOffset = LastMemberEndOffset;
uint64_t NumSyms = 0;
for (const auto &M : Data) {
LastMemberHeaderOffset = LastMemberEndOffset;
LastMemberEndOffset += M.Header.size() + M.Data.size() + M.Padding.size();
NumSyms += M.Symbols.size();
}
if (WriteSymtab && !isAIXBigArchive(Kind)) {
uint64_t SymtabSize = computeSymbolTableSize(Kind, NumSyms, 4, SymNamesBuf);
auto computeSymbolTableHeaderSize =
[=] {
SmallString<0> TmpBuf;
raw_svector_ostream Tmp(TmpBuf);
writeSymbolTableHeader(Tmp, Kind, Deterministic, SymtabSize);
return TmpBuf.size();
};
LastMemberHeaderOffset += computeSymbolTableHeaderSize() + SymtabSize;
uint64_t Sym64Threshold = 1ULL << 32;
const char *Sym64Env = std::getenv("SYM64_THRESHOLD");
if (Sym64Env)
StringRef(Sym64Env).getAsInteger(10, Sym64Threshold);
if (LastMemberHeaderOffset >= Sym64Threshold) {
if (Kind == object::Archive::K_DARWIN)
Kind = object::Archive::K_DARWIN64;
else
Kind = object::Archive::K_GNU64;
}
}
if (Thin)
Out << "!<thin>\n";
else if (isAIXBigArchive(Kind))
Out << "<bigaf>\n";
else
Out << "!<arch>\n";
if (!isAIXBigArchive(Kind)) {
if (WriteSymtab)
writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf);
for (const MemberData &M : Data)
Out << M.Header << M.Data << M.Padding;
} else {
uint64_t MemberTableNameStrTblSize = 0;
std::vector<size_t> MemberOffsets;
std::vector<StringRef> MemberNames;
uint64_t MemberEndOffset = sizeof(object::BigArchive::FixLenHdr);
for (size_t I = 0, Size = NewMembers.size(); I != Size; ++I) {
const NewArchiveMember &Member = NewMembers[I];
MemberTableNameStrTblSize += Member.MemberName.size() + 1;
MemberOffsets.push_back(MemberEndOffset);
MemberNames.push_back(Member.MemberName);
MemberEndOffset += sizeof(object::BigArMemHdrType) +
alignTo(Data[I].Data.size(), 2) +
alignTo(Member.MemberName.size(), 2);
}
unsigned MemberTableSize = 20 + 20 * MemberOffsets.size() +
MemberTableNameStrTblSize;
unsigned GlobalSymbolOffset =
(WriteSymtab && NumSyms > 0)
? LastMemberEndOffset +
alignTo(sizeof(object::BigArMemHdrType) + MemberTableSize, 2)
: 0;
printWithSpacePadding(Out, NewMembers.size() ? LastMemberEndOffset : 0,
20); printWithSpacePadding(Out, NewMembers.size() ? GlobalSymbolOffset : 0, 20);
printWithSpacePadding(
Out, 0,
20); printWithSpacePadding(
Out, NewMembers.size() ? sizeof(object::BigArchive::FixLenHdr) : 0,
20); printWithSpacePadding(Out, NewMembers.size() ? LastMemberHeaderOffset : 0,
20); printWithSpacePadding(
Out, 0,
20);
for (const MemberData &M : Data) {
Out << M.Header << M.Data;
if (M.Data.size() % 2)
Out << '\0';
}
if (NewMembers.size()) {
printBigArchiveMemberHeader(Out, "", sys::toTimePoint(0), 0, 0, 0,
MemberTableSize, LastMemberHeaderOffset,
GlobalSymbolOffset);
printWithSpacePadding(Out, MemberOffsets.size(), 20); for (uint64_t MemberOffset : MemberOffsets)
printWithSpacePadding(Out, MemberOffset,
20); for (StringRef MemberName : MemberNames)
Out << MemberName << '\0';
if (MemberTableNameStrTblSize % 2)
Out << '\0';
if (WriteSymtab && NumSyms > 0)
writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf,
LastMemberEndOffset);
}
}
Out.flush();
return Error::success();
}
Error writeArchive(StringRef ArcName, ArrayRef<NewArchiveMember> NewMembers,
bool WriteSymtab, object::Archive::Kind Kind,
bool Deterministic, bool Thin,
std::unique_ptr<MemoryBuffer> OldArchiveBuf) {
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 (Error E = writeArchiveToStream(Out, NewMembers, WriteSymtab, Kind,
Deterministic, Thin)) {
if (Error DiscardError = Temp->discard())
return joinErrors(std::move(E), std::move(DiscardError));
return E;
}
OldArchiveBuf.reset();
return Temp->keep(ArcName);
}
Expected<std::unique_ptr<MemoryBuffer>>
writeArchiveToBuffer(ArrayRef<NewArchiveMember> NewMembers, bool WriteSymtab,
object::Archive::Kind Kind, bool Deterministic,
bool Thin) {
SmallVector<char, 0> ArchiveBufferVector;
raw_svector_ostream ArchiveStream(ArchiveBufferVector);
if (Error E = writeArchiveToStream(ArchiveStream, NewMembers, WriteSymtab,
Kind, Deterministic, Thin))
return std::move(E);
return std::make_unique<SmallVectorMemoryBuffer>(
std::move(ArchiveBufferVector), false);
}
}