Index: llvm/trunk/test/tools/llvm-lipo/segalign-invalid-input.test =================================================================== --- llvm/trunk/test/tools/llvm-lipo/segalign-invalid-input.test +++ llvm/trunk/test/tools/llvm-lipo/segalign-invalid-input.test @@ -0,0 +1,29 @@ +# RUN: yaml2obj %p/Inputs/arm64-slice.yaml > %t-arm64.o +# RUN: yaml2obj %p/Inputs/armv7-slice.yaml > %t-armv7.o + +# RUN: not llvm-lipo %t-armv7.o %t-arm64.o -create -o %t.o -segalign a 2>&1 | FileCheck --check-prefix=MISSING_ARG %s +# MISSING_ARG: error: segalign is missing an argument: expects -segalign arch_type alignment_value + +# RUN: not llvm-lipo %t-armv7.o %t-arm64.o -o %t.o -segalign arm64 10 2>&1 | FileCheck --check-prefix=MISSING_ACTION %s +# MISSING_ACTION: error: at least one action should be specified + +# RUN: not llvm-lipo %t-armv7.o %t-arm64.o -create -o %t.o -segalign arm6 10 2>&1 | FileCheck --check-prefix=INVALID_ARCH %s +# INVALID_ARCH: error: Invalid architecture: arm6 + +# RUN: not llvm-lipo %t-armv7.o %t-arm64.o -create -o %t.o -segalign arm64 15.7 2>&1 | FileCheck --check-prefix=INVALID_ALIGN_NOT_HEX %s +# INVALID_ALIGN_NOT_HEX: error: argument to -segalign 15.7 (hex) is not a proper hexadecimal number + +# RUN: not llvm-lipo %t-armv7.o %t-arm64.o -create -o %t.o -segalign arm64 15 2>&1 | FileCheck --check-prefix=INVALID_ALIGN_HEX %s +# INVALID_ALIGN_HEX: error: argument to -segalign 15 (hex) must be a non-zero power of two + +# RUN: not llvm-lipo %t-armv7.o %t-arm64.o -create -o %t.o -segalign arm64 0 2>&1 | FileCheck --check-prefix=INVALID_ALIGN_ZERO %s +# INVALID_ALIGN_ZERO: error: argument to -segalign 0 (hex) must be a non-zero power of two + +# RUN: not llvm-lipo %t-armv7.o %t-arm64.o -create -o %t.o -segalign arm64 10000 2>&1 | FileCheck --check-prefix=INVALID_ALIGN_BIG %s +# INVALID_ALIGN_BIG: error: argument to -segalign 10000 (hex) must be less than or equal to the maximum section align 2^15 + +# RUN: not llvm-lipo %t-armv7.o %t-arm64.o -create -o %t.o -segalign arm64 100 -segalign arm64 200 2>&1 | FileCheck --check-prefix=DUPLICATE_ALIGN %s +# DUPLICATE_ALIGN: error: -segalign arm64 specified multiple times: 256, 512 + +# RUN: not llvm-lipo %t-armv7.o %t-arm64.o -create -o %t.o -segalign i386 100 2>&1 | FileCheck --check-prefix=EXCLUDED_ALIGN %s +# EXCLUDED_ALIGN: error: -segalign i386 specified but resulting fat file does not contain that architecture Index: llvm/trunk/test/tools/llvm-lipo/segalign.test =================================================================== --- llvm/trunk/test/tools/llvm-lipo/segalign.test +++ llvm/trunk/test/tools/llvm-lipo/segalign.test @@ -0,0 +1,113 @@ +# RUN: yaml2obj %p/Inputs/arm64-slice.yaml > %t-arm64.o +# RUN: yaml2obj %p/Inputs/armv7-slice.yaml > %t-armv7.o +# RUN: yaml2obj %p/Inputs/i386-x86_64-universal.yaml > %t-universal.o + +# segalign expects an architecture type followed by a hexadecimal value for alignment + +# RUN: llvm-lipo %t-universal.o %t-arm64.o -create -o %t.o -segalign arm64 100 +# RUN: llvm-objdump %t.o -m --universal-headers | FileCheck --check-prefix=CHECK_SMALL_ARM64 %s +# CHECK_SMALL_ARM64: nfat_arch 3 +# CHECK_SMALL_ARM64-NEXT: architecture i386 +# CHECK_SMALL_ARM64: offset 4096 +# CHECK_SMALL_ARM64-NEXT: size 432 +# CHECK_SMALL_ARM64-NEXT: align 2^12 (4096) +# CHECK_SMALL_ARM64-NEXT: architecture x86_64 +# CHECK_SMALL_ARM64: offset 8192 +# CHECK_SMALL_ARM64-NEXT: size 488 +# CHECK_SMALL_ARM64-NEXT: align 2^12 (4096) +# CHECK_SMALL_ARM64-NEXT: architecture arm64 +# CHECK_SMALL_ARM64: offset 8704 +# CHECK_SMALL_ARM64-NEXT: size 516 +# CHECK_SMALL_ARM64-NEXT: align 2^8 (256) +###### WITHOUT SEGALIGN ###### +# RUN: llvm-lipo %t-universal.o %t-arm64.o -create -o %t-nosegalign.o +# RUN: llvm-objdump %t-nosegalign.o -m --universal-headers | FileCheck --check-prefix=CHECK_SMALL_ARM64_NO_SEGALIGN %s +# CHECK_SMALL_ARM64_NO_SEGALIGN: architecture arm64 +# CHECK_SMALL_ARM64_NO_SEGALIGN: offset 16384 +# CHECK_SMALL_ARM64_NO_SEGALIGN-NEXT: size 516 +# CHECK_SMALL_ARM64_NO_SEGALIGN-NEXT: align 2^14 (16384) + +# RUN: llvm-lipo %t.o -thin arm64 -o %thin-arm64.o +# RUN: cmp %thin-arm64.o %t-arm64.o + +# RUN: llvm-lipo %t-universal.o %t-armv7.o -create -o %t2.o -segalign armv7 100 +# RUN: llvm-objdump %t2.o -m --universal-headers | FileCheck --check-prefix=CHECK_SMALL_ARMV7 %s +# CHECK_SMALL_ARMV7: nfat_arch 3 +# CHECK_SMALL_ARMV7-NEXT: architecture armv7 +# CHECK_SMALL_ARMV7: offset 256 +# CHECK_SMALL_ARMV7-NEXT: size 304 +# CHECK_SMALL_ARMV7-NEXT: align 2^8 (256) +# CHECK_SMALL_ARMV7-NEXT: architecture i386 +# CHECK_SMALL_ARMV7: offset 4096 +# CHECK_SMALL_ARMV7-NEXT: size 432 +# CHECK_SMALL_ARMV7-NEXT: align 2^12 (4096) +# CHECK_SMALL_ARMV7-NEXT: architecture x86_64 +# CHECK_SMALL_ARMV7: offset 8192 +# CHECK_SMALL_ARMV7-NEXT: size 488 +# CHECK_SMALL_ARMV7-NEXT: align 2^12 (4096) +###### WITHOUT SEGALIGN ###### +# RUN: llvm-lipo %t-universal.o %t-armv7.o -create -o %t2-nosegalign.o +# RUN: llvm-objdump %t2-nosegalign.o -m --universal-headers | FileCheck --check-prefix=CHECK_SMALL_ARMV7_NO_SEGALIGN %s +# CHECK_SMALL_ARMV7_NO_SEGALIGN: nfat_arch 3 +# CHECK_SMALL_ARMV7_NO_SEGALIGN-NEXT: architecture i386 +# CHECK_SMALL_ARMV7_NO_SEGALIGN: offset 4096 +# CHECK_SMALL_ARMV7_NO_SEGALIGN-NEXT: size 432 +# CHECK_SMALL_ARMV7_NO_SEGALIGN-NEXT: align 2^12 (4096) +# CHECK_SMALL_ARMV7_NO_SEGALIGN-NEXT: architecture x86_64 +# CHECK_SMALL_ARMV7_NO_SEGALIGN: offset 8192 +# CHECK_SMALL_ARMV7_NO_SEGALIGN-NEXT: size 488 +# CHECK_SMALL_ARMV7_NO_SEGALIGN-NEXT: align 2^12 (4096) +# CHECK_SMALL_ARMV7_NO_SEGALIGN-NEXT: architecture armv7 +# CHECK_SMALL_ARMV7_NO_SEGALIGN: offset 16384 +# CHECK_SMALL_ARMV7_NO_SEGALIGN-NEXT: size 304 +# CHECK_SMALL_ARMV7_NO_SEGALIGN-NEXT: align 2^14 (16384) + +# RUN: llvm-lipo %t2.o -thin armv7 -o %thin-armv7.o +# RUN: cmp %thin-armv7.o %t-armv7.o + +# RUN: yaml2obj %p/Inputs/armv7-slice-big.yaml > %t-armv7-big.o +# RUN: yaml2obj %p/Inputs/i386-x86_64-armv7-arm64-universal.yaml > %t-universal-big.o + +# RUN: llvm-lipo %t-universal-big.o -replace armv7 %t-armv7-big.o -o %t3.o -segalign armv7 4 +# RUN: llvm-objdump %t3.o -m --universal-headers | FileCheck --check-prefix=CHECK_REPLACE_ARMV7 %s +# CHECK_REPLACE_ARMV7: nfat_arch 4 +# CHECK_REPLACE_ARMV7-NEXT: architecture armv7 +# CHECK_REPLACE_ARMV7: offset 88 +# CHECK_REPLACE_ARMV7-NEXT: size 88128 +# CHECK_REPLACE_ARMV7-NEXT: align 2^2 (4) +# CHECK_REPLACE_ARMV7-NEXT: architecture i386 +# CHECK_REPLACE_ARMV7: offset 90112 +# CHECK_REPLACE_ARMV7-NEXT: size 432 +# CHECK_REPLACE_ARMV7-NEXT: align 2^12 (4096) +# CHECK_REPLACE_ARMV7-NEXT: architecture x86_64 +# CHECK_REPLACE_ARMV7: offset 94208 +# CHECK_REPLACE_ARMV7-NEXT: size 488 +# CHECK_REPLACE_ARMV7-NEXT: align 2^12 (4096) +# CHECK_REPLACE_ARMV7-NEXT: architecture arm64 +# CHECK_REPLACE_ARMV7: offset 98304 +# CHECK_REPLACE_ARMV7-NEXT: size 516 +# CHECK_REPLACE_ARMV7-NEXT: align 2^14 (16384) +###### WITHOUT SEGALIGN ###### +# RUN: llvm-lipo %t-universal-big.o -replace armv7 %t-armv7-big.o -o %t3-nosegalign.o +# RUN: llvm-objdump %t3-nosegalign.o -m --universal-headers | FileCheck --check-prefix=CHECK_REPLACE_ARMV7_NO_SEGALGIN %s +# CHECK_REPLACE_ARMV7_NO_SEGALGIN: nfat_arch 4 +# CHECK_REPLACE_ARMV7_NO_SEGALGIN-NEXT: architecture i386 +# CHECK_REPLACE_ARMV7_NO_SEGALGIN: offset 4096 +# CHECK_REPLACE_ARMV7_NO_SEGALGIN-NEXT: size 432 +# CHECK_REPLACE_ARMV7_NO_SEGALGIN-NEXT: align 2^12 (4096) +# CHECK_REPLACE_ARMV7_NO_SEGALGIN-NEXT: architecture x86_64 +# CHECK_REPLACE_ARMV7_NO_SEGALGIN: offset 8192 +# CHECK_REPLACE_ARMV7_NO_SEGALGIN-NEXT: size 488 +# CHECK_REPLACE_ARMV7_NO_SEGALGIN-NEXT: align 2^12 (4096) +# CHECK_REPLACE_ARMV7_NO_SEGALGIN-NEXT: architecture armv7 +# CHECK_REPLACE_ARMV7_NO_SEGALGIN: offset 16384 +# CHECK_REPLACE_ARMV7_NO_SEGALGIN-NEXT: size 88128 +# CHECK_REPLACE_ARMV7_NO_SEGALGIN-NEXT: align 2^14 (16384) +# CHECK_REPLACE_ARMV7_NO_SEGALGIN-NEXT: architecture arm64 +# CHECK_REPLACE_ARMV7_NO_SEGALGIN: offset 114688 +# CHECK_REPLACE_ARMV7_NO_SEGALGIN-NEXT: size 516 +# CHECK_REPLACE_ARMV7_NO_SEGALGIN-NEXT: align 2^14 (16384) + + +# RUN: llvm-lipo %t3.o -thin armv7 -o %thin-armv7-big.o +# RUN: cmp %thin-armv7-big.o %t-armv7-big.o Index: llvm/trunk/tools/llvm-lipo/LipoOpts.td =================================================================== --- llvm/trunk/tools/llvm-lipo/LipoOpts.td +++ llvm/trunk/tools/llvm-lipo/LipoOpts.td @@ -6,6 +6,12 @@ def version : Flag<["-", "--"], "version">, HelpText<"Print the version and exit.">; +def segalign + : MultiArg<["-", "--"], "segalign", 2>, + HelpText<"Specifies the segment alignment for the specified " + "architecture when creating a universal binary file. The " + "alignment is a hexadecimal number that is a power of 2.">; + def action_group : OptionGroup<"action group">; def verify_arch Index: llvm/trunk/tools/llvm-lipo/llvm-lipo.cpp =================================================================== --- llvm/trunk/tools/llvm-lipo/llvm-lipo.cpp +++ llvm/trunk/tools/llvm-lipo/llvm-lipo.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" #include "llvm/Object/Binary.h" #include "llvm/Object/MachO.h" @@ -94,6 +95,7 @@ SmallVector InputFiles; SmallVector VerifyArchList; SmallVector Replacements; + StringMap SegmentAlignments; std::string ThinArchType; std::string OutputFile; LipoAction ActionToPerform; @@ -162,6 +164,36 @@ if (InputArgs.hasArg(LIPO_output)) C.OutputFile = InputArgs.getLastArgValue(LIPO_output); + for (auto Segalign : InputArgs.filtered(LIPO_segalign)) { + if (!Segalign->getValue(1)) + reportError("segalign is missing an argument: expects -segalign " + "arch_type alignment_value"); + + validateArchitectureName(Segalign->getValue(0)); + + uint32_t AlignmentValue; + if (!to_integer(Segalign->getValue(1), AlignmentValue, 16)) + reportError("argument to -segalign " + + Twine(Segalign->getValue(1)) + + " (hex) is not a proper hexadecimal number"); + if (!isPowerOf2_32(AlignmentValue)) + reportError("argument to -segalign " + + Twine(Segalign->getValue(1)) + + " (hex) must be a non-zero power of two"); + if (Log2_32(AlignmentValue) > MachOUniversalBinary::MaxSectionAlignment) + reportError( + "argument to -segalign " + Twine(Segalign->getValue(1)) + + " (hex) must be less than or equal to the maximum section align 2^" + + Twine(MachOUniversalBinary::MaxSectionAlignment)); + auto Entry = C.SegmentAlignments.try_emplace(Segalign->getValue(0), + Log2_32(AlignmentValue)); + if (!Entry.second) + reportError("-segalign " + Twine(Segalign->getValue(0)) + + " specified multiple times: " + + Twine(1 << Entry.first->second) + ", " + + Twine(AlignmentValue)); + } + SmallVector ActionArgs(InputArgs.filtered(LIPO_action_group)); if (ActionArgs.empty()) reportError("at least one action should be specified"); @@ -474,10 +506,36 @@ return Lhs.Alignment < Rhs.Alignment; } +template +static void +updateSegmentAlignments(Range &Slices, + const StringMap &Alignments) { + for (auto &Slice : Slices) { + auto Alignment = Alignments.find(getArchString(*Slice.ObjectFile)); + if (Alignment != Alignments.end()) + Slice.Alignment = Alignment->second; + } +} + +static void checkUnusedAlignments(ArrayRef Slices, + const StringMap &Alignments) { + auto HasArch = [&](StringRef Arch) { + return llvm::find_if(Slices, [Arch](Slice S) { + return getArchString(*S.ObjectFile) == Arch; + }) != Slices.end(); + }; + for (StringRef Arch : Alignments.keys()) + if (!HasArch(Arch)) + reportError("-segalign " + Arch + + " specified but resulting fat file does not contain " + "that architecture "); +} + // Updates vector ExtractedObjects with the MachOObjectFiles extracted from // Universal Binary files to transfer ownership. static SmallVector buildSlices( ArrayRef> InputBinaries, + const StringMap &Alignments, SmallVectorImpl> &ExtractedObjects) { SmallVector Slices; for (auto &IB : InputBinaries) { @@ -497,6 +555,7 @@ llvm_unreachable("Unexpected binary format"); } } + updateSegmentAlignments(Slices, Alignments); return Slices; } @@ -576,13 +635,16 @@ LLVM_ATTRIBUTE_NORETURN static void createUniversalBinary(ArrayRef> InputBinaries, + const StringMap &Alignments, StringRef OutputFileName) { assert(InputBinaries.size() >= 1 && "Incorrect number of input binaries"); assert(!OutputFileName.empty() && "Create expects a single output file"); SmallVector, 1> ExtractedObjects; - SmallVector Slices = buildSlices(InputBinaries, ExtractedObjects); + SmallVector Slices = + buildSlices(InputBinaries, Alignments, ExtractedObjects); checkArchDuplicates(Slices); + checkUnusedAlignments(Slices, Alignments); createUniversalBinary(Slices, OutputFileName); exit(EXIT_SUCCESS); @@ -590,6 +652,7 @@ static StringMap buildReplacementSlices(ArrayRef> ReplacementBinaries, + const StringMap &Alignments, ArrayRef Replacements) { assert(ReplacementBinaries.size() == Replacements.size() && "Number of replacment binaries does not match the number of " @@ -620,11 +683,15 @@ Entry.first->second.ObjectFile->getFileName() + ", " + O->getFileName()); } + auto SlicesMapRange = map_range( + Slices, [](StringMapEntry &E) -> Slice & { return E.getValue(); }); + updateSegmentAlignments(SlicesMapRange, Alignments); return Slices; } LLVM_ATTRIBUTE_NORETURN static void replaceSlices(ArrayRef> InputBinaries, + const StringMap &Alignments, StringRef OutputFileName, ArrayRef Replacements) { assert(InputBinaries.size() == 1 && "Incorrect number of input binaries"); @@ -642,9 +709,10 @@ readInputBinaries(ReplacementFiles); StringMap ReplacementSlices = - buildReplacementSlices(ReplacementBinaries, Replacements); + buildReplacementSlices(ReplacementBinaries, Alignments, Replacements); SmallVector, 2> ExtractedObjects; - SmallVector Slices = buildSlices(InputBinaries, ExtractedObjects); + SmallVector Slices = + buildSlices(InputBinaries, Alignments, ExtractedObjects); for (auto &Slice : Slices) { auto It = ReplacementSlices.find(getArchString(*Slice.ObjectFile)); @@ -659,6 +727,8 @@ " specified but fat file: " + InputBinaries.front().getBinary()->getFileName() + " does not contain that architecture"); + + checkUnusedAlignments(Slices, Alignments); createUniversalBinary(Slices, OutputFileName); exit(EXIT_SUCCESS); } @@ -683,10 +753,11 @@ extractSlice(InputBinaries, C.ThinArchType, C.OutputFile); break; case LipoAction::CreateUniversal: - createUniversalBinary(InputBinaries, C.OutputFile); + createUniversalBinary(InputBinaries, C.SegmentAlignments, C.OutputFile); break; case LipoAction::ReplaceArch: - replaceSlices(InputBinaries, C.OutputFile, C.Replacements); + replaceSlices(InputBinaries, C.SegmentAlignments, C.OutputFile, + C.Replacements); break; } return EXIT_SUCCESS;