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 @@ -21,30 +21,34 @@ -------- :program:`llvm-libtool-darwin` supports the following options: -.. option:: -h, -help - - Show help and usage for this command. +.. option:: -arch_only -.. option:: -help-list - - Show help and usage for this command without grouping the options - into categories. + Build a static library only for the specified `` and ignore all + other architectures in the files. .. option:: -color Use colors in output. -.. option:: -version +.. option:: -D - Display the version of this program. + Use zero for timestamps and UIDs/GIDs. This is set by default. -.. option:: -D +.. option:: -filelist - Use zero for timestamps and UIDs/GIDs. This is set by default. + Read input file names from ``. File names are specified in `` + one per line, separated only by newlines. Whitespace on a line is assumed + to be part of the filename. If the directory name, `dirname`, is also + specified then it is prepended to each file name in the ``. -.. option:: -U +.. option:: -h, -help + + Show help and usage for this command. - Use actual timestamps and UIDs/GIDs. +.. option:: -help-list + + Show help and usage for this command without grouping the options + into categories. .. option:: -o @@ -52,14 +56,15 @@ .. option:: -static - Produces a static library from the input files. + Produces a static library from the input files. -.. option:: -filelist +.. option:: -U + + Use actual timestamps and UIDs/GIDs. - Read input file names from ``. File names are specified in `` - one per line, separated only by newlines. Whitespace on a line is assumed - to be part of the filename. If the directory name, `dirname`, is also - specified then it is prepended to each file name in the ``. +.. option:: -version + + Display the version of this program. EXIT STATUS ----------- diff --git a/llvm/test/tools/llvm-libtool-darwin/cpu-subtype-matching.test b/llvm/test/tools/llvm-libtool-darwin/cpu-subtype-matching.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-libtool-darwin/cpu-subtype-matching.test @@ -0,0 +1,269 @@ +## This test checks that the CPU subtype matching logic is handled correctly. + +# RUN: yaml2obj %s --docnum=1 -o %t.armv6 +# RUN: yaml2obj %s --docnum=2 -o %t.armv7 + +# RUN: llvm-libtool-darwin -static -o %t.lib %t.armv6 %t.armv7 -arch_only armv7 + +## Check that only armv7 binary is present: +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=ARM-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp + +# ARM-NAMES: [[PREFIX]].armv7 + +## Check that only armv7 symbol is present: +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=ARM-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +# ARM-SYMBOLS: Archive map +# ARM-SYMBOLS-NEXT: _armv7 in [[PREFIX]].armv7 +# ARM-SYMBOLS-EMPTY: + +## armv6.yaml +## CPUTYPE: CPU_TYPE_ARM +## CPUSUBTYPE: CPU_SUBTYPE_ARM_V6 +--- !mach-o +FileHeader: + magic: 0xFEEDFACE + cputype: 0x0000000C + cpusubtype: 0x00000006 + filetype: 0x00000001 + ncmds: 2 + sizeofcmds: 148 + flags: 0x00002000 +LoadCommands: + - cmd: LC_SEGMENT + cmdsize: 124 + segname: '' + vmaddr: 0 + vmsize: 24 + fileoff: 296 + filesize: 24 + maxprot: 7 + initprot: 7 + nsects: 1 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000000 + size: 24 + offset: 0x00000128 + align: 2 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: 04D04DE208009FE500008DE504D08DE21EFF2FE100000000 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 328 + nsyms: 1 + stroff: 340 + strsize: 8 +LinkEditData: + NameList: + - n_strx: 1 + n_type: 0x0F + n_sect: 1 + n_desc: 0 + n_value: 0 + StringTable: + - '' + - _armv6 + - '' +... + +## armv7.yaml +## CPUTYPE: CPU_TYPE_ARM +## CPUSUBTYPE: CPU_SUBTYPE_ARM_V7 +--- !mach-o +FileHeader: + magic: 0xFEEDFACE + cputype: 0x0000000C + cpusubtype: 0x00000009 + filetype: 0x00000001 + ncmds: 2 + sizeofcmds: 148 + flags: 0x00002000 +LoadCommands: + - cmd: LC_SEGMENT + cmdsize: 124 + segname: '' + vmaddr: 0 + vmsize: 10 + fileoff: 280 + filesize: 10 + maxprot: 7 + initprot: 7 + nsects: 1 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000000 + size: 10 + offset: 0x00000118 + align: 1 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: 81B00020009001B07047 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 292 + nsyms: 1 + stroff: 304 + strsize: 8 +LinkEditData: + NameList: + - n_strx: 1 + n_type: 0x0F + n_sect: 1 + n_desc: 8 + n_value: 0 + StringTable: + - '' + - _armv7 + - '' +... + +# RUN: yaml2obj %s --docnum=3 -o %t.x86_64 +# RUN: yaml2obj %s --docnum=4 -o %t.x86_64_h + +# RUN: llvm-libtool-darwin -static -o %t.lib %t.x86_64 %t.x86_64_h -arch_only x86_64 + +## Check that only x86_64 binary is present: +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=X86-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp + +# X86-NAMES: [[PREFIX]].x86_64 + +## Check that only x86_64 symbol is present: +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=X86-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +# X86-SYMBOLS: Archive map +# X86-SYMBOLS-NEXT: _x86_64 in [[PREFIX]].x86_64 +# X86-SYMBOLS-EMPTY: + +## x86_64.yaml +## CPUTYPE: CPU_TYPE_X86_64 +## CPUSUBTYPE: CPU_SUBTYPE_X86_64_ALL +--- !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: + - '' + - _x86_64 + - '' +... + +## x86_64h.yaml +## CPUTYPE: CPU_TYPE_X86_64 +## CPUSUBTYPE: CPU_SUBTYPE_X86_64_H +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000008 + 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: + - '' + - _x86_64_h + - '' +... diff --git a/llvm/test/tools/llvm-libtool-darwin/universal-file-flattening.test b/llvm/test/tools/llvm-libtool-darwin/universal-file-flattening.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-libtool-darwin/universal-file-flattening.test @@ -0,0 +1,242 @@ +## This test checks that a universal file is flattened correctly. + +# RUN: yaml2obj %s -o %t-universal.o +# RUN: yaml2obj %S/Inputs/input1.yaml -o %t-input1.o +# RUN: yaml2obj %S/Inputs/input2.yaml -o %t-input2.o + +# RUN: llvm-libtool-darwin -static -o %t.lib %t-universal.o -arch_only arm64 + +## Check that the binary is present: +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=CHECK-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp + +# CHECK-NAMES: [[PREFIX]]-universal.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: _arm64 in [[PREFIX]]-universal.o +# CHECK-SYMBOLS-EMPTY: + +## Check that the output archive is in Darwin 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]]-universal.o +# FORMAT-NOT: {{.}} + +## Passing both a universal file and an object file: +# RUN: llvm-libtool-darwin -static -o %t.lib %t-universal.o %t-input1.o -arch_only x86_64 +# 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]]-universal.o +# BOTH-NAMES-NEXT: [[PREFIX]]-input1.o + +# BOTH-SYMBOLS: Archive map +# BOTH-SYMBOLS-NEXT: _x86_64 in [[PREFIX]]-universal.o +# BOTH-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o +# BOTH-SYMBOLS-EMPTY: + +## Passing both a universal file and an object file but filtering out the object file: +# RUN: llvm-libtool-darwin -static -o %t.lib %t-universal.o %t-input1.o -arch_only arm64 +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=CHECK-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=CHECK-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +## Universal file containing an archive: +# RUN: rm -f %t.ar +# RUN: llvm-ar cr %t.ar %t-input1.o %t-input2.o +# RUN: llvm-lipo %t.ar -create -output %t-fat-with-archive.o +# RUN: llvm-libtool-darwin -static -o %t.lib %t-fat-with-archive.o -arch_only x86_64 +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=ARCHIVE-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=ARCHIVE-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +# ARCHIVE-NAMES: [[PREFIX]]-input1.o +# ARCHIVE-NAMES-NEXT: [[PREFIX]]-input2.o + +# ARCHIVE-SYMBOLS: Archive map +# ARCHIVE-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o +# ARCHIVE-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o +# ARCHIVE-SYMBOLS-EMPTY: + +## Allow arch_only to be specified more than once (pick the last one): +# RUN: llvm-libtool-darwin -static -o %t.lib %t-universal.o -arch_only arm64 -arch_only x86_64 +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=DOUBLE-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=DOUBLE-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +# DOUBLE-NAMES: [[PREFIX]]-universal.o + +# DOUBLE-SYMBOLS: Archive map +# DOUBLE-SYMBOLS-NEXT: _x86_64 in [[PREFIX]]-universal.o +# DOUBLE-SYMBOLS-EMPTY: + +## Invalid architecture: +# RUN: not llvm-libtool-darwin -static -o %t.lib %t-universal.o -arch_only arch101 2>&1 | \ +# RUN: FileCheck %s --check-prefix=INVALID-ARCH + +# INVALID-ARCH: invalid architecture: arch101 +# INVALID-ARCH-NEXT: Valid architecture names are + +## Empty architecture: +# RUN: not llvm-libtool-darwin -static -o %t.lib %t-universal.o -arch_only "" 2>&1 | \ +# RUN: FileCheck %s --check-prefix=EMPTY-ARCH + +# EMPTY-ARCH: invalid architecture: +# EMPTY-ARCH-NEXT: Valid architecture names are + +## Missing architecture: +# RUN: not llvm-libtool-darwin -static -o %t.lib %t-universal.o -arch_only ppc 2>&1 | \ +# RUN: FileCheck %s --check-prefix=MISSING-ARCH + +# MISSING-ARCH: error: no library created (no object files in input files matching -arch_only ppc) + +## arch_only missing argument: +# RUN: not llvm-libtool-darwin -static -o %t.lib %t-universal.o -arch_only 2>&1 | \ +# RUN: FileCheck %s --check-prefix=REQUIRE-ARCH + +# REQUIRE-ARCH: for the --arch_only option: requires a value! + +## x86_64-arm64-universal.yaml +--- !fat-mach-o +FatHeader: + magic: 0xCAFEBABE + nfat_arch: 2 +FatArchs: + - cputype: 0x01000007 + cpusubtype: 0x00000003 + offset: 0x0000000000001000 + size: 352 + align: 12 + - cputype: 0x0100000C + cpusubtype: 0x00000000 + offset: 0x0000000000004000 + size: 384 + align: 14 +Slices: + - !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: + - '' + - _x86_64 + - '' + - !mach-o + FileHeader: + magic: 0xFEEDFACF + cputype: 0x0100000C + cpusubtype: 0x00000000 + filetype: 0x00000001 + ncmds: 2 + sizeofcmds: 176 + flags: 0x00002000 + reserved: 0x00000000 + LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 152 + segname: '' + vmaddr: 0 + vmsize: 24 + fileoff: 312 + filesize: 24 + maxprot: 7 + initprot: 7 + nsects: 1 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000000 + size: 24 + offset: 0x00000138 + align: 2 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: FF4300D1FF0F00B908008052E00308AAFF430091C0035FD6 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 336 + nsyms: 2 + stroff: 368 + strsize: 16 + LinkEditData: + NameList: + - n_strx: 7 + n_type: 0x0E + n_sect: 1 + n_desc: 0 + n_value: 0 + - n_strx: 1 + n_type: 0x0F + n_sect: 1 + n_desc: 0 + n_value: 0 + StringTable: + - '' + - _arm64 + - ltmp0 + - '' + - '' + - '' +... 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 @@ -2,6 +2,7 @@ BinaryFormat Object Support + TextAPI ) add_llvm_tool(llvm-libtool-darwin 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 = BinaryFormat Object Support +required_libraries = BinaryFormat Object Support TextAPI 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 @@ -13,11 +13,13 @@ #include "llvm/BinaryFormat/Magic.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/MachO.h" +#include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/LineIterator.h" #include "llvm/Support/WithColor.h" +#include "llvm/TextAPI/MachO/Architecture.h" using namespace llvm; using namespace llvm::object; @@ -33,6 +35,10 @@ cl::ZeroOrMore, cl::cat(LibtoolCategory)); +static cl::opt ArchType( + "arch_only", cl::desc("Specify architecture type for output library"), + cl::value_desc("arch_type"), cl::ZeroOrMore, cl::cat(LibtoolCategory)); + enum class Operation { Static }; static cl::opt LibraryOperation( @@ -92,7 +98,54 @@ return Error::success(); } -static Error verifyMachOObject(const NewArchiveMember &Member) { +static Error validateArchitectureName(StringRef ArchitectureName) { + if (!MachOObjectFile::isValidArch(ArchitectureName)) { + std::string Buf; + raw_string_ostream OS(Buf); + for (StringRef Arch : MachOObjectFile::getValidArchs()) + OS << Arch << " "; + + return createStringError( + std::errc::invalid_argument, + "invalid architecture: %s\nValid architecture names are %s", + ArchitectureName.str().c_str(), OS.str().c_str()); + } + return Error::success(); +} + +// Check that a file's architecture [FileCPUType, FileCPUSubtype] +// matches the architecture specified under -arch_only flag. +static bool acceptFileArch(uint32_t FileCPUType, uint32_t FileCPUSubtype) { + uint32_t ArchCPUType, ArchCPUSubtype; + std::tie(ArchCPUType, ArchCPUSubtype) = MachO::getCPUTypeFromArchitecture( + MachO::getArchitectureFromName(ArchType)); + + if (ArchCPUType != FileCPUType) + return false; + + // Currently LLVM doesn't have the constant MachO::CPU_SUBTYPE_ARM64_V8. + // When the constant is added, following modification needs to be done for the + // case of MachO::CPU_TYPE_ARM64 below: + // if (ArchCPUType == MachO::CPU_TYPE_ARM64) { + // if (ArchCPUSubtype == MachO::CPU_SUBTYPE_ARM64_ALL) + // return FileCPUSubtype == MachO::CPU_SUBTYPE_ARM64_ALL || + // FileCPUSubtype == MachO::CPU_SUBTYPE_ARM64_V8; + // else + // return ArchCPUSubtype == FileCPUSubtype; + // } + switch (ArchCPUType) { + case MachO::CPU_TYPE_ARM: + case MachO::CPU_TYPE_ARM64: + case MachO::CPU_TYPE_ARM64_32: + case MachO::CPU_TYPE_X86_64: + return ArchCPUSubtype == FileCPUSubtype; + default: + return true; + } +} + +static Error verifyAndAddMachOObject(std::vector &Members, + NewArchiveMember Member) { auto MBRef = Member.Buf->getMemBufferRef(); Expected> ObjOrErr = object::ObjectFile::createObjectFile(MBRef); @@ -107,6 +160,18 @@ "'%s': format not supported", Member.MemberName.data()); + auto *O = dyn_cast(ObjOrErr->get()); + uint32_t FileCPUType, FileCPUSubtype; + std::tie(FileCPUType, FileCPUSubtype) = MachO::getCPUTypeFromArchitecture( + MachO::getArchitectureFromName(O->getArchTriple().getArchName())); + + // If -arch_only is specified then skip this file if it doesn't match + // the architecture specified. + if (!ArchType.empty() && !acceptFileArch(FileCPUType, FileCPUSubtype)) { + return Error::success(); + } + + Members.push_back(std::move(Member)); return Error::success(); } @@ -117,18 +182,89 @@ if (!NMOrErr) return NMOrErr.takeError(); - // Verify that Member is a Mach-O object file. - if (Error E = verifyMachOObject(*NMOrErr)) + if (Error E = verifyAndAddMachOObject(Members, std::move(*NMOrErr))) return E; - Members.push_back(std::move(*NMOrErr)); + return Error::success(); +} + +static Error processArchive(std::vector &Members, + object::Archive &Lib, StringRef FileName, + const Config &C) { + Error Err = Error::success(); + for (const object::Archive::Child &Child : Lib.children(Err)) + if (Error E = addChildMember(Members, Child, C)) + return createFileError(FileName, std::move(E)); + if (Err) + return createFileError(FileName, std::move(Err)); + return Error::success(); } static Error -addMember(std::vector &Members, StringRef FileName, - std::vector> &ArchiveBuffers, - const Config &C) { +addArchiveMembers(std::vector &Members, + std::vector> &ArchiveBuffers, + NewArchiveMember NM, StringRef FileName, const Config &C) { + Expected> LibOrErr = + object::Archive::create(NM.Buf->getMemBufferRef()); + if (!LibOrErr) + return createFileError(FileName, LibOrErr.takeError()); + + if (Error E = processArchive(Members, **LibOrErr, FileName, C)) + return E; + + // Update vector ArchiveBuffers with the MemoryBuffers to transfer + // ownership. + ArchiveBuffers.push_back(std::move(NM.Buf)); + return Error::success(); +} + +static Error addUniversalMembers( + std::vector &Members, + std::vector> &UniversalBuffers, + NewArchiveMember NM, StringRef FileName, const Config &C) { + Expected> BinaryOrErr = + MachOUniversalBinary::create(NM.Buf->getMemBufferRef()); + if (!BinaryOrErr) + return createFileError(FileName, BinaryOrErr.takeError()); + + auto *UO = BinaryOrErr->get(); + for (const MachOUniversalBinary::ObjectForArch &O : UO->objects()) { + + Expected> MachOObjOrErr = + O.getAsObjectFile(); + if (MachOObjOrErr) { + NewArchiveMember NewMember = + NewArchiveMember(MachOObjOrErr->get()->getMemoryBufferRef()); + NewMember.MemberName = sys::path::filename(NewMember.MemberName); + + if (Error E = verifyAndAddMachOObject(Members, std::move(NewMember))) + return E; + continue; + } + + Expected> ArchiveOrError = O.getAsArchive(); + if (ArchiveOrError) { + consumeError(MachOObjOrErr.takeError()); + if (Error E = processArchive(Members, **ArchiveOrError, FileName, C)) + return E; + continue; + } + + Error CombinedError = + joinErrors(ArchiveOrError.takeError(), MachOObjOrErr.takeError()); + return createFileError(FileName, std::move(CombinedError)); + } + + // Update vector UniversalBuffers with the MemoryBuffers to transfer + // ownership. + UniversalBuffers.push_back(std::move(NM.Buf)); + return Error::success(); +} + +static Error addMember(std::vector &Members, + std::vector> &FileBuffers, + StringRef FileName, const Config &C) { Expected NMOrErr = NewArchiveMember::getFile(FileName, C.Deterministic); if (!NMOrErr) @@ -137,43 +273,36 @@ // For regular archives, use the basename of the object path for the member // name. NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName); + file_magic Magic = identify_magic(NMOrErr->Buf->getBuffer()); // 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, C)) - 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(); - } + if (Magic == file_magic::archive) + return addArchiveMembers(Members, FileBuffers, std::move(*NMOrErr), + FileName, C); - // Verify that Member is a Mach-O object file. - if (Error E = verifyMachOObject(*NMOrErr)) - return E; + // Flatten universal files. + if (Magic == file_magic::macho_universal_binary) + return addUniversalMembers(Members, FileBuffers, std::move(*NMOrErr), + FileName, C); - Members.push_back(std::move(*NMOrErr)); + if (Error E = verifyAndAddMachOObject(Members, std::move(*NMOrErr))) + return E; return Error::success(); } static Error createStaticLibrary(const Config &C) { std::vector NewMembers; - std::vector> ArchiveBuffers; - for (StringRef Member : InputFiles) - if (Error E = addMember(NewMembers, Member, ArchiveBuffers, C)) + std::vector> FileBuffers; + for (StringRef FileName : InputFiles) + if (Error E = addMember(NewMembers, FileBuffers, FileName, C)) return E; + if (NewMembers.empty() && !ArchType.empty()) + return createStringError(std::errc::invalid_argument, + "no library created (no object files in input " + "files matching -arch_only %s)", + ArchType.c_str()); + if (Error E = writeArchive(OutputFile, NewMembers, /*WriteSymtab=*/true, @@ -201,6 +330,10 @@ return createStringError(std::errc::invalid_argument, "no input files specified"); + if (ArchType.getNumOccurrences()) + if (Error E = validateArchitectureName(ArchType)) + return std::move(E); + return C; }