Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

114 lines
4.6 KiB
C++
Raw Normal View History

//===- Archive.cpp --------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Archive.h"
#include "llvm/ObjCopy/CommonConfig.h"
#include "llvm/ObjCopy/MultiFormatConfig.h"
#include "llvm/ObjCopy/ObjCopy.h"
#include "llvm/Object/Error.h"
[Object] Fix updating darwin archives When creating an archive, llvm-ar looks at the host to determine the archive format to use, on Apple platforms this means it uses the K_DARWIN format. K_DARWIN is _virtually_ equivalent to K_BSD, expect for some very slight differences around padding, timestamps in deterministic mode, and 64 bit formats. When updating an archive using llvm-ar, or llvm-objcopy, Archive would try to determine the kind, but it was not possible to get K_DARWIN in the initialization of the archive, because they're virtually inciting usable from K_BSD, especially since the slight differences only apply in very specific cases. This leads to linker failures when the alignment workaround is not applied to an archive copied with llvm-objcopy. This change teaches Archive to infer the K_DARWIN type in the cases where it's possible and the first object in the archive is a macho object. This avoids using the host triple to determine this to not affect cross compiling. Ideally we would eliminate the separate K_DARWIN type entirely since it's not a truly separate archive type, but then we'd have to force the macho workarounds on the BSD format generally. This might be acceptable but then it would be unclear how to handle this case without forcing the K_DARWIN64 format on all BSD users: ``` if (LastOffset >= Sym64Threshold) { if (Kind == object::Archive::K_DARWIN) Kind = object::Archive::K_DARWIN64; else Kind = object::Archive::K_GNU64; } ``` The logic used to determine if the object is macho is derived from the logic llvm-ar uses. Previous context: - 111cd669e90e5b2132187d36f8b141b11a671a8b - 23a76be5adcaa768ba538f8a4514a7afccf61988 Differential Revision: https://reviews.llvm.org/D124895
2022-05-03 18:43:46 -07:00
#include "llvm/Object/MachO.h"
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/SmallVectorMemoryBuffer.h"
namespace llvm {
namespace objcopy {
using namespace llvm::object;
Expected<std::vector<NewArchiveMember>>
createNewArchiveMembers(const MultiFormatConfig &Config, const Archive &Ar) {
std::vector<NewArchiveMember> NewArchiveMembers;
Error Err = Error::success();
for (const Archive::Child &Child : Ar.children(Err)) {
Expected<StringRef> ChildNameOrErr = Child.getName();
if (!ChildNameOrErr)
return createFileError(Ar.getFileName(), ChildNameOrErr.takeError());
Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
if (!ChildOrErr)
return createFileError(Ar.getFileName() + "(" + *ChildNameOrErr + ")",
ChildOrErr.takeError());
SmallVector<char, 0> Buffer;
raw_svector_ostream MemStream(Buffer);
if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MemStream))
return std::move(E);
Expected<NewArchiveMember> Member = NewArchiveMember::getOldMember(
Child, Config.getCommonConfig().DeterministicArchives);
if (!Member)
return createFileError(Ar.getFileName(), Member.takeError());
Member->Buf = std::make_unique<SmallVectorMemoryBuffer>(
std::move(Buffer), ChildNameOrErr.get());
Member->MemberName = Member->Buf->getBufferIdentifier();
NewArchiveMembers.push_back(std::move(*Member));
}
if (Err)
return createFileError(Config.getCommonConfig().InputFilename,
std::move(Err));
return std::move(NewArchiveMembers);
}
// For regular archives this function simply calls llvm::writeArchive,
// For thin archives it writes the archive file itself as well as its members.
static Error deepWriteArchive(StringRef ArcName,
ArrayRef<NewArchiveMember> NewMembers,
SymtabWritingMode WriteSymtab,
object::Archive::Kind Kind, bool Deterministic,
bool Thin) {
[Object] Fix updating darwin archives When creating an archive, llvm-ar looks at the host to determine the archive format to use, on Apple platforms this means it uses the K_DARWIN format. K_DARWIN is _virtually_ equivalent to K_BSD, expect for some very slight differences around padding, timestamps in deterministic mode, and 64 bit formats. When updating an archive using llvm-ar, or llvm-objcopy, Archive would try to determine the kind, but it was not possible to get K_DARWIN in the initialization of the archive, because they're virtually inciting usable from K_BSD, especially since the slight differences only apply in very specific cases. This leads to linker failures when the alignment workaround is not applied to an archive copied with llvm-objcopy. This change teaches Archive to infer the K_DARWIN type in the cases where it's possible and the first object in the archive is a macho object. This avoids using the host triple to determine this to not affect cross compiling. Ideally we would eliminate the separate K_DARWIN type entirely since it's not a truly separate archive type, but then we'd have to force the macho workarounds on the BSD format generally. This might be acceptable but then it would be unclear how to handle this case without forcing the K_DARWIN64 format on all BSD users: ``` if (LastOffset >= Sym64Threshold) { if (Kind == object::Archive::K_DARWIN) Kind = object::Archive::K_DARWIN64; else Kind = object::Archive::K_GNU64; } ``` The logic used to determine if the object is macho is derived from the logic llvm-ar uses. Previous context: - 111cd669e90e5b2132187d36f8b141b11a671a8b - 23a76be5adcaa768ba538f8a4514a7afccf61988 Differential Revision: https://reviews.llvm.org/D124895
2022-05-03 18:43:46 -07:00
if (Kind == object::Archive::K_BSD && !NewMembers.empty() &&
NewMembers.front().detectKindFromObject() == object::Archive::K_DARWIN)
Kind = object::Archive::K_DARWIN;
if (Error E = writeArchive(ArcName, NewMembers, WriteSymtab, Kind,
Deterministic, Thin))
return createFileError(ArcName, std::move(E));
if (!Thin)
return Error::success();
for (const NewArchiveMember &Member : NewMembers) {
// For regular files (as is the case for deepWriteArchive),
// FileOutputBuffer::create will return OnDiskBuffer.
// OnDiskBuffer uses a temporary file and then renames it. So in reality
// there is no inefficiency / duplicated in-memory buffers in this case. For
// now in-memory buffers can not be completely avoided since
// NewArchiveMember still requires them even though writeArchive does not
// write them on disk.
Expected<std::unique_ptr<FileOutputBuffer>> FB =
FileOutputBuffer::create(Member.MemberName, Member.Buf->getBufferSize(),
FileOutputBuffer::F_executable);
if (!FB)
return FB.takeError();
std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(),
(*FB)->getBufferStart());
if (Error E = (*FB)->commit())
return E;
}
return Error::success();
}
Error executeObjcopyOnArchive(const MultiFormatConfig &Config,
const object::Archive &Ar) {
Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr =
createNewArchiveMembers(Config, Ar);
if (!NewArchiveMembersOrErr)
return NewArchiveMembersOrErr.takeError();
const CommonConfig &CommonConfig = Config.getCommonConfig();
return deepWriteArchive(CommonConfig.OutputFilename, *NewArchiveMembersOrErr,
Ar.hasSymbolTable() ? SymtabWritingMode::NormalSymtab
: SymtabWritingMode::NoSymtab,
Ar.kind(), CommonConfig.DeterministicArchives,
Ar.isThin());
}
} // end namespace objcopy
} // end namespace llvm