diff --git a/llvm/docs/CommandGuide/llvm-libtool-darwin.rst b/llvm/docs/CommandGuide/llvm-libtool-darwin.rst --- a/llvm/docs/CommandGuide/llvm-libtool-darwin.rst +++ b/llvm/docs/CommandGuide/llvm-libtool-darwin.rst @@ -41,6 +41,10 @@ Specify the output file name. Must be specified exactly once. +.. option:: -static + + Produces a static library from the input files. + EXIT STATUS ----------- diff --git a/llvm/test/tools/llvm-libtool-darwin/basic.test b/llvm/test/tools/llvm-libtool-darwin/basic.test deleted file mode 100644 --- a/llvm/test/tools/llvm-libtool-darwin/basic.test +++ /dev/null @@ -1,10 +0,0 @@ -# This test checks that main exits normally (EC 0) for correct input/output args. - -# RUN: yaml2obj %S/Inputs/input1.yaml -o %t-input1.o -# RUN: yaml2obj %S/Inputs/input2.yaml -o %t-input2.o - -## Pass single input: -# RUN: llvm-libtool-darwin -o %t.lib %t-input1.o - -## Pass multiple inputs: -# RUN: llvm-libtool-darwin -o %t.lib %t-input1.o %t-input2.o diff --git a/llvm/test/tools/llvm-libtool-darwin/create-static-lib.test b/llvm/test/tools/llvm-libtool-darwin/create-static-lib.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-libtool-darwin/create-static-lib.test @@ -0,0 +1,62 @@ +## This test checks that a correct static library is created. + +# RUN: yaml2obj %S/Inputs/input1.yaml -o %t-input1.o +# RUN: yaml2obj %S/Inputs/input2.yaml -o %t-input2.o + +# RUN: rm -rf %t.lib +# RUN: llvm-libtool-darwin -static -o %t.lib %t-input1.o %t-input2.o + +## Check that binaries are present: +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=CHECK-NAMES + +# CHECK-NAMES: input1.o +# CHECK-NAMES-NEXT: input2.o +# CHECK-NAMES-NOT: {{.}} + +## Check that symbols are present: +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=CHECK-SYMBOLS + +# CHECK-SYMBOLS: _symbol1 in +# CHECK-SYMBOLS: _symbol2 in +# CHECK-SYMBOLS-EMPTY: + +## Check that output archive is in Darwin format: +# RUN: llvm-objdump --macho --archive-headers %t.lib | \ +# RUN: FileCheck %s --check-prefix=FORMAT + +# FORMAT: __.SYMDEF +# FORMAT-NEXT: input1.o +# FORMAT-NEXT: input2.o +# FORMAT_NOT: {{.}} + +## Checking that the output file is overwritten: +# RUN: llvm-libtool-darwin -static -o %t.lib %t-input2.o +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=OVERWRITE-NAMES +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=OVERWRITE-SYMBOLS + +# OVERWRITE-NAMES: input2.o +# OVERWRITE-NAMES-NOT: {{.}} + +# OVERWRITE-SYMBOLS: _symbol2 in +# OVERWRITE-SYMBOLS-EMPTY: + +## Duplicate a binary: +# RUN: llvm-libtool-darwin -static -o %t.lib %t-input1.o %t-input2.o %t-input1.o +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=DUPLICATE-NAMES +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=DUPLICATE-SYMBOLS + +# DUPLICATE-NAMES: input1.o +# DUPLICATE-NAMES: input2.o +# DUPLICATE-NAMES: input1.o +# DUPLICATE-NAMES-NOT: {{.}} + +# DUPLICATE-SYMBOLS: _symbol1 in +# DUPLICATE-SYMBOLS: _symbol2 in +# DUPLICATE-SYMBOLS: _symbol1 in +# DUPLICATE-SYMBOLS-EMPTY: diff --git a/llvm/test/tools/llvm-libtool-darwin/invalid-arguments.test b/llvm/test/tools/llvm-libtool-darwin/invalid-arguments.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-libtool-darwin/invalid-arguments.test @@ -0,0 +1,76 @@ +## This test checks that an error is thrown in case of invalid arguments to -static option + +## Missing -static option: +# RUN: not llvm-libtool-darwin -o %t.lib %t.input 2>&1 | \ +# RUN: FileCheck %s --check-prefix=MISSING-OPERATION + +# MISSING-OPERATION: Library Type: option: must be specified at least once! + +## Input file not found: +# RUN: not llvm-libtool-darwin -static -o %t.lib %t.missing 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NO-FILE -DFILE=%t.missing + +# NO-FILE: '[[FILE]]': {{[nN]}}o such file or directory + +## Input file is not an object file: +# RUN: touch %t.invalid +# RUN: not llvm-libtool-darwin -static -o %t.lib %t.invalid 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NOT-OBJECT + +# NOT-OBJECT: The file was not recognized as a valid object file + +## Input file is not a MachO object file: +# RUN: yaml2obj %s -o %t +# RUN: not llvm-libtool-darwin -static -o $t.lib %t 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NOT-MACHO + +# NOT-MACHO: format not supported + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000001 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + EntSize: 0x0000000000000001 + Content: 004743433A2028474E552920382E332E3120323031393131323120285265642048617420382E332E312D352900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 +Symbols: + - Name: foo.c + Type: STT_FILE + Index: SHN_ABS + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .comment + Type: STT_SECTION + Section: .comment +... 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 + 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 = Support +required_libraries = 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,10 +10,15 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Object/ArchiveWriter.h" +#include "llvm/Object/MachO.h" +#include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/WithColor.h" using namespace llvm; +using namespace llvm::object; static cl::opt OutputFile("output", cl::desc("Specify output filename"), @@ -25,7 +30,77 @@ static cl::list InputFiles(cl::Positional, cl::desc(""), cl::OneOrMore); +enum Operation { Static }; +static cl::opt LibraryOperation( + cl::desc("Library Type: "), + cl::values( + clEnumValN(Static, "static", + "Produce a statically linked library from the input files")), + cl::Required); + +static Error verifyDarwinObject(const NewArchiveMember &Member) { + auto MBRef = Member.Buf->getMemBufferRef(); + Expected> ObjOrErr = + object::ObjectFile::createObjectFile(MBRef); + + // Throw error if not a valid object file. + if (!ObjOrErr) + return createFileError(Member.MemberName, ObjOrErr.takeError()); + + // Throw error if not in darwin format. + if (!isa(**ObjOrErr)) + return createStringError(std::errc::invalid_argument, + "'%s': format not supported", + Member.MemberName.data()); + + return Error::success(); +} + +static Error addMember(std::vector &Members, + StringRef FileName) { + Expected NMOrErr = + NewArchiveMember::getFile(FileName, /*Deterministic=*/true); + + if (!NMOrErr) + return createFileError(FileName, NMOrErr.takeError()); + + // For regular archives, use the basename of the object path for the member + // name. + NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName); + + // Verify that Member is a darwin object file. + if (Error E = verifyDarwinObject(*NMOrErr)) + return E; + + Members.push_back(std::move(*NMOrErr)); + return Error::success(); +} + +static Error createStaticLibrary() { + std::vector NewMembers; + for (const StringRef &Member : InputFiles) + if (Error E = addMember(NewMembers, Member)) + return E; + + if (Error E = writeArchive(OutputFile, NewMembers, + /*WriteSymtab=*/true, + /*Kind=*/object::Archive::K_DARWIN, + /*Deterministic=*/true, + /*Thin=*/false)) + return E; + return Error::success(); +} + int main(int Argc, char **Argv) { InitLLVM X(Argc, Argv); cl::ParseCommandLineOptions(Argc, Argv, "llvm-libtool-darwin\n"); + + switch (LibraryOperation) { + case Static: + if (Error E = createStaticLibrary()) { + WithColor::defaultErrorHandler(std::move(E)); + exit(EXIT_FAILURE); + } + break; + } }