diff --git a/llvm/test/tools/llvm-libtool-darwin/Inputs/helloMachO.yaml b/llvm/test/tools/llvm-libtool-darwin/Inputs/helloMachO.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-libtool-darwin/Inputs/helloMachO.yaml @@ -0,0 +1,56 @@ +# int hello() { +# return 0; +# } +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 2 + sizeofcmds: 176 + flags: 0x00002000 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 152 + segname: '' + vmaddr: 0 + vmsize: 8 + fileoff: 312 + filesize: 8 + maxprot: 7 + initprot: 7 + nsects: 1 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000000 + size: 8 + offset: 0x00000138 + align: 4 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: 554889E531C05DC3 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 320 + nsyms: 1 + stroff: 336 + strsize: 8 +LinkEditData: + NameList: + - n_strx: 1 + n_type: 0x0F + n_sect: 1 + n_desc: 0 + n_value: 0 + StringTable: + - '' + - _hello +... diff --git a/llvm/test/tools/llvm-libtool-darwin/Inputs/mainMachO.yaml b/llvm/test/tools/llvm-libtool-darwin/Inputs/mainMachO.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-libtool-darwin/Inputs/mainMachO.yaml @@ -0,0 +1,57 @@ +# int main() { +# return 0; +# } +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 2 + sizeofcmds: 176 + flags: 0x00002000 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 152 + segname: '' + vmaddr: 0 + vmsize: 15 + fileoff: 312 + filesize: 15 + maxprot: 7 + initprot: 7 + nsects: 1 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000000 + size: 15 + offset: 0x00000138 + align: 4 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: 554889E531C0C745FC000000005DC3 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 328 + nsyms: 1 + stroff: 344 + strsize: 8 +LinkEditData: + NameList: + - n_strx: 1 + n_type: 0x0F + n_sect: 1 + n_desc: 0 + n_value: 0 + StringTable: + - '' + - _main + - '' +... 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,56 @@ +## This test checks that a correct static library is created. + +# RUN: yaml2obj %S/Inputs/helloMachO.yaml -o %t-hello.o +# RUN: yaml2obj %S/Inputs/mainMachO.yaml -o %t-main.o + +# RUN: rm -rf %t.lib +# RUN: llvm-libtool-darwin -static -o %t.lib %t-hello.o %t-main.o + +## Check that binaries are present: +# RUN: llvm-ar tv %t.lib | \ +# RUN: FileCheck %s --check-prefix=CHECK-NAMES --implicit-check-not=.o + +# CHECK-NAMES: hello.o +# CHECK-NAMES: main.o + +## Check that symbols are present: +# RUN: llvm-nm %t.lib | \ +# RUN: FileCheck %s --check-prefix=CHECK-SYMBOLS --implicit-check-not=T + +# CHECK-SYMBOLS: T _hello +# CHECK-SYMBOLS: T _main + +## Check that output archive is in Darwin format: +# RUN: llvm-objdump --macho --archive-headers %t.lib | \ +# RUN: FileCheck %s --check-prefix=FORMAT --implicit-check-not=.o + +# FORMAT: __.SYMDEF +# FORMAT-NEXT: hello.o +# FORMAT-NEXT: main.o + +## Checking that the output file is overwritten: +# RUN: llvm-libtool-darwin -static -o %t.lib %t-main.o %t-hello.o +# RUN: llvm-ar tv %t.lib | \ +# RUN: FileCheck %s --check-prefix=OVERWRITE-NAMES --implicit-check-not=.o +# RUN: llvm-nm %t.lib | \ +# RUN: FileCheck %s --check-prefix=OVERWRITE-SYMBOLS --implicit-check-not=T + +# OVERWRITE-NAMES: main.o +# OVERWRITE-NAMES: hello.o +# OVERWRITE-SYMBOLS: T _main +# OVERWRITE-SYMBOLS: T _hello + +## Duplicate a binary: +# RUN: llvm-libtool-darwin -static -o %t.lib %t-hello.o %t-main.o %t-hello.o +# RUN: llvm-ar tv %t.lib | \ +# RUN: FileCheck %s --check-prefix=DUPLICATE-NAMES --implicit-check-not=.o +# RUN: llvm-nm %t.lib | \ +# RUN: FileCheck %s --check-prefix=DUPLICATE-SYMBOLS --implicit-check-not=T + +# DUPLICATE-NAMES: hello.o +# DUPLICATE-NAMES: main.o +# DUPLICATE-NAMES: hello.o + +# DUPLICATE-SYMBOLS: T _hello +# DUPLICATE-SYMBOLS: T _main +# DUPLICATE-SYMBOLS: T _hello 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,100 @@ +## This test checks that an error is thrown in case of invalid argument(s). + +## Missing input file: +# RUN: not llvm-libtool-darwin -o libOut.a 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NO-INPUT + +# NO-INPUT: Must specify at least 1 positional argument + +## Missing output file: +# RUN: not llvm-libtool-darwin %t 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NO-OUTPUT + +# NO-OUTPUT: for the --output option: must be specified at least once! + +## Missing argument to -o: +# RUN: not llvm-libtool-darwin %t -o 2>&1 | \ +# RUN: FileCheck %s --check-prefix=MISSING + +# MISSING: for the -o option: requires a value! + +## Missing both -static and -dynamic options: +# RUN: not llvm-libtool-darwin -o libOut.a %t 2>&1 | \ +# RUN: FileCheck %s --check-prefix=MISSING-OPERATION + +# MISSING-OPERATION: Library Type: option: must be specified at least once! + +## Passing both -static and -dynamic options: +# RUN: not llvm-libtool-darwin -static -dynamic -o libOut.a %t 2>&1 | \ +# RUN: FileCheck %s --check-prefix=DOUBLE-OPERATION + +# DOUBLE-OPERATION: must occur exactly one time! + +## Input file not found: +# RUN: not llvm-libtool-darwin -static -o libOut.a %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 libOut.a %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 libOut.a %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/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 @@ -39,10 +39,91 @@ static cl::list InputFiles(cl::Positional, cl::desc(""), cl::OneOrMore); +enum Operation { Static, Dynamic }; +static cl::opt LibraryOperation( + cl::desc("Library Type: "), + cl::values( + clEnumValN(Static, "static", + "Produce a statically linked library from the input files"), + clEnumValN(Dynamic, "dynamic", + "Produce a shared dynamic library from the input files")), + cl::Required); + +static cl::opt + Deterministic("D", cl::desc("Use zeroes for timestamps and UIDs/GIDs"), + cl::init(false)); + +LLVM_ATTRIBUTE_NORETURN static void reportError(Twine Message) { + WithColor::error(errs(), ToolName) << Message << "\n"; + errs().flush(); + exit(EXIT_FAILURE); +} + +LLVM_ATTRIBUTE_NORETURN static void reportError(StringRef File, Error E) { + assert(E); + std::string Buf; + raw_string_ostream OS(Buf); + logAllUnhandledErrors(std::move(E), OS); + OS.flush(); + WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf; + exit(EXIT_FAILURE); +} + +static void verifyDarwinObject(const NewArchiveMember &Member) { + auto MBRef = Member.Buf->getMemBufferRef(); + Expected> ObjOrErr = + object::ObjectFile::createObjectFile(MBRef); + + // throw error if not a valid object file + if (Error E = ObjOrErr.takeError()) + reportError(Member.MemberName, std::move(E)); + + // throw error if not in darwin format + if (!isa(**ObjOrErr)) + reportError("'" + Member.MemberName + "': format not supported"); +} + +static void addMember(std::vector &Members, + StringRef FileName) { + Expected NMOrErr = + NewArchiveMember::getFile(FileName, Deterministic); + + if (Error E = NMOrErr.takeError()) + reportError(FileName, std::move(E)); + + // 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 + verifyDarwinObject(*NMOrErr); + + Members.push_back(std::move(*NMOrErr)); +} + +static void createStaticLibrary() { + std::vector NewMembers; + for (const auto &Member : InputFiles) + addMember(NewMembers, Member); + + if (Error E = writeArchive(OutputFile, NewMembers, + /*symtab=*/true, + /*kind=*/object::Archive::K_DARWIN, Deterministic, + /*thin=*/false)) + reportError(OutputFile, std::move(E)); +} + int main(int Argc, char **Argv) { InitLLVM X(Argc, Argv); ToolName = Argv[0]; cl::ParseCommandLineOptions(Argc, Argv, "llvm-libtool\n"); + switch (LibraryOperation) { + case Static: + createStaticLibrary(); + break; + case Dynamic: + reportError("dynamic option is currently unsupported"); + } return EXIT_SUCCESS; }