Darwin: Add assembler directives to create version-min load commands.
authorJim Grosbach <grosbach@apple.com>
Tue, 18 Mar 2014 22:09:05 +0000 (22:09 +0000)
committerJim Grosbach <grosbach@apple.com>
Tue, 18 Mar 2014 22:09:05 +0000 (22:09 +0000)
Allow object files to be tagged with a version-min load command for iOS
or MacOSX.

Teach macho-dump to understand the version-min load commands for
testcases.

rdar://11337778

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@204190 91177308-0d34-0410-b5e6-96231b3b80d8

15 files changed:
include/llvm/MC/MCAssembler.h
include/llvm/MC/MCDirectives.h
include/llvm/MC/MCStreamer.h
include/llvm/Object/MachO.h
lib/MC/MCAsmStreamer.cpp
lib/MC/MCAssembler.cpp
lib/MC/MCMachOStreamer.cpp
lib/MC/MCParser/DarwinAsmParser.cpp
lib/MC/MachObjectWriter.cpp
lib/Object/MachOObjectFile.cpp
test/MC/AsmParser/version-min-diagnostics.s [new file with mode: 0644]
test/MC/AsmParser/version-min.s [new file with mode: 0644]
test/MC/MachO/ios-version-min-load-command.s [new file with mode: 0644]
test/MC/MachO/osx-version-min-load-command.s [new file with mode: 0644]
tools/macho-dump/macho-dump.cpp

index c4b475ee03c584d7da1f3aff478248b6a66a5d1f..4b519dd7c97812cbbc1dbcf92580e0415fe72580 100644 (file)
@@ -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<DataRegionData>::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.
   ///
index 0461766c2fd3c0ac71aadb37f51e1f9b0f45b914..f9d66e0b15d7e73d34f7897ccb0b231585efb6e8 100644 (file)
@@ -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
index fc7a3b8648210b846c4690c9a648390e79cf4fc4..37d18fbe43a6121d09e3838d5762a167009e9555 100644 (file)
@@ -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;
index 50296aed9b8fb6dbdc3cc42b96032e7249c155d5..34edec3ebfd123aaaa7c975138a942ce823b80f1 100644 (file)
@@ -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;
index 85263fec6260ed0203b05db85a148128d9e2ecb9..e28c56e7d31263f95938d4809f04ebe072438062 100644 (file)
@@ -133,6 +133,8 @@ public:
   void EmitAssemblerFlag(MCAssemblerFlag Flag) override;
   void EmitLinkerOptions(ArrayRef<std::string> 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.
index 89d7d1901675208f1f0ba7e3c3080a78ccbdf143..910295c5d2b8e9dd3d8586b5101c783a1de8cef9 100644 (file)
@@ -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() {
index fef7b6bd70b713e391a11774d3a45c9ffc10640f..c932c3c375aeff044766ecb6b58c6911e1e981b3 100644 (file)
@@ -49,6 +49,8 @@ public:
   void EmitAssemblerFlag(MCAssemblerFlag Flag) override;
   void EmitLinkerOptions(ArrayRef<std::string> 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.
index 81c2cf021c1090829a140671d2adab4c271b1cd3..dd7eccb2378bf64049c4a35d9f0d6119acca4dd6 100644 (file)
@@ -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<int>(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() {
index b3b593d3154be1bfa30fedaa38d0bf5c578f4b35..ff55c978d6adf4ee7aa81448e4e26060345217f0 100644 (file)
@@ -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) {
index 450693f64e1b23571c696fe32fdfec6980b8842b..6b1dd8759585e0c2882e3ad15e80fe6fa05a9b79 100644 (file)
@@ -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<MachO::linker_options_command>(this, L.Ptr);
 }
 
+MachO::version_min_command
+MachOObjectFile::getVersionMinLoadCommand(const LoadCommandInfo &L) const {
+  return getStruct<MachO::version_min_command>(this, L.Ptr);
+}
+
 MachO::any_relocation_info
 MachOObjectFile::getRelocation(DataRefImpl Rel) const {
   const char *P = reinterpret_cast<const char *>(Rel.p);
diff --git a/test/MC/AsmParser/version-min-diagnostics.s b/test/MC/AsmParser/version-min-diagnostics.s
new file mode 100644 (file)
index 0000000..15d44d3
--- /dev/null
@@ -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 (file)
index 0000000..0a40338
--- /dev/null
@@ -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 (file)
index 0000000..e065d14
--- /dev/null
@@ -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 (file)
index 0000000..2a73609
--- /dev/null
@@ -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:  ),
index beeef5b176765a679be942e19fc4859e1724611c..886487bfb43e792d44203149c89a558f4de3c0dc 100644 (file)
@@ -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;