From 93bfb99ffc7ea9c828c3679ecc61719de453a8d0 Mon Sep 17 00:00:00 2001 From: Jordan Rupprecht Date: Mon, 14 Jan 2019 21:11:46 +0000 Subject: [PATCH] [llvm-ar] Flatten thin archives. Summary: Normal behavior for GNU ar is to flatten thin archives when adding them to another thin archive, i.e. add the members directly instead of nesting the archive. Some refactoring done as part of this patch to ease things: - Consolidate `addMember`/`addLibMember` methods - Rename `addMember` to `addChildMember` to make it more visibly different at the call site that an archive child is passed instead of a regular member - Pass in a separate vector and splice it back into position instead of passing a vector + optional Pos (which makes expanding libs tricky) This fixes PR37530 as raised by https://github.com/ClangBuiltLinux/linux/issues/279. Reviewers: mstorsjo, pcc, ruiu Reviewed By: mstorsjo Subscribers: llvm-commits, tpimh, nickdesaulniers Differential Revision: https://reviews.llvm.org/D56508 llvm-svn: 351120 --- llvm/test/tools/llvm-ar/Inputs/a-plus-b.a | 6 ++ llvm/test/tools/llvm-ar/Inputs/a.txt | 1 + llvm/test/tools/llvm-ar/Inputs/b.txt | 1 + llvm/test/tools/llvm-ar/Inputs/c.txt | 1 + llvm/test/tools/llvm-ar/Inputs/d.txt | 1 + .../tools/llvm-ar/Inputs/nested-thin-archive.a | 7 ++ .../llvm-ar/flatten-thin-archive-recursive.test | 13 ++++ llvm/test/tools/llvm-ar/flatten-thin-archive.test | 18 +++++ llvm/tools/llvm-ar/llvm-ar.cpp | 90 +++++++++++----------- 9 files changed, 93 insertions(+), 45 deletions(-) create mode 100644 llvm/test/tools/llvm-ar/Inputs/a-plus-b.a create mode 100644 llvm/test/tools/llvm-ar/Inputs/a.txt create mode 100644 llvm/test/tools/llvm-ar/Inputs/b.txt create mode 100644 llvm/test/tools/llvm-ar/Inputs/c.txt create mode 100644 llvm/test/tools/llvm-ar/Inputs/d.txt create mode 100644 llvm/test/tools/llvm-ar/Inputs/nested-thin-archive.a create mode 100644 llvm/test/tools/llvm-ar/flatten-thin-archive-recursive.test create mode 100644 llvm/test/tools/llvm-ar/flatten-thin-archive.test diff --git a/llvm/test/tools/llvm-ar/Inputs/a-plus-b.a b/llvm/test/tools/llvm-ar/Inputs/a-plus-b.a new file mode 100644 index 0000000..a58406e --- /dev/null +++ b/llvm/test/tools/llvm-ar/Inputs/a-plus-b.a @@ -0,0 +1,6 @@ +! +// 14 ` +a.txt/ +b.txt/ +/0 0 0 0 644 11 ` +/7 0 0 0 644 11 ` diff --git a/llvm/test/tools/llvm-ar/Inputs/a.txt b/llvm/test/tools/llvm-ar/Inputs/a.txt new file mode 100644 index 0000000..12d91b6 --- /dev/null +++ b/llvm/test/tools/llvm-ar/Inputs/a.txt @@ -0,0 +1 @@ +a-contents diff --git a/llvm/test/tools/llvm-ar/Inputs/b.txt b/llvm/test/tools/llvm-ar/Inputs/b.txt new file mode 100644 index 0000000..aee476f --- /dev/null +++ b/llvm/test/tools/llvm-ar/Inputs/b.txt @@ -0,0 +1 @@ +b-contents diff --git a/llvm/test/tools/llvm-ar/Inputs/c.txt b/llvm/test/tools/llvm-ar/Inputs/c.txt new file mode 100644 index 0000000..a80e36bc3 --- /dev/null +++ b/llvm/test/tools/llvm-ar/Inputs/c.txt @@ -0,0 +1 @@ +c-contents diff --git a/llvm/test/tools/llvm-ar/Inputs/d.txt b/llvm/test/tools/llvm-ar/Inputs/d.txt new file mode 100644 index 0000000..20d7aa0 --- /dev/null +++ b/llvm/test/tools/llvm-ar/Inputs/d.txt @@ -0,0 +1 @@ +d-contents diff --git a/llvm/test/tools/llvm-ar/Inputs/nested-thin-archive.a b/llvm/test/tools/llvm-ar/Inputs/nested-thin-archive.a new file mode 100644 index 0000000..312e8de --- /dev/null +++ b/llvm/test/tools/llvm-ar/Inputs/nested-thin-archive.a @@ -0,0 +1,7 @@ +! +// 20 ` +a-plus-b.a/ +c.txt/ + +/0 0 0 0 644 202 ` +/12 0 0 0 644 11 ` diff --git a/llvm/test/tools/llvm-ar/flatten-thin-archive-recursive.test b/llvm/test/tools/llvm-ar/flatten-thin-archive-recursive.test new file mode 100644 index 0000000..fdd752d --- /dev/null +++ b/llvm/test/tools/llvm-ar/flatten-thin-archive-recursive.test @@ -0,0 +1,13 @@ +# Since llvm-ar cannot create thin archives that contain any thin archives, +# nested-thin-archive.a is a manually constructed thin archive that contains +# another (unflattened) thin archive. +# This test ensures that flat archives are recursively flattened. + +RUN: rm -f %t.a +RUN: llvm-ar rcsT %t.a %S/Inputs/nested-thin-archive.a %S/Inputs/d.txt +RUN: llvm-ar t %t.a | FileCheck %s + +CHECK: a.txt +CHECK-NEXT: b.txt +CHECK-NEXT: c.txt +CHECK-NEXT: d.txt diff --git a/llvm/test/tools/llvm-ar/flatten-thin-archive.test b/llvm/test/tools/llvm-ar/flatten-thin-archive.test new file mode 100644 index 0000000..430f48f --- /dev/null +++ b/llvm/test/tools/llvm-ar/flatten-thin-archive.test @@ -0,0 +1,18 @@ +# This test creates a thin archive that contains a thin archive, a regular +# archive, and a file. +# +# The inner thin archive should be flattened, but the regular archive should +# not. The order of members in the archive should match the input order, with +# flattened members appearing together. + +RUN: touch %t-a.txt %t-b.txt %t-c.txt %t-d.txt %t-e.txt +RUN: rm -f %t-a-plus-b.a %t.a +RUN: llvm-ar rcsT %t-a-plus-b.a %t-a.txt %t-b.txt +RUN: llvm-ar rcs %t-d-plus-e.a %t-d.txt %t-e.txt +RUN: llvm-ar rcsT %t.a %t-a-plus-b.a %t-c.txt %t-d-plus-e.a +RUN: llvm-ar t %t.a | FileCheck %s + +CHECK: a.txt +CHECK-NEXT: b.txt +CHECK-NEXT: c.txt +CHECK-NEXT: -d-plus-e.a diff --git a/llvm/tools/llvm-ar/llvm-ar.cpp b/llvm/tools/llvm-ar/llvm-ar.cpp index d3d82d3..15a1ede 100644 --- a/llvm/tools/llvm-ar/llvm-ar.cpp +++ b/llvm/tools/llvm-ar/llvm-ar.cpp @@ -536,52 +536,53 @@ static void performReadOperation(ArchiveOperation Operation, exit(1); } -static void addMember(std::vector &Members, - StringRef FileName, int Pos = -1) { - Expected NMOrErr = - NewArchiveMember::getFile(FileName, Deterministic); - failIfError(NMOrErr.takeError(), FileName); - - // Use the basename of the object path for the member name. - NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName); - - if (Pos == -1) - Members.push_back(std::move(*NMOrErr)); - else - Members[Pos] = std::move(*NMOrErr); -} - -static void addMember(std::vector &Members, - const object::Archive::Child &M, int Pos = -1) { +static void addChildMember(std::vector &Members, + const object::Archive::Child &M, + bool FlattenArchive = false) { if (Thin && !M.getParent()->isThin()) fail("Cannot convert a regular archive to a thin one"); Expected NMOrErr = NewArchiveMember::getOldMember(M, Deterministic); failIfError(NMOrErr.takeError()); - if (Pos == -1) - Members.push_back(std::move(*NMOrErr)); - else - Members[Pos] = std::move(*NMOrErr); + if (FlattenArchive && + identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { + Expected FileNameOrErr = M.getName(); + failIfError(FileNameOrErr.takeError()); + object::Archive &Lib = readLibrary(*FileNameOrErr); + // When creating thin archives, only flatten if the member is also thin. + if (!Thin || Lib.isThin()) { + Error Err = Error::success(); + // Only Thin archives are recursively flattened. + for (auto &Child : Lib.children(Err)) + addChildMember(Members, Child, /*FlattenArchive=*/Thin); + failIfError(std::move(Err)); + return; + } + } + Members.push_back(std::move(*NMOrErr)); } -static void addLibMember(std::vector &Members, - StringRef FileName) { +static void addMember(std::vector &Members, + StringRef FileName, bool FlattenArchive = false) { Expected NMOrErr = NewArchiveMember::getFile(FileName, Deterministic); failIfError(NMOrErr.takeError(), FileName); - if (identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { + if (FlattenArchive && + identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { object::Archive &Lib = readLibrary(FileName); - Error Err = Error::success(); - - for (auto &Child : Lib.children(Err)) - addMember(Members, Child); - - failIfError(std::move(Err)); - } else { - // Use the basename of the object path for the member name. - NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName); - Members.push_back(std::move(*NMOrErr)); + // When creating thin archives, only flatten if the member is also thin. + if (!Thin || Lib.isThin()) { + Error Err = Error::success(); + // Only Thin archives are recursively flattened. + for (auto &Child : Lib.children(Err)) + addChildMember(Members, Child, /*FlattenArchive=*/Thin); + failIfError(std::move(Err)); + return; + } } + // Use the basename of the object path for the member name. + NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName); + Members.push_back(std::move(*NMOrErr)); } enum InsertAction { @@ -670,7 +671,7 @@ computeNewArchiveMembers(ArchiveOperation Operation, computeInsertAction(Operation, Child, Name, MemberI); switch (Action) { case IA_AddOldMember: - addMember(Ret, Child); + addChildMember(Ret, Child); break; case IA_AddNewMember: addMember(Ret, *MemberI); @@ -678,7 +679,7 @@ computeNewArchiveMembers(ArchiveOperation Operation, case IA_Delete: break; case IA_MoveOldMember: - addMember(Moved, Child); + addChildMember(Moved, Child); break; case IA_MoveNewMember: addMember(Moved, *MemberI); @@ -709,17 +710,16 @@ computeNewArchiveMembers(ArchiveOperation Operation, if (AddLibrary) { assert(Operation == QuickAppend); for (auto &Member : Members) - addLibMember(Ret, Member); + addMember(Ret, Member, /*FlattenArchive=*/true); return Ret; } - for (unsigned I = 0; I != Members.size(); ++I) - Ret.insert(Ret.begin() + InsertPos, NewArchiveMember()); - Pos = InsertPos; - for (auto &Member : Members) { - addMember(Ret, Member, Pos); - ++Pos; - } + std::vector NewMembers; + for (auto &Member : Members) + addMember(NewMembers, Member, /*FlattenArchive=*/Thin); + Ret.reserve(Ret.size() + NewMembers.size()); + std::move(NewMembers.begin(), NewMembers.end(), + std::inserter(Ret, std::next(Ret.begin(), InsertPos))); return Ret; } @@ -897,7 +897,7 @@ static void runMRIScript() { { Error Err = Error::success(); for (auto &Member : Lib.children(Err)) - addMember(NewMembers, Member); + addChildMember(NewMembers, Member); failIfError(std::move(Err)); } break; -- 2.7.4