diff --git a/include/llvm/MC/MCAssembler.h b/include/llvm/MC/MCAssembler.h index c4b475ee03c..4b519dd7c97 100644 --- a/include/llvm/MC/MCAssembler.h +++ b/include/llvm/MC/MCAssembler.h @@ -15,6 +15,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/ilist.h" #include "llvm/ADT/ilist_node.h" +#include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCFixup.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCSubtargetInfo.h" @@ -840,6 +841,15 @@ public: const_data_region_iterator; typedef std::vector::iterator data_region_iterator; + /// MachO specific deployment target version info. + // A Major version of 0 indicates that no version information was supplied + // and so the corresponding load command should not be emitted. + typedef struct { + MCVersionMinType Kind; + unsigned Major; + unsigned Minor; + unsigned Update; + } VersionMinInfoType; private: MCAssembler(const MCAssembler&) LLVM_DELETED_FUNCTION; void operator=(const MCAssembler&) LLVM_DELETED_FUNCTION; @@ -902,6 +912,8 @@ private: // Access to the flags is necessary in cases where assembler directives affect // which flags to be set. unsigned ELFHeaderEFlags; + + VersionMinInfoType VersionMinInfo; private: /// Evaluate a fixup to a relocatable expression and the value which should be /// placed into the fixup. @@ -983,6 +995,16 @@ public: unsigned getELFHeaderEFlags() const {return ELFHeaderEFlags;} void setELFHeaderEFlags(unsigned Flags) { ELFHeaderEFlags = Flags;} + /// MachO deployment target version information. + const VersionMinInfoType &getVersionMinInfo() const { return VersionMinInfo; } + void setVersionMinInfo(MCVersionMinType Kind, unsigned Major, unsigned Minor, + unsigned Update) { + VersionMinInfo.Kind = Kind; + VersionMinInfo.Major = Major; + VersionMinInfo.Minor = Minor; + VersionMinInfo.Update = Update; + } + public: /// Construct a new assembler instance. /// diff --git a/include/llvm/MC/MCDirectives.h b/include/llvm/MC/MCDirectives.h index 0461766c2fd..f9d66e0b15d 100644 --- a/include/llvm/MC/MCDirectives.h +++ b/include/llvm/MC/MCDirectives.h @@ -60,6 +60,11 @@ enum MCDataRegionType { MCDR_DataRegionEnd ///< .end_data_region }; +enum MCVersionMinType { + MCVM_IOSVersionMin, ///< .ios_version_min + MCVM_OSXVersionMin ///< .macosx_version_min +}; + } // end namespace llvm #endif diff --git a/include/llvm/MC/MCStreamer.h b/include/llvm/MC/MCStreamer.h index fc7a3b86482..37d18fbe43a 100644 --- a/include/llvm/MC/MCStreamer.h +++ b/include/llvm/MC/MCStreamer.h @@ -386,6 +386,10 @@ public: /// EmitDataRegion - Note in the output the specified region @p Kind. virtual void EmitDataRegion(MCDataRegionType Kind) {} + /// EmitVersionMin - Specify the MachO minimum deployment target version. + virtual void EmitVersionMin(MCVersionMinType, unsigned Major, unsigned Minor, + unsigned Update) {} + /// EmitThumbFunc - Note in the output that the specified @p Func is /// a Thumb mode function (ARM target only). virtual void EmitThumbFunc(MCSymbol *Func) = 0; diff --git a/include/llvm/Object/MachO.h b/include/llvm/Object/MachO.h index 50296aed9b8..34edec3ebfd 100644 --- a/include/llvm/Object/MachO.h +++ b/include/llvm/Object/MachO.h @@ -178,6 +178,8 @@ public: getSegment64LoadCommand(const LoadCommandInfo &L) const; MachO::linker_options_command getLinkerOptionsLoadCommand(const LoadCommandInfo &L) const; + MachO::version_min_command + getVersionMinLoadCommand(const LoadCommandInfo &L) const; MachO::any_relocation_info getRelocation(DataRefImpl Rel) const; MachO::data_in_code_entry getDice(DataRefImpl Rel) const; diff --git a/lib/MC/MCAsmStreamer.cpp b/lib/MC/MCAsmStreamer.cpp index 85263fec626..e28c56e7d31 100644 --- a/lib/MC/MCAsmStreamer.cpp +++ b/lib/MC/MCAsmStreamer.cpp @@ -133,6 +133,8 @@ public: void EmitAssemblerFlag(MCAssemblerFlag Flag) override; void EmitLinkerOptions(ArrayRef Options) override; void EmitDataRegion(MCDataRegionType Kind) override; + void EmitVersionMin(MCVersionMinType Kind, unsigned Major, unsigned Minor, + unsigned Update) override; void EmitThumbFunc(MCSymbol *Func) override; void EmitAssignment(MCSymbol *Symbol, const MCExpr *Value) override; @@ -380,6 +382,18 @@ void MCAsmStreamer::EmitDataRegion(MCDataRegionType Kind) { EmitEOL(); } +void MCAsmStreamer::EmitVersionMin(MCVersionMinType Kind, unsigned Major, + unsigned Minor, unsigned Update) { + switch (Kind) { + case MCVM_IOSVersionMin: OS << "\t.ios_version_min"; break; + case MCVM_OSXVersionMin: OS << "\t.macosx_version_min"; break; + } + OS << " " << Major << ", " << Minor; + if (Update) + OS << ", " << Update; + EmitEOL(); +} + void MCAsmStreamer::EmitThumbFunc(MCSymbol *Func) { // This needs to emit to a temporary string to get properly quoted // MCSymbols when they have spaces in them. diff --git a/lib/MC/MCAssembler.cpp b/lib/MC/MCAssembler.cpp index 89d7d190167..910295c5d2b 100644 --- a/lib/MC/MCAssembler.cpp +++ b/lib/MC/MCAssembler.cpp @@ -296,6 +296,7 @@ MCAssembler::MCAssembler(MCContext &Context_, MCAsmBackend &Backend_, : Context(Context_), Backend(Backend_), Emitter(Emitter_), Writer(Writer_), OS(OS_), BundleAlignSize(0), RelaxAll(false), NoExecStack(false), SubsectionsViaSymbols(false), ELFHeaderEFlags(0) { + VersionMinInfo.Major = 0; // Major version == 0 for "none specified" } MCAssembler::~MCAssembler() { diff --git a/lib/MC/MCMachOStreamer.cpp b/lib/MC/MCMachOStreamer.cpp index fef7b6bd70b..c932c3c375a 100644 --- a/lib/MC/MCMachOStreamer.cpp +++ b/lib/MC/MCMachOStreamer.cpp @@ -49,6 +49,8 @@ public: void EmitAssemblerFlag(MCAssemblerFlag Flag) override; void EmitLinkerOptions(ArrayRef Options) override; void EmitDataRegion(MCDataRegionType Kind) override; + void EmitVersionMin(MCVersionMinType Kind, unsigned Major, + unsigned Minor, unsigned Update) override; void EmitThumbFunc(MCSymbol *Func) override; bool EmitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override; void EmitSymbolDesc(MCSymbol *Symbol, unsigned DescValue) override; @@ -193,6 +195,11 @@ void MCMachOStreamer::EmitDataRegion(MCDataRegionType Kind) { } } +void MCMachOStreamer::EmitVersionMin(MCVersionMinType Kind, unsigned Major, + unsigned Minor, unsigned Update) { + getAssembler().setVersionMinInfo(Kind, Major, Minor, Update); +} + void MCMachOStreamer::EmitThumbFunc(MCSymbol *Symbol) { // Remember that the function is a thumb function. Fixup and relocation // values will need adjusted. diff --git a/lib/MC/MCParser/DarwinAsmParser.cpp b/lib/MC/MCParser/DarwinAsmParser.cpp index 81c2cf021c1..dd7eccb2378 100644 --- a/lib/MC/MCParser/DarwinAsmParser.cpp +++ b/lib/MC/MCParser/DarwinAsmParser.cpp @@ -163,6 +163,9 @@ public: addDirectiveHandler<&DarwinAsmParser::ParseSectionDirectiveTLV>(".tlv"); addDirectiveHandler<&DarwinAsmParser::ParseSectionDirectiveIdent>(".ident"); + addDirectiveHandler<&DarwinAsmParser::ParseVersionMin>(".ios_version_min"); + addDirectiveHandler<&DarwinAsmParser::ParseVersionMin>( + ".macosx_version_min"); } bool ParseDirectiveDesc(StringRef, SMLoc); @@ -360,6 +363,7 @@ public: return ParseSectionSwitch("__DATA", "__thread_init", MachO::S_THREAD_LOCAL_INIT_FUNCTION_POINTERS); } + bool ParseVersionMin(StringRef, SMLoc); }; @@ -859,6 +863,50 @@ bool DarwinAsmParser::ParseDirectiveDataRegionEnd(StringRef, SMLoc) { return false; } +/// ParseVersionMin +/// ::= .ios_version_min major,minor[,update] +/// ::= .macosx_version_min major,minor[,update] +bool DarwinAsmParser::ParseVersionMin(StringRef Directive, SMLoc) { + int64_t Major = 0, Minor = 0, Update = 0; + int Kind = StringSwitch(Directive) + .Case(".ios_version_min", MCVM_IOSVersionMin) + .Case(".macosx_version_min", MCVM_OSXVersionMin); + // Get the major version number. + if (getLexer().isNot(AsmToken::Integer)) + return TokError("invalid OS major version number"); + Major = getLexer().getTok().getIntVal(); + if (Major > 65535 || Major <= 0) + return TokError("invalid OS major version number"); + Lex(); + if (getLexer().isNot(AsmToken::Comma)) + return TokError("minor OS version number required, comma expected"); + Lex(); + // Get the minor version number. + if (getLexer().isNot(AsmToken::Integer)) + return TokError("invalid OS minor version number"); + Minor = getLexer().getTok().getIntVal(); + if (Minor > 255 || Minor < 0) + return TokError("invalid OS minor version number"); + Lex(); + // Get the update level, if specified + if (getLexer().isNot(AsmToken::EndOfStatement)) { + if (getLexer().isNot(AsmToken::Comma)) + return TokError("invalid update specifier, comma expected"); + Lex(); + if (getLexer().isNot(AsmToken::Integer)) + return TokError("invalid OS update number"); + Update = getLexer().getTok().getIntVal(); + if (Update > 255 || Update < 0) + return TokError("invalid OS update number"); + Lex(); + } + + // We've parsed a correct version specifier, so send it to the streamer. + getStreamer().EmitVersionMin((MCVersionMinType)Kind, Major, Minor, Update); + + return false; +} + namespace llvm { MCAsmParserExtension *createDarwinAsmParser() { diff --git a/lib/MC/MachObjectWriter.cpp b/lib/MC/MachObjectWriter.cpp index b3b593d3154..ff55c978d6a 100644 --- a/lib/MC/MachObjectWriter.cpp +++ b/lib/MC/MachObjectWriter.cpp @@ -737,6 +737,8 @@ IsSymbolRefDifferenceFullyResolvedImpl(const MCAssembler &Asm, void MachObjectWriter::WriteObject(MCAssembler &Asm, const MCAsmLayout &Layout) { unsigned NumSections = Asm.size(); + const MCAssembler::VersionMinInfoType &VersionInfo = + Layout.getAssembler().getVersionMinInfo(); // The section data starts after the header, the segment load command (and // section headers) and the symbol table. @@ -745,6 +747,12 @@ void MachObjectWriter::WriteObject(MCAssembler &Asm, sizeof(MachO::segment_command_64) + NumSections * sizeof(MachO::section_64): sizeof(MachO::segment_command) + NumSections * sizeof(MachO::section); + // Add the deployment target version info load command size, if used. + if (VersionInfo.Major != 0) { + ++NumLoadCommands; + LoadCommandsSize += sizeof(MachO::version_min_command); + } + // Add the data-in-code load command size, if used. unsigned NumDataRegions = Asm.getDataRegions().size(); if (NumDataRegions) { @@ -817,6 +825,20 @@ void MachObjectWriter::WriteObject(MCAssembler &Asm, RelocTableEnd += NumRelocs * sizeof(MachO::any_relocation_info); } + // Write out the deployment target information, if it's available. + if (VersionInfo.Major != 0) { + assert(VersionInfo.Update < 256 && "unencodable update target version"); + assert(VersionInfo.Minor < 256 && "unencodable minor target version"); + assert(VersionInfo.Major < 65536 && "unencodable major target version"); + uint32_t EncodedVersion = VersionInfo.Update | (VersionInfo.Minor << 8) | + (VersionInfo.Major << 16); + Write32(VersionInfo.Kind == MCVM_OSXVersionMin ? MachO::LC_VERSION_MIN_MACOSX : + MachO::LC_VERSION_MIN_IPHONEOS); + Write32(sizeof(MachO::version_min_command)); + Write32(EncodedVersion); + Write32(0); // reserved. + } + // Write the data-in-code load command, if used. uint64_t DataInCodeTableEnd = RelocTableEnd + NumDataRegions * 8; if (NumDataRegions) { diff --git a/lib/Object/MachOObjectFile.cpp b/lib/Object/MachOObjectFile.cpp index 450693f64e1..6b1dd875958 100644 --- a/lib/Object/MachOObjectFile.cpp +++ b/lib/Object/MachOObjectFile.cpp @@ -213,6 +213,14 @@ void SwapStruct(MachO::linker_options_command &C) { SwapValue(C.count); } +template<> +void SwapStruct(MachO::version_min_command&C) { + SwapValue(C.cmd); + SwapValue(C.cmdsize); + SwapValue(C.version); + SwapValue(C.reserved); +} + template<> void SwapStruct(MachO::data_in_code_entry &C) { SwapValue(C.offset); @@ -1467,6 +1475,11 @@ MachOObjectFile::getLinkerOptionsLoadCommand(const LoadCommandInfo &L) const { return getStruct(this, L.Ptr); } +MachO::version_min_command +MachOObjectFile::getVersionMinLoadCommand(const LoadCommandInfo &L) const { + return getStruct(this, L.Ptr); +} + MachO::any_relocation_info MachOObjectFile::getRelocation(DataRefImpl Rel) const { const char *P = reinterpret_cast(Rel.p); diff --git a/test/MC/AsmParser/version-min-diagnostics.s b/test/MC/AsmParser/version-min-diagnostics.s new file mode 100644 index 00000000000..15d44d31661 --- /dev/null +++ b/test/MC/AsmParser/version-min-diagnostics.s @@ -0,0 +1,49 @@ +// RUN: not llvm-mc -triple i386-apple-darwin %s 2> %t +// RUN: FileCheck %s < %t +// RUN: not llvm-mc -triple x86_64-apple-darwin %s 2> %t +// RUN: FileCheck %s < %t +// RUN: not llvm-mc -triple armv7-apple-ios %s 2> %t +// RUN: FileCheck %s < %t + +.ios_version_min 5,2,257 +.ios_version_min 5,256,1 +.ios_version_min 5,-1,1 +.ios_version_min 0,1,1 +.ios_version_min 70000,1 +.macosx_version_min 99,2,257 +.macosx_version_min 50,256,1 +.macosx_version_min 10,-1,1 +.macosx_version_min 0,1,1 +.macosx_version_min 70000,1 + + +// CHECK: error: invalid OS update number +// CHECK: .ios_version_min 5,2,257 +// CHECK: ^ +// CHECK: error: invalid OS minor version number +// CHECK: .ios_version_min 5,256,1 +// CHECK: ^ +// CHECK: error: invalid OS minor version number +// CHECK: .ios_version_min 5,-1,1 +// CHECK: ^ +// CHECK: error: invalid OS major version number +// CHECK: .ios_version_min 0,1,1 +// CHECK: ^ +// CHECK: error: invalid OS major version number +// CHECK: .ios_version_min 70000,1 +// CHECK: ^ +// CHECK: error: invalid OS update number +// CHECK: .macosx_version_min 99,2,257 +// CHECK: ^ +// CHECK: error: invalid OS minor version number +// CHECK: .macosx_version_min 50,256,1 +// CHECK: ^ +// CHECK: error: invalid OS minor version number +// CHECK: .macosx_version_min 10,-1,1 +// CHECK: ^ +// CHECK: error: invalid OS major version number +// CHECK: .macosx_version_min 0,1,1 +// CHECK: ^ +// CHECK: error: invalid OS major version number +// CHECK: .macosx_version_min 70000,1 +// CHECK: ^ diff --git a/test/MC/AsmParser/version-min.s b/test/MC/AsmParser/version-min.s new file mode 100644 index 00000000000..0a40338ed5e --- /dev/null +++ b/test/MC/AsmParser/version-min.s @@ -0,0 +1,21 @@ +// RUN: llvm-mc -triple i386-apple-darwin %s | FileCheck %s +// RUN: llvm-mc -triple x86_64-apple-darwin %s | FileCheck %s +// RUN: llvm-mc -triple armv7s-apple-ios %s | FileCheck %s + +// Test the parsing of well-formed version-min directives. + +.ios_version_min 5,2,0 +.ios_version_min 3,2,1 +.ios_version_min 5,0 + +// CHECK: .ios_version_min 5, 2 +// CHECK: .ios_version_min 3, 2, 1 +// CHECK: .ios_version_min 5, 0 + +.macosx_version_min 10,2,0 +.macosx_version_min 10,8,1 +.macosx_version_min 2,0 + +// CHECK: .macosx_version_min 10, 2 +// CHECK: .macosx_version_min 10, 8, 1 +// CHECK: .macosx_version_min 2, 0 diff --git a/test/MC/MachO/ios-version-min-load-command.s b/test/MC/MachO/ios-version-min-load-command.s new file mode 100644 index 00000000000..e065d147be7 --- /dev/null +++ b/test/MC/MachO/ios-version-min-load-command.s @@ -0,0 +1,10 @@ +// RUN: llvm-mc -triple armv7-apple-ios %s -filetype=obj -o - | macho-dump | FileCheck %s + +// Test the formation of the version-min load command in the MachO. +// use a nonsense but well formed version. +.ios_version_min 99,8,7 +// CHECK: (('command', 37) +// CHECK: ('size', 16) +// CHECK: ('version, 6490119) +// CHECK: ('reserved, 0) +// CHECK: ), diff --git a/test/MC/MachO/osx-version-min-load-command.s b/test/MC/MachO/osx-version-min-load-command.s new file mode 100644 index 00000000000..2a73609dc01 --- /dev/null +++ b/test/MC/MachO/osx-version-min-load-command.s @@ -0,0 +1,10 @@ +// RUN: llvm-mc -triple x86_64-apple-darwin %s -filetype=obj -o - | macho-dump | FileCheck %s + +// Test the formation of the version-min load command in the MachO. +// use a nonsense but well formed version. +.macosx_version_min 25,3,1 +// CHECK: (('command', 36) +// CHECK: ('size', 16) +// CHECK: ('version, 1639169) +// CHECK: ('reserved, 0) +// CHECK: ), diff --git a/tools/macho-dump/macho-dump.cpp b/tools/macho-dump/macho-dump.cpp index beeef5b1767..886487bfb43 100644 --- a/tools/macho-dump/macho-dump.cpp +++ b/tools/macho-dump/macho-dump.cpp @@ -319,6 +319,15 @@ DumpLinkerOptionsCommand(const MachOObjectFile &Obj, return 0; } +static int +DumpVersionMin(const MachOObjectFile &Obj, + const MachOObjectFile::LoadCommandInfo &LCI) { + MachO::version_min_command VMLC = Obj.getVersionMinLoadCommand(LCI); + outs() << " ('version, " << VMLC.version << ")\n" + << " ('reserved, " << VMLC.reserved << ")\n"; + return 0; +} + static int DumpLoadCommand(const MachOObjectFile &Obj, MachOObjectFile::LoadCommandInfo &LCI) { switch (LCI.C.cmd) { @@ -338,6 +347,9 @@ static int DumpLoadCommand(const MachOObjectFile &Obj, return DumpDataInCodeDataCommand(Obj, LCI); case MachO::LC_LINKER_OPTIONS: return DumpLinkerOptionsCommand(Obj, LCI); + case MachO::LC_VERSION_MIN_IPHONEOS: + case MachO::LC_VERSION_MIN_MACOSX: + return DumpVersionMin(Obj, LCI); default: Warning("unknown load command: " + Twine(LCI.C.cmd)); return 0;