Skip to content

Commit

Permalink
Don't modify archive members unless really needed.
Browse files Browse the repository at this point in the history
For whatever reason ld64 requires that member headers (not the member
themselves) should be aligned. The only way to do that is to edit the
previous member so that it ends at an aligned boundary.

Since modifying data put in an archive is an undesirable property,
llvm-ar should only do it when it is absolutely necessary.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@295765 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
espindola committed Feb 21, 2017
1 parent 2c0dd61 commit 13f0c80
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 29 deletions.
1 change: 1 addition & 0 deletions include/llvm/Object/Archive.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ class Archive : public Binary {
K_GNU,
K_MIPS64,
K_BSD,
K_DARWIN,
K_DARWIN64,
K_COFF
};
Expand Down
49 changes: 31 additions & 18 deletions lib/Object/ArchiveWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,26 @@ static void printWithSpacePadding(raw_fd_ostream &OS, T Data, unsigned Size,
}
}

static bool isBSDLike(object::Archive::Kind Kind) {
switch (Kind) {
case object::Archive::K_GNU:
return false;
case object::Archive::K_BSD:
case object::Archive::K_DARWIN:
return true;
case object::Archive::K_MIPS64:
case object::Archive::K_DARWIN64:
case object::Archive::K_COFF:
llvm_unreachable("not supported for writting");
}
}

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
if (isBSDLike(Kind))
support::endian::Writer<support::little>(Out).write(Val);
else
support::endian::Writer<support::big>(Out).write(Val);
}

static void printRestOfMemberHeader(
Expand Down Expand Up @@ -178,7 +192,7 @@ printMemberHeader(raw_fd_ostream &Out, object::Archive::Kind Kind, bool Thin,
std::vector<unsigned>::iterator &StringMapIndexIter,
const sys::TimePoint<std::chrono::seconds> &ModTime,
unsigned UID, unsigned GID, unsigned Perms, unsigned Size) {
if (Kind == object::Archive::K_BSD)
if (isBSDLike(Kind))
return printBSDMemberHeader(Out, Name, ModTime, UID, GID, Perms, Size);
if (!useStringTable(Thin, Name))
return printGNUSmallMemberHeader(Out, Name, ModTime, UID, GID, Perms, Size);
Expand Down Expand Up @@ -285,10 +299,10 @@ writeSymbolTable(raw_fd_ostream &Out, object::Archive::Kind Kind,

if (!HeaderStartOffset) {
HeaderStartOffset = Out.tell();
if (Kind == object::Archive::K_GNU)
printGNUSmallMemberHeader(Out, "", now(Deterministic), 0, 0, 0, 0);
else
if (isBSDLike(Kind))
printBSDMemberHeader(Out, "__.SYMDEF", now(Deterministic), 0, 0, 0, 0);
else
printGNUSmallMemberHeader(Out, "", now(Deterministic), 0, 0, 0, 0);
BodyStartOffset = Out.tell();
print32(Out, Kind, 0); // number of entries or bytes
}
Expand All @@ -307,7 +321,7 @@ writeSymbolTable(raw_fd_ostream &Out, object::Archive::Kind Kind,
return EC;
NameOS << '\0';
MemberOffsetRefs.push_back(MemberNum);
if (Kind == object::Archive::K_BSD)
if (isBSDLike(Kind))
print32(Out, Kind, NameOffset);
print32(Out, Kind, 0); // member offset
}
Expand All @@ -318,12 +332,12 @@ writeSymbolTable(raw_fd_ostream &Out, object::Archive::Kind Kind,

// ld64 prefers the cctools type archive which pads its string table to a
// boundary of sizeof(int32_t).
if (Kind == object::Archive::K_BSD)
if (isBSDLike(Kind))
for (unsigned P = OffsetToAlignment(NameOS.tell(), sizeof(int32_t)); P--;)
NameOS << '\0';

StringRef StringTable = NameOS.str();
if (Kind == object::Archive::K_BSD)
if (isBSDLike(Kind))
print32(Out, Kind, StringTable.size()); // byte count of the string table
Out << StringTable;

Expand All @@ -342,10 +356,10 @@ writeSymbolTable(raw_fd_ostream &Out, object::Archive::Kind Kind,
// Patch up the number of symbols.
Out.seek(BodyStartOffset);
unsigned NumSyms = MemberOffsetRefs.size();
if (Kind == object::Archive::K_GNU)
print32(Out, Kind, NumSyms);
else
if (isBSDLike(Kind))
print32(Out, Kind, NumSyms * 8);
else
print32(Out, Kind, NumSyms);

Out.seek(Pos);
return BodyStartOffset + 4;
Expand All @@ -357,8 +371,7 @@ llvm::writeArchive(StringRef ArcName,
bool WriteSymtab, object::Archive::Kind Kind,
bool Deterministic, bool Thin,
std::unique_ptr<MemoryBuffer> OldArchiveBuf) {
assert((!Thin || Kind == object::Archive::K_GNU) &&
"Only the gnu format has a thin mode");
assert((!Thin || !isBSDLike(Kind)) && "Only the gnu format has a thin mode");
SmallString<128> TmpArchive;
int TmpArchiveFD;
if (auto EC = sys::fs::createUniqueFile(ArcName + ".temp-archive-%%%%%%%.a",
Expand Down Expand Up @@ -388,7 +401,7 @@ llvm::writeArchive(StringRef ArcName,
}

std::vector<unsigned> StringMapIndexes;
if (Kind != object::Archive::K_BSD)
if (!isBSDLike(Kind))
writeStringTable(Out, ArcName, NewMembers, StringMapIndexes, Thin);

std::vector<unsigned>::iterator StringMapIndexIter = StringMapIndexes.begin();
Expand All @@ -404,7 +417,7 @@ llvm::writeArchive(StringRef ArcName,
// least 4-byte aligned for 32-bit content. Opt for the larger encoding
// uniformly. This matches the behaviour with cctools and ensures that ld64
// is happy with archives that we generate.
if (Kind == object::Archive::K_BSD)
if (Kind == object::Archive::K_DARWIN)
Padding = OffsetToAlignment(M.Buf->getBufferSize(), 8);

printMemberHeader(Out, Kind, Thin,
Expand All @@ -424,7 +437,7 @@ llvm::writeArchive(StringRef ArcName,
if (MemberReferenceOffset) {
Out.seek(MemberReferenceOffset);
for (unsigned MemberNum : MemberOffsetRefs) {
if (Kind == object::Archive::K_BSD)
if (isBSDLike(Kind))
Out.seek(Out.tell() + 4); // skip over the string offset
print32(Out, Kind, MemberOffset[MemberNum]);
}
Expand Down
6 changes: 3 additions & 3 deletions test/Object/archive-extract.test
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@
CHECK-GNU: 1465 2004-11-19 03:01:31.000000000 very_long_bytecode_file_name.bc

; RUN: rm -f %t.a
; RUN: llvm-ar -format bsd rcU %t.a very_long_bytecode_file_name.bc
; RUN: env TZ=GMT llvm-ar tv %t.a | FileCheck %s -check-prefix CHECK-BSD
; RUN: llvm-ar -format darwin rcU %t.a very_long_bytecode_file_name.bc
; RUN: env TZ=GMT llvm-ar tv %t.a | FileCheck %s -check-prefix CHECK-DARWIN

CHECK-BSD: 1472 2004-11-19 03:01:31.000000000 very_long_bytecode_file_name.bc
CHECK-DARWIN: 1472 2004-11-19 03:01:31.000000000 very_long_bytecode_file_name.bc

RUN: not llvm-ar x %p/Inputs/GNU.a foo.o 2>&1 | FileCheck --check-prefix=NOTFOUND %s
NOTFOUND: foo.o was not found
Expand Down
19 changes: 15 additions & 4 deletions test/Object/archive-format.test
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,23 @@ RUN: llvm-ar --format=bsd rc %t.a 0123456789abcde 0123456789abcdef
RUN: cat %t.a | FileCheck -strict-whitespace --check-prefix=BSD %s

BSD: !<arch>
BSD-NEXT: #1/20 0 0 0 644 28 `
BSD-NEXT: #1/20 0 0 0 644 24 `
BSD-NEXT: 0123456789abcde{{.....}}bar.
BSD-SAME: #1/16 0 0 0 644 20 `
BSD-NEXT: 0123456789abcdefzed.

RUN: rm -f %t.a
RUN: llvm-ar --format=darwin rc %t.a 0123456789abcde 0123456789abcdef
RUN: cat %t.a | FileCheck -strict-whitespace --check-prefix=DARWIN %s

DARWIN: !<arch>
DARWIN-NEXT: #1/20 0 0 0 644 28 `
Each [[:space:]] matches a newline. We explicitly match 3 newlines, as the
fourth newline is implicitly consumed by FileCheck and cannot be matched.
BSD-NEXT: 0123456789abcde{{.....}}bar.{{[[:space:]][[:space:]][[:space:]]}}
BSD-NEXT: #1/20 0 0 0 644 28 `
BSD-NEXT: 0123456789abcdef{{....}}zed.
DARWIN-NEXT: 0123456789abcde{{.....}}bar.{{[[:space:]][[:space:]][[:space:]]}}
DARWIN-NEXT: #1/20 0 0 0 644 28 `
DARWIN-NEXT: 0123456789abcdef{{....}}zed.


RUN: rm -f test.a
RUN: llvm-ar --format=gnu rcT test.a 0123456789abcde 0123456789abcdef
Expand Down
19 changes: 19 additions & 0 deletions test/Object/archive-pad.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Test that only the darwin format needs to modify archive members to
avoid a ld64 bug.

RUN: echo foo > %t.o

RUN: rm -f %t.a
RUN: llvm-ar -format=bsd rc %t.a %t.o
RUN: llvm-ar p %t.a > %bsd.o
RUN: cmp %bsd.o %t.o

RUN: rm -f %t.a
RUN: llvm-ar -format=gnu rc %t.a %t.o
RUN: llvm-ar p %t.a > %gnu.o
RUN: cmp %gnu.o %t.o

RUN: rm -f %t.a
RUN: llvm-ar -format=darwin rc %t.a %t.o
RUN: llvm-ar p %t.a > %darwin.o
RUN: not cmp %darwin.o %t.o
15 changes: 11 additions & 4 deletions tools/llvm-ar/llvm-ar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,14 @@ static cl::opt<bool> MRI("M", cl::desc(""));
static cl::opt<std::string> Plugin("plugin", cl::desc("plugin (ignored for compatibility"));

namespace {
enum Format { Default, GNU, BSD };
enum Format { Default, GNU, BSD, DARWIN };
}

static cl::opt<Format>
FormatOpt("format", cl::desc("Archive format to create"),
cl::values(clEnumValN(Default, "default", "default"),
clEnumValN(GNU, "gnu", "gnu"),
clEnumValN(DARWIN, "darwin", "darwin"),
clEnumValN(BSD, "bsd", "bsd")));

static std::string Options;
Expand Down Expand Up @@ -623,8 +624,9 @@ computeNewArchiveMembers(ArchiveOperation Operation,
}

static object::Archive::Kind getDefaultForHost() {
return Triple(sys::getProcessTriple()).isOSDarwin() ? object::Archive::K_BSD
: object::Archive::K_GNU;
return Triple(sys::getProcessTriple()).isOSDarwin()
? object::Archive::K_DARWIN
: object::Archive::K_GNU;
}

static object::Archive::Kind getKindFromMember(const NewArchiveMember &Member) {
Expand All @@ -633,7 +635,7 @@ static object::Archive::Kind getKindFromMember(const NewArchiveMember &Member) {

if (OptionalObject)
return isa<object::MachOObjectFile>(**OptionalObject)
? object::Archive::K_BSD
? object::Archive::K_DARWIN
: object::Archive::K_GNU;

// squelch the error in case we had a non-object file
Expand Down Expand Up @@ -672,6 +674,11 @@ performWriteOperation(ArchiveOperation Operation,
fail("Only the gnu format has a thin mode");
Kind = object::Archive::K_BSD;
break;
case DARWIN:
if (Thin)
fail("Only the gnu format has a thin mode");
Kind = object::Archive::K_DARWIN;
break;
}

std::pair<StringRef, std::error_code> Result =
Expand Down

0 comments on commit 13f0c80

Please sign in to comment.