diff --git a/llvm/test/tools/llvm-libtool-darwin/archive-flattening.test b/llvm/test/tools/llvm-libtool-darwin/archive-flattening.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-libtool-darwin/archive-flattening.test @@ -0,0 +1,95 @@ +## This test checks that an archive is flattened correctly. + +# RUN: yaml2obj %S/Inputs/input1.yaml -o %t-input1.o +# RUN: yaml2obj %S/Inputs/input2.yaml -o %t-input2.o + +## Cannot read archive: +# RUN: echo '!' > %t-invalid-archive.lib +# RUN: echo 'invalid' >> %t-invalid-archive.lib +# RUN: not llvm-libtool-darwin -static -o %t.lib %t-invalid-archive.lib 2>&1 | \ +# RUN: FileCheck %s --check-prefix=INVALID-ARCHIVE -DARCHIVE=%t-invalid-archive.lib + +# INVALID-ARCHIVE: error: '[[ARCHIVE]]': truncated or malformed archive + +## Archive member not an object file: +# RUN: rm -f %t.not-object.ar +# RUN: touch %t.txt +# RUN: llvm-ar cr %t.not-object.ar %t.txt +# RUN: not llvm-libtool-darwin -static -o %t.lib %t.not-object.ar 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NOT-OBJECT -DARCHIVE=%t.not-object.ar -DFILE=%basename_t.tmp.txt + +## Do not recursively flatten archives: +# RUN: rm -f %t.inner +# RUN: rm -f %t.outer +# RUN: llvm-ar cr %t.inner %t-input1.o +# RUN: llvm-ar cr %t.outer %t.inner +# RUN: not llvm-libtool-darwin -static -o %t.lib %t.outer 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NOT-OBJECT -DARCHIVE=%t.outer -DFILE=%basename_t.tmp.inner + +# NOT-OBJECT: error: '[[ARCHIVE]]': '[[FILE]]': The file was not recognized as a valid object file + +## Archive member not a Mach-O object file: +# RUN: rm -f %t.not-macho.ar +# RUN: yaml2obj %s -o %t.elf +# RUN: llvm-ar cr %t.not-macho.ar %t.elf +# RUN: not llvm-libtool-darwin -static -o %t.lib %t.not-macho.ar 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NOT-MACHO -DARCHIVE=%t.not-macho.ar -DFILE=%basename_t.tmp.elf + +# NOT-MACHO: error: '[[ARCHIVE]]': '[[FILE]]': format not supported + +## Input a correct archive: +# RUN: rm -f %t.correct.ar +# RUN: llvm-ar cr %t.correct.ar %t-input1.o %t-input2.o +# RUN: llvm-libtool-darwin -static -o %t.lib %t.correct.ar + +## Check that binaries are present: +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=CHECK-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp + +# CHECK-NAMES: [[PREFIX]]-input1.o +# CHECK-NAMES-NEXT: [[PREFIX]]-input2.o + +## Check that symbols are present: +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=CHECK-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +# CHECK-SYMBOLS: Archive map +# CHECK-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o +# CHECK-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o +# CHECK-SYMBOLS-EMPTY: + +## Check that output archive is in Mach-O format: +# RUN: llvm-objdump --macho --archive-headers %t.lib | \ +# RUN: FileCheck %s --check-prefix=FORMAT -DPREFIX=%basename_t.tmp -DARCHIVE=%t.lib + +# FORMAT: Archive : [[ARCHIVE]] +# FORMAT-NEXT: __.SYMDEF +# FORMAT-NEXT: [[PREFIX]]-input1.o +# FORMAT-NEXT: [[PREFIX]]-input2.o +# FORMAT-NOT: {{.}} + +## Passing both archive and object file: +# RUN: llvm-libtool-darwin -static -o %t.lib %t-input2.o %t.correct.ar %t-input1.o +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=BOTH-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=BOTH-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +# BOTH-NAMES: [[PREFIX]]-input2.o +# BOTH-NAMES-NEXT: [[PREFIX]]-input1.o +# BOTH-NAMES-NEXT: [[PREFIX]]-input2.o +# BOTH-NAMES-NEXT: [[PREFIX]]-input1.o + +# BOTH-SYMBOLS: Archive map +# BOTH-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o +# BOTH-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o +# BOTH-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o +# BOTH-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o +# BOTH-SYMBOLS-EMPTY: + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 diff --git a/llvm/tools/llvm-libtool-darwin/CMakeLists.txt b/llvm/tools/llvm-libtool-darwin/CMakeLists.txt --- a/llvm/tools/llvm-libtool-darwin/CMakeLists.txt +++ b/llvm/tools/llvm-libtool-darwin/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + BinaryFormat Object Support ) diff --git a/llvm/tools/llvm-libtool-darwin/LLVMBuild.txt b/llvm/tools/llvm-libtool-darwin/LLVMBuild.txt --- a/llvm/tools/llvm-libtool-darwin/LLVMBuild.txt +++ b/llvm/tools/llvm-libtool-darwin/LLVMBuild.txt @@ -17,4 +17,4 @@ type = Tool name = llvm-libtool-darwin parent = Tools -required_libraries = Object Support +required_libraries = BinaryFormat Object Support diff --git a/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp b/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp --- a/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp +++ b/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/BinaryFormat/Magic.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" @@ -58,11 +59,26 @@ return Error::success(); } -static Error addMember(std::vector &Members, - StringRef FileName) { +static Error addChildMember(std::vector &Members, + const object::Archive::Child &M) { Expected NMOrErr = - NewArchiveMember::getFile(FileName, /*Deterministic=*/true); + NewArchiveMember::getOldMember(M, /*Deterministic=*/true); + if (!NMOrErr) + return NMOrErr.takeError(); + + // Verify that Member is a Mach-O object file. + if (Error E = verifyMachOObject(*NMOrErr)) + return E; + + Members.push_back(std::move(*NMOrErr)); + return Error::success(); +} +static Error +addMember(std::vector &Members, StringRef FileName, + std::vector> &ArchiveBuffers) { + Expected NMOrErr = + NewArchiveMember::getFile(FileName, /*Deterministic=*/true); if (!NMOrErr) return createFileError(FileName, NMOrErr.takeError()); @@ -70,6 +86,27 @@ // name. NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName); + // Flatten archives. + if (identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { + Expected> LibOrErr = + object::Archive::create(NMOrErr->Buf->getMemBufferRef()); + if (!LibOrErr) + return createFileError(FileName, LibOrErr.takeError()); + object::Archive &Lib = **LibOrErr; + + Error Err = Error::success(); + for (const object::Archive::Child &Child : Lib.children(Err)) + if (Error E = addChildMember(Members, Child)) + return createFileError(FileName, std::move(E)); + if (Err) + return createFileError(FileName, std::move(Err)); + + // Update vector ArchiveBuffers with the MemoryBuffers to transfer + // ownership. + ArchiveBuffers.push_back(std::move(NMOrErr->Buf)); + return Error::success(); + } + // Verify that Member is a Mach-O object file. if (Error E = verifyMachOObject(*NMOrErr)) return E; @@ -80,8 +117,9 @@ static Error createStaticLibrary() { std::vector NewMembers; + std::vector> ArchiveBuffers; for (StringRef Member : InputFiles) - if (Error E = addMember(NewMembers, Member)) + if (Error E = addMember(NewMembers, Member, ArchiveBuffers)) return E; if (Error E = writeArchive(OutputFile, NewMembers,