mirror of
https://github.com/RPCSX/llvm.git
synced 2024-12-13 14:46:53 +00:00
c6bf9be16d
Produce the first specific error message for a malformed Mach-O file describing the problem instead of the generic message for object_error::parse_failed of "Invalid data was encountered while parsing the file”. Many more good error messages will follow after this first one. This is built on Lang Hames’ great work of adding the ’Error' class for structured error handling and threading Error through MachOObjectFile construction. And making createMachOObjectFile return Expected<...> . So to to get the error to the llvm-obdump tool, I changed the stack of these methods to also return Expected<...> : object::ObjectFile::createObjectFile() object::SymbolicFile::createSymbolicFile() object::createBinary() Then finally in ParseInputMachO() in MachODump.cpp the error can be reported and the specific error message can be printed in llvm-objdump and can be seen in the existing test case for the existing malformed binary but with the updated error message. Converting these interfaces to Expected<> from ErrorOr<> does involve touching a number of places. To contain the changes for now use of errorToErrorCode() and errorOrToExpected() are used where the callers are yet to be converted. Also there some were bugs in the existing code that did not deal with the old ErrorOr<> return values. So now with Expected<> since they must be checked and the error handled, I added a TODO and a comment: “// TODO: Actually report errors helpfully” and a call something like consumeError(ObjOrErr.takeError()) so the buggy code will not crash since needed to deal with the Error. Note there is one fix also needed to lld/COFF/InputFiles.cpp that goes along with this that I will commit right after this. So expect lld not to built after this commit and before the next one. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@265606 91177308-0d34-0410-b5e6-96231b3b80d8
448 lines
15 KiB
C++
448 lines
15 KiB
C++
//===- ArchiveWriter.cpp - ar File Format implementation --------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines the writeArchive function.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Object/ArchiveWriter.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/Object/Archive.h"
|
|
#include "llvm/Object/ObjectFile.h"
|
|
#include "llvm/Object/SymbolicFile.h"
|
|
#include "llvm/Support/EndianStream.h"
|
|
#include "llvm/Support/Errc.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/Format.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/ToolOutputFile.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#if !defined(_MSC_VER) && !defined(__MINGW32__)
|
|
#include <unistd.h>
|
|
#else
|
|
#include <io.h>
|
|
#endif
|
|
|
|
using namespace llvm;
|
|
|
|
NewArchiveIterator::NewArchiveIterator(const object::Archive::Child &OldMember,
|
|
StringRef Name)
|
|
: IsNewMember(false), Name(Name), OldMember(OldMember) {}
|
|
|
|
NewArchiveIterator::NewArchiveIterator(StringRef FileName)
|
|
: IsNewMember(true), Name(FileName), OldMember(nullptr, nullptr, nullptr) {}
|
|
|
|
StringRef NewArchiveIterator::getName() const { return Name; }
|
|
|
|
bool NewArchiveIterator::isNewMember() const { return IsNewMember; }
|
|
|
|
const object::Archive::Child &NewArchiveIterator::getOld() const {
|
|
assert(!IsNewMember);
|
|
return OldMember;
|
|
}
|
|
|
|
StringRef NewArchiveIterator::getNew() const {
|
|
assert(IsNewMember);
|
|
return Name;
|
|
}
|
|
|
|
llvm::ErrorOr<int>
|
|
NewArchiveIterator::getFD(sys::fs::file_status &NewStatus) const {
|
|
assert(IsNewMember);
|
|
int NewFD;
|
|
if (auto EC = sys::fs::openFileForRead(Name, NewFD))
|
|
return EC;
|
|
assert(NewFD != -1);
|
|
|
|
if (auto EC = sys::fs::status(NewFD, NewStatus))
|
|
return EC;
|
|
|
|
// Opening a directory doesn't make sense. Let it fail.
|
|
// Linux cannot open directories with open(2), although
|
|
// cygwin and *bsd can.
|
|
if (NewStatus.type() == sys::fs::file_type::directory_file)
|
|
return make_error_code(errc::is_a_directory);
|
|
|
|
return NewFD;
|
|
}
|
|
|
|
template <typename T>
|
|
static void printWithSpacePadding(raw_fd_ostream &OS, T Data, unsigned Size,
|
|
bool MayTruncate = false) {
|
|
uint64_t OldPos = OS.tell();
|
|
OS << Data;
|
|
unsigned SizeSoFar = OS.tell() - OldPos;
|
|
if (Size > SizeSoFar) {
|
|
OS.indent(Size - SizeSoFar);
|
|
} else if (Size < SizeSoFar) {
|
|
assert(MayTruncate && "Data doesn't fit in Size");
|
|
// Some of the data this is used for (like UID) can be larger than the
|
|
// space available in the archive format. Truncate in that case.
|
|
OS.seek(OldPos + Size);
|
|
}
|
|
}
|
|
|
|
static void print32(raw_ostream &Out, object::Archive::Kind Kind,
|
|
uint32_t Val) {
|
|
if (Kind == object::Archive::K_GNU)
|
|
support::endian::Writer<support::big>(Out).write(Val);
|
|
else
|
|
support::endian::Writer<support::little>(Out).write(Val);
|
|
}
|
|
|
|
static void printRestOfMemberHeader(raw_fd_ostream &Out,
|
|
const sys::TimeValue &ModTime, unsigned UID,
|
|
unsigned GID, unsigned Perms,
|
|
unsigned Size) {
|
|
printWithSpacePadding(Out, ModTime.toEpochTime(), 12);
|
|
printWithSpacePadding(Out, UID, 6, true);
|
|
printWithSpacePadding(Out, GID, 6, true);
|
|
printWithSpacePadding(Out, format("%o", Perms), 8);
|
|
printWithSpacePadding(Out, Size, 10);
|
|
Out << "`\n";
|
|
}
|
|
|
|
static void printGNUSmallMemberHeader(raw_fd_ostream &Out, StringRef Name,
|
|
const sys::TimeValue &ModTime,
|
|
unsigned UID, unsigned GID,
|
|
unsigned Perms, unsigned Size) {
|
|
printWithSpacePadding(Out, Twine(Name) + "/", 16);
|
|
printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size);
|
|
}
|
|
|
|
static void printBSDMemberHeader(raw_fd_ostream &Out, StringRef Name,
|
|
const sys::TimeValue &ModTime, unsigned UID,
|
|
unsigned GID, unsigned Perms, unsigned Size) {
|
|
uint64_t PosAfterHeader = Out.tell() + 60 + Name.size();
|
|
// Pad so that even 64 bit object files are aligned.
|
|
unsigned Pad = OffsetToAlignment(PosAfterHeader, 8);
|
|
unsigned NameWithPadding = Name.size() + Pad;
|
|
printWithSpacePadding(Out, Twine("#1/") + Twine(NameWithPadding), 16);
|
|
printRestOfMemberHeader(Out, ModTime, UID, GID, Perms,
|
|
NameWithPadding + Size);
|
|
Out << Name;
|
|
assert(PosAfterHeader == Out.tell());
|
|
while (Pad--)
|
|
Out.write(uint8_t(0));
|
|
}
|
|
|
|
static bool useStringTable(bool Thin, StringRef Name) {
|
|
return Thin || Name.size() >= 16;
|
|
}
|
|
|
|
static void
|
|
printMemberHeader(raw_fd_ostream &Out, object::Archive::Kind Kind, bool Thin,
|
|
StringRef Name,
|
|
std::vector<unsigned>::iterator &StringMapIndexIter,
|
|
const sys::TimeValue &ModTime, unsigned UID, unsigned GID,
|
|
unsigned Perms, unsigned Size) {
|
|
if (Kind == object::Archive::K_BSD)
|
|
return printBSDMemberHeader(Out, Name, ModTime, UID, GID, Perms, Size);
|
|
if (!useStringTable(Thin, Name))
|
|
return printGNUSmallMemberHeader(Out, Name, ModTime, UID, GID, Perms, Size);
|
|
Out << '/';
|
|
printWithSpacePadding(Out, *StringMapIndexIter++, 15);
|
|
printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size);
|
|
}
|
|
|
|
// Compute the relative path from From to To.
|
|
static std::string computeRelativePath(StringRef From, StringRef To) {
|
|
if (sys::path::is_absolute(From) || sys::path::is_absolute(To))
|
|
return To;
|
|
|
|
StringRef DirFrom = sys::path::parent_path(From);
|
|
auto FromI = sys::path::begin(DirFrom);
|
|
auto ToI = sys::path::begin(To);
|
|
while (*FromI == *ToI) {
|
|
++FromI;
|
|
++ToI;
|
|
}
|
|
|
|
SmallString<128> Relative;
|
|
for (auto FromE = sys::path::end(DirFrom); FromI != FromE; ++FromI)
|
|
sys::path::append(Relative, "..");
|
|
|
|
for (auto ToE = sys::path::end(To); ToI != ToE; ++ToI)
|
|
sys::path::append(Relative, *ToI);
|
|
|
|
return Relative.str();
|
|
}
|
|
|
|
static void writeStringTable(raw_fd_ostream &Out, StringRef ArcName,
|
|
ArrayRef<NewArchiveIterator> Members,
|
|
std::vector<unsigned> &StringMapIndexes,
|
|
bool Thin) {
|
|
unsigned StartOffset = 0;
|
|
for (const NewArchiveIterator &I : Members) {
|
|
StringRef Name = sys::path::filename(I.getName());
|
|
if (!useStringTable(Thin, Name))
|
|
continue;
|
|
if (StartOffset == 0) {
|
|
printWithSpacePadding(Out, "//", 58);
|
|
Out << "`\n";
|
|
StartOffset = Out.tell();
|
|
}
|
|
StringMapIndexes.push_back(Out.tell() - StartOffset);
|
|
|
|
if (Thin)
|
|
Out << computeRelativePath(ArcName, I.getName());
|
|
else
|
|
Out << Name;
|
|
|
|
Out << "/\n";
|
|
}
|
|
if (StartOffset == 0)
|
|
return;
|
|
if (Out.tell() % 2)
|
|
Out << '\n';
|
|
int Pos = Out.tell();
|
|
Out.seek(StartOffset - 12);
|
|
printWithSpacePadding(Out, Pos - StartOffset, 10);
|
|
Out.seek(Pos);
|
|
}
|
|
|
|
static sys::TimeValue now(bool Deterministic) {
|
|
if (!Deterministic)
|
|
return sys::TimeValue::now();
|
|
sys::TimeValue TV;
|
|
TV.fromEpochTime(0);
|
|
return TV;
|
|
}
|
|
|
|
// Returns the offset of the first reference to a member offset.
|
|
static ErrorOr<unsigned>
|
|
writeSymbolTable(raw_fd_ostream &Out, object::Archive::Kind Kind,
|
|
ArrayRef<NewArchiveIterator> Members,
|
|
ArrayRef<MemoryBufferRef> Buffers,
|
|
std::vector<unsigned> &MemberOffsetRefs, bool Deterministic) {
|
|
unsigned HeaderStartOffset = 0;
|
|
unsigned BodyStartOffset = 0;
|
|
SmallString<128> NameBuf;
|
|
raw_svector_ostream NameOS(NameBuf);
|
|
LLVMContext Context;
|
|
for (unsigned MemberNum = 0, N = Members.size(); MemberNum < N; ++MemberNum) {
|
|
MemoryBufferRef MemberBuffer = Buffers[MemberNum];
|
|
Expected<std::unique_ptr<object::SymbolicFile>> ObjOrErr =
|
|
object::SymbolicFile::createSymbolicFile(
|
|
MemberBuffer, sys::fs::file_magic::unknown, &Context);
|
|
if (!ObjOrErr) {
|
|
// FIXME: check only for "not an object file" errors.
|
|
consumeError(ObjOrErr.takeError());
|
|
continue;
|
|
}
|
|
object::SymbolicFile &Obj = *ObjOrErr.get();
|
|
|
|
if (!HeaderStartOffset) {
|
|
HeaderStartOffset = Out.tell();
|
|
if (Kind == object::Archive::K_GNU)
|
|
printGNUSmallMemberHeader(Out, "", now(Deterministic), 0, 0, 0, 0);
|
|
else
|
|
printBSDMemberHeader(Out, "__.SYMDEF", now(Deterministic), 0, 0, 0, 0);
|
|
BodyStartOffset = Out.tell();
|
|
print32(Out, Kind, 0); // number of entries or bytes
|
|
}
|
|
|
|
for (const object::BasicSymbolRef &S : Obj.symbols()) {
|
|
uint32_t Symflags = S.getFlags();
|
|
if (Symflags & object::SymbolRef::SF_FormatSpecific)
|
|
continue;
|
|
if (!(Symflags & object::SymbolRef::SF_Global))
|
|
continue;
|
|
if (Symflags & object::SymbolRef::SF_Undefined)
|
|
continue;
|
|
|
|
unsigned NameOffset = NameOS.tell();
|
|
if (auto EC = S.printName(NameOS))
|
|
return EC;
|
|
NameOS << '\0';
|
|
MemberOffsetRefs.push_back(MemberNum);
|
|
if (Kind == object::Archive::K_BSD)
|
|
print32(Out, Kind, NameOffset);
|
|
print32(Out, Kind, 0); // member offset
|
|
}
|
|
}
|
|
|
|
if (HeaderStartOffset == 0)
|
|
return 0;
|
|
|
|
StringRef StringTable = NameOS.str();
|
|
if (Kind == object::Archive::K_BSD)
|
|
print32(Out, Kind, StringTable.size()); // byte count of the string table
|
|
Out << StringTable;
|
|
|
|
// ld64 requires the next member header to start at an offset that is
|
|
// 4 bytes aligned.
|
|
unsigned Pad = OffsetToAlignment(Out.tell(), 4);
|
|
while (Pad--)
|
|
Out.write(uint8_t(0));
|
|
|
|
// Patch up the size of the symbol table now that we know how big it is.
|
|
unsigned Pos = Out.tell();
|
|
const unsigned MemberHeaderSize = 60;
|
|
Out.seek(HeaderStartOffset + 48); // offset of the size field.
|
|
printWithSpacePadding(Out, Pos - MemberHeaderSize - HeaderStartOffset, 10);
|
|
|
|
// Patch up the number of symbols.
|
|
Out.seek(BodyStartOffset);
|
|
unsigned NumSyms = MemberOffsetRefs.size();
|
|
if (Kind == object::Archive::K_GNU)
|
|
print32(Out, Kind, NumSyms);
|
|
else
|
|
print32(Out, Kind, NumSyms * 8);
|
|
|
|
Out.seek(Pos);
|
|
return BodyStartOffset + 4;
|
|
}
|
|
|
|
std::pair<StringRef, std::error_code>
|
|
llvm::writeArchive(StringRef ArcName,
|
|
std::vector<NewArchiveIterator> &NewMembers,
|
|
bool WriteSymtab, object::Archive::Kind Kind,
|
|
bool Deterministic, bool Thin) {
|
|
SmallString<128> TmpArchive;
|
|
int TmpArchiveFD;
|
|
if (auto EC = sys::fs::createUniqueFile(ArcName + ".temp-archive-%%%%%%%.a",
|
|
TmpArchiveFD, TmpArchive))
|
|
return std::make_pair(ArcName, EC);
|
|
|
|
tool_output_file Output(TmpArchive, TmpArchiveFD);
|
|
raw_fd_ostream &Out = Output.os();
|
|
if (Thin)
|
|
Out << "!<thin>\n";
|
|
else
|
|
Out << "!<arch>\n";
|
|
|
|
std::vector<unsigned> MemberOffsetRefs;
|
|
|
|
std::vector<std::unique_ptr<MemoryBuffer>> Buffers;
|
|
std::vector<MemoryBufferRef> Members;
|
|
std::vector<sys::fs::file_status> NewMemberStatus;
|
|
|
|
for (unsigned I = 0, N = NewMembers.size(); I < N; ++I) {
|
|
NewArchiveIterator &Member = NewMembers[I];
|
|
MemoryBufferRef MemberRef;
|
|
|
|
if (Member.isNewMember()) {
|
|
StringRef Filename = Member.getNew();
|
|
NewMemberStatus.resize(NewMemberStatus.size() + 1);
|
|
sys::fs::file_status &Status = NewMemberStatus.back();
|
|
ErrorOr<int> FD = Member.getFD(Status);
|
|
if (auto EC = FD.getError())
|
|
return std::make_pair(Filename, EC);
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> MemberBufferOrErr =
|
|
MemoryBuffer::getOpenFile(FD.get(), Filename, Status.getSize(),
|
|
false);
|
|
if (auto EC = MemberBufferOrErr.getError())
|
|
return std::make_pair(Filename, EC);
|
|
if (close(FD.get()) != 0)
|
|
return std::make_pair(Filename,
|
|
std::error_code(errno, std::generic_category()));
|
|
Buffers.push_back(std::move(MemberBufferOrErr.get()));
|
|
MemberRef = Buffers.back()->getMemBufferRef();
|
|
} else {
|
|
const object::Archive::Child &OldMember = Member.getOld();
|
|
assert((!Thin || OldMember.getParent()->isThin()) &&
|
|
"Thin archives cannot refers to member of other archives");
|
|
ErrorOr<MemoryBufferRef> MemberBufferOrErr =
|
|
OldMember.getMemoryBufferRef();
|
|
if (auto EC = MemberBufferOrErr.getError())
|
|
return std::make_pair("", EC);
|
|
MemberRef = MemberBufferOrErr.get();
|
|
}
|
|
Members.push_back(MemberRef);
|
|
}
|
|
|
|
unsigned MemberReferenceOffset = 0;
|
|
if (WriteSymtab) {
|
|
ErrorOr<unsigned> MemberReferenceOffsetOrErr = writeSymbolTable(
|
|
Out, Kind, NewMembers, Members, MemberOffsetRefs, Deterministic);
|
|
if (auto EC = MemberReferenceOffsetOrErr.getError())
|
|
return std::make_pair(ArcName, EC);
|
|
MemberReferenceOffset = MemberReferenceOffsetOrErr.get();
|
|
}
|
|
|
|
std::vector<unsigned> StringMapIndexes;
|
|
if (Kind != object::Archive::K_BSD)
|
|
writeStringTable(Out, ArcName, NewMembers, StringMapIndexes, Thin);
|
|
|
|
unsigned MemberNum = 0;
|
|
unsigned NewMemberNum = 0;
|
|
std::vector<unsigned>::iterator StringMapIndexIter = StringMapIndexes.begin();
|
|
std::vector<unsigned> MemberOffset;
|
|
for (const NewArchiveIterator &I : NewMembers) {
|
|
MemoryBufferRef File = Members[MemberNum++];
|
|
|
|
unsigned Pos = Out.tell();
|
|
MemberOffset.push_back(Pos);
|
|
|
|
sys::TimeValue ModTime;
|
|
unsigned UID;
|
|
unsigned GID;
|
|
unsigned Perms;
|
|
if (Deterministic) {
|
|
ModTime.fromEpochTime(0);
|
|
UID = 0;
|
|
GID = 0;
|
|
Perms = 0644;
|
|
} else if (I.isNewMember()) {
|
|
const sys::fs::file_status &Status = NewMemberStatus[NewMemberNum];
|
|
ModTime = Status.getLastModificationTime();
|
|
UID = Status.getUser();
|
|
GID = Status.getGroup();
|
|
Perms = Status.permissions();
|
|
} else {
|
|
const object::Archive::Child &OldMember = I.getOld();
|
|
ModTime = OldMember.getLastModified();
|
|
UID = OldMember.getUID();
|
|
GID = OldMember.getGID();
|
|
Perms = OldMember.getAccessMode();
|
|
}
|
|
|
|
if (I.isNewMember()) {
|
|
StringRef FileName = I.getNew();
|
|
const sys::fs::file_status &Status = NewMemberStatus[NewMemberNum++];
|
|
printMemberHeader(Out, Kind, Thin, sys::path::filename(FileName),
|
|
StringMapIndexIter, ModTime, UID, GID, Perms,
|
|
Status.getSize());
|
|
} else {
|
|
const object::Archive::Child &OldMember = I.getOld();
|
|
ErrorOr<uint32_t> Size = OldMember.getSize();
|
|
if (std::error_code EC = Size.getError())
|
|
return std::make_pair("", EC);
|
|
StringRef FileName = I.getName();
|
|
printMemberHeader(Out, Kind, Thin, sys::path::filename(FileName),
|
|
StringMapIndexIter, ModTime, UID, GID, Perms,
|
|
Size.get());
|
|
}
|
|
|
|
if (!Thin)
|
|
Out << File.getBuffer();
|
|
|
|
if (Out.tell() % 2)
|
|
Out << '\n';
|
|
}
|
|
|
|
if (MemberReferenceOffset) {
|
|
Out.seek(MemberReferenceOffset);
|
|
for (unsigned MemberNum : MemberOffsetRefs) {
|
|
if (Kind == object::Archive::K_BSD)
|
|
Out.seek(Out.tell() + 4); // skip over the string offset
|
|
print32(Out, Kind, MemberOffset[MemberNum]);
|
|
}
|
|
}
|
|
|
|
Output.keep();
|
|
Out.close();
|
|
sys::fs::rename(TmpArchive, ArcName);
|
|
return std::make_pair("", std::error_code());
|
|
}
|