diff --git a/llvm/test/tools/llvm-libtool-darwin/universal-output.test b/llvm/test/tools/llvm-libtool-darwin/universal-output.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-libtool-darwin/universal-output.test @@ -0,0 +1,142 @@ +## This test checks that a correct universal binary is produced when +## llvm-libtool-darwin is given inputs for multiple architectures. + +# 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 + +## Check that architectures are present in the universal output: +# RUN: llvm-lipo -info %t.lib | \ +# RUN: FileCheck %s --check-prefix=ARCHS -DFILE=%t.lib + +## Check that the files with the same architecture are combined in an archive: +# RUN: llvm-libtool-darwin -static -o %t.lib %t.armv6 %t.armv6 %t.armv7 +# RUN: llvm-lipo -info %t.lib | \ +# RUN: FileCheck %s --check-prefix=ARCHS -DFILE=%t.lib +# RUN: llvm-objdump --all-headers %t.lib | \ +# RUN: FileCheck %s --check-prefix=UNIVERSAL-MEMBERS -DFILE=%t.lib -DPREFIX=%basename_t.tmp + +# ARCHS: Architectures in the fat file: [[FILE]] are: armv6 armv7 + +# UNIVERSAL-MEMBERS: Archive : [[FILE]] (architecture armv6) +# UNIVERSAL-MEMBERS-NEXT: __.SYMDEF +# UNIVERSAL-MEMBERS-NEXT: [[PREFIX]].armv6 +# UNIVERSAL-MEMBERS-NEXT: [[PREFIX]].armv6 +# UNIVERSAL-MEMBERS: Archive : [[FILE]] (architecture armv7) +# UNIVERSAL-MEMBERS-NEXT: __.SYMDEF +# UNIVERSAL-MEMBERS-NEXT: [[PREFIX]].armv7 + +## 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 + - '' +... 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,12 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/DenseMap.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" +#include "llvm/Object/MachOUniversalWriter.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" @@ -24,6 +26,9 @@ using namespace llvm; using namespace llvm::object; +typedef DenseMap> + MembersPerArchitectureMap; + cl::OptionCategory LibtoolCategory("llvm-libtool-darwin Options"); static cl::opt OutputFile("o", cl::desc("Specify output filename"), @@ -113,6 +118,20 @@ return Error::success(); } +static uint64_t getCPUID(uint32_t CPUType, uint32_t CPUSubtype) { + switch (CPUType) { + case MachO::CPU_TYPE_ARM: + case MachO::CPU_TYPE_ARM64: + case MachO::CPU_TYPE_ARM64_32: + case MachO::CPU_TYPE_X86_64: + // We consider CPUSubtype only for the above 4 CPUTypes to match cctools' + // libtool behavior. + return static_cast(CPUType) << 32 | CPUSubtype; + default: + return CPUType; + } +} + // 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) { @@ -144,7 +163,7 @@ } } -static Error verifyAndAddMachOObject(std::vector &Members, +static Error verifyAndAddMachOObject(MembersPerArchitectureMap &Members, NewArchiveMember Member) { auto MBRef = Member.Buf->getMemBufferRef(); Expected> ObjOrErr = @@ -171,11 +190,12 @@ return Error::success(); } - Members.push_back(std::move(Member)); + uint64_t FileCPUID = getCPUID(FileCPUType, FileCPUSubtype); + Members[FileCPUID].push_back(std::move(Member)); return Error::success(); } -static Error addChildMember(std::vector &Members, +static Error addChildMember(MembersPerArchitectureMap &Members, const object::Archive::Child &M, const Config &C) { Expected NMOrErr = NewArchiveMember::getOldMember(M, C.Deterministic); @@ -188,7 +208,7 @@ return Error::success(); } -static Error processArchive(std::vector &Members, +static Error processArchive(MembersPerArchitectureMap &Members, object::Archive &Lib, StringRef FileName, const Config &C) { Error Err = Error::success(); @@ -202,7 +222,7 @@ } static Error -addArchiveMembers(std::vector &Members, +addArchiveMembers(MembersPerArchitectureMap &Members, std::vector> &ArchiveBuffers, NewArchiveMember NM, StringRef FileName, const Config &C) { Expected> LibOrErr = @@ -220,7 +240,7 @@ } static Error addUniversalMembers( - std::vector &Members, + MembersPerArchitectureMap &Members, std::vector> &UniversalBuffers, NewArchiveMember NM, StringRef FileName, const Config &C) { Expected> BinaryOrErr = @@ -267,7 +287,7 @@ return Error::success(); } -static Error addMember(std::vector &Members, +static Error addMember(MembersPerArchitectureMap &Members, std::vector> &FileBuffers, StringRef FileName, const Config &C) { Expected NMOrErr = @@ -295,25 +315,77 @@ return Error::success(); } +static Expected> +buildSlices(ArrayRef> OutputBinaries) { + SmallVector Slices; + + for (auto &OB : OutputBinaries) { + const Archive *A = OB.getBinary(); + Expected ArchiveSlice = Slice::create(A); + if (!ArchiveSlice) + return ArchiveSlice.takeError(); + Slices.push_back(*ArchiveSlice); + } + return Slices; +} + static Error createStaticLibrary(const Config &C) { - std::vector NewMembers; + MembersPerArchitectureMap NewMembers; 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, - /*Kind=*/object::Archive::K_DARWIN, C.Deterministic, - /*Thin=*/false)) - return E; + if (!ArchType.empty()) { + uint32_t ArchCPUType, ArchCPUSubtype; + std::tie(ArchCPUType, ArchCPUSubtype) = MachO::getCPUTypeFromArchitecture( + MachO::getArchitectureFromName(ArchType)); + + uint64_t ArchCPUID = getCPUID(ArchCPUType, ArchCPUSubtype); + if (NewMembers.find(ArchCPUID) == NewMembers.end()) + return createStringError(std::errc::invalid_argument, + "no library created (no object files in input " + "files matching -arch_only %s)", + ArchType.c_str()); + } + + if (NewMembers.size() == 1) { + if (Error E = + writeArchive(OutputFile, NewMembers.begin()->second, + /*WriteSymtab=*/true, + /*Kind=*/object::Archive::K_DARWIN, C.Deterministic, + /*Thin=*/false)) + return E; + } else { + SmallVector, 2> OutputBinaries; + for (const auto &M : NewMembers) { + Expected> OutputBufferOrErr = + writeArchiveToBuffer(M.getSecond(), + /*WriteSymtab=*/true, + /*Kind=*/object::Archive::K_DARWIN, + C.Deterministic, + /*Thin=*/false); + if (!OutputBufferOrErr) + return OutputBufferOrErr.takeError(); + std::unique_ptr &OutputBuffer = OutputBufferOrErr.get(); + + Expected> ArchiveOrError = + Archive::create(OutputBuffer->getMemBufferRef()); + if (!ArchiveOrError) + return ArchiveOrError.takeError(); + std::unique_ptr &A = ArchiveOrError.get(); + + OutputBinaries.push_back( + OwningBinary(std::move(A), std::move(OutputBuffer))); + } + Expected> Slices = buildSlices(OutputBinaries); + if (!Slices) + return Slices.takeError(); + + llvm::stable_sort(*Slices); + if (Error E = writeUniversalBinary(*Slices, OutputFile)) + return E; + } return Error::success(); }