Generate version min load commands when the platform is unknown.
In the case where we are emitting to an object file, the platform is
possibly unknown, and the source object files contained load commands
for version min, we can take the maximum of those min versions and
emit in in the output object file.
This test also tests r259739.
llvm-svn: 259742
diff --git a/lld/lib/ReaderWriter/MachO/File.h b/lld/lib/ReaderWriter/MachO/File.h
index f282d5d..a0d20ea 100644
--- a/lld/lib/ReaderWriter/MachO/File.h
+++ b/lld/lib/ReaderWriter/MachO/File.h
@@ -201,6 +201,16 @@
     _objcConstraint = v;
   }
 
+  uint32_t minVersion() const { return _minVersion; }
+  void setMinVersion(uint32_t v) { _minVersion = v; }
+
+  LoadCommandType minVersionLoadCommandKind() const {
+    return _minVersionLoadCommandKind;
+  }
+  void setMinVersionLoadCommandKind(LoadCommandType v) {
+    _minVersionLoadCommandKind = v;
+  }
+
   uint32_t swiftVersion() const { return _swiftVersion; }
   void setSwiftVersion(uint32_t v) { _swiftVersion = v; }
 
@@ -249,6 +259,8 @@
   NameToAtom                     _undefAtoms;
   MachOLinkingContext::Arch      _arch = MachOLinkingContext::arch_unknown;
   MachOLinkingContext::OS        _os = MachOLinkingContext::OS::unknown;
+  uint32_t                       _minVersion = 0;
+  LoadCommandType               _minVersionLoadCommandKind = (LoadCommandType)0;
   MachOLinkingContext::ObjCConstraint _objcConstraint =
       MachOLinkingContext::objc_unknown;
   uint32_t                       _swiftVersion = 0;
diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFile.h b/lld/lib/ReaderWriter/MachO/MachONormalizedFile.h
index d2b4806..6dab8ba 100644
--- a/lld/lib/ReaderWriter/MachO/MachONormalizedFile.h
+++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFile.h
@@ -253,6 +253,7 @@
   Hex64                       sourceVersion = 0;
   PackedVersion               minOSverson = 0;
   PackedVersion               sdkVersion = 0;
+  LoadCommandType             minOSVersionKind = (LoadCommandType)0;
 
   // Maps to load commands with LINKEDIT content (final linked images only).
   Hex32                       pageSize = 0;
diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
index 1013d3d..7a9dd4b 100644
--- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
+++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
@@ -471,6 +471,15 @@
     case LC_DYLD_INFO_ONLY:
       dyldInfo = reinterpret_cast<const dyld_info_command*>(lc);
       break;
+    case LC_VERSION_MIN_MACOSX:
+    case LC_VERSION_MIN_IPHONEOS:
+    case LC_VERSION_MIN_WATCHOS:
+    case LC_VERSION_MIN_TVOS:
+      // If we are emitting an object file, then we may take the load command
+      // kind from these commands and pass it on to the output
+      // file.
+      f->minOSVersionKind = (LoadCommandType)cmd;
+      break;
     }
     return false;
   });
diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp
index d46cf2c..0eb2b11 100644
--- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp
+++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp
@@ -317,6 +317,10 @@
                                + file.sections.size() * sectsSize
                                + sizeof(symtab_command);
     _countOfLoadCommands = 2;
+    if (file.hasMinVersionLoadCommand) {
+      _endOfLoadCommands += sizeof(version_min_command);
+      _countOfLoadCommands++;
+    }
     if (!_file.dataInCode.empty()) {
       _endOfLoadCommands += sizeof(linkedit_data_command);
       _countOfLoadCommands++;
@@ -738,6 +742,38 @@
   return std::error_code();
 }
 
+static void writeVersionMinLoadCommand(const NormalizedFile &_file,
+                                       bool _swap,
+                                       uint8_t *&lc) {
+  if (!_file.hasMinVersionLoadCommand)
+    return;
+  version_min_command *vm = reinterpret_cast<version_min_command*>(lc);
+  switch (_file.os) {
+    case MachOLinkingContext::OS::unknown:
+      vm->cmd     = _file.minOSVersionKind;
+      vm->cmdsize = sizeof(version_min_command);
+      vm->version = _file.minOSverson;
+      vm->sdk     = 0;
+      break;
+    case MachOLinkingContext::OS::macOSX:
+      vm->cmd     = LC_VERSION_MIN_MACOSX;
+      vm->cmdsize = sizeof(version_min_command);
+      vm->version = _file.minOSverson;
+      vm->sdk     = _file.sdkVersion;
+      break;
+    case MachOLinkingContext::OS::iOS:
+    case MachOLinkingContext::OS::iOS_simulator:
+      vm->cmd     = LC_VERSION_MIN_IPHONEOS;
+      vm->cmdsize = sizeof(version_min_command);
+      vm->version = _file.minOSverson;
+      vm->sdk     = _file.sdkVersion;
+      break;
+  }
+  if (_swap)
+    swapStruct(*vm);
+  lc += sizeof(version_min_command);
+}
+
 std::error_code MachOFileLayout::writeLoadCommands() {
   std::error_code ec;
   uint8_t *lc = &_buffer[_startOfLoadCommands];
@@ -759,6 +795,11 @@
     if (_swap)
       swapStruct(*st);
     lc += sizeof(symtab_command);
+
+    // Add LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS,
+    // LC_VERSION_MIN_WATCHOS, LC_VERSION_MIN_TVOS
+    writeVersionMinLoadCommand(_file, _swap, lc);
+
     // Add LC_DATA_IN_CODE if needed.
     if (_dataInCodeSize != 0) {
       linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc);
@@ -872,32 +913,7 @@
 
     // Add LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_VERSION_MIN_WATCHOS,
     // LC_VERSION_MIN_TVOS
-    if (_file.hasMinVersionLoadCommand) {
-      version_min_command *vm = reinterpret_cast<version_min_command*>(lc);
-      switch (_file.os) {
-        case MachOLinkingContext::OS::unknown:
-          // TODO: We need to emit the load command if we managed to derive
-          // a platform from one of the files we are linking.
-          llvm_unreachable("Version commands for unknown OS aren't supported");
-          break;
-        case MachOLinkingContext::OS::macOSX:
-          vm->cmd     = LC_VERSION_MIN_MACOSX;
-          vm->cmdsize = sizeof(version_min_command);
-          vm->version = _file.minOSverson;
-          vm->sdk     = _file.sdkVersion;
-          break;
-        case MachOLinkingContext::OS::iOS:
-        case MachOLinkingContext::OS::iOS_simulator:
-          vm->cmd     = LC_VERSION_MIN_MACOSX;
-          vm->cmdsize = sizeof(version_min_command);
-          vm->version = _file.minOSverson;
-          vm->sdk     = _file.sdkVersion;
-          break;
-      }
-      if (_swap)
-        swapStruct(*vm);
-      lc += sizeof(version_min_command);
-    }
+    writeVersionMinLoadCommand(_file, _swap, lc);
 
     // If main executable, add LC_MAIN.
     if (_file.fileType == llvm::MachO::MH_EXECUTE) {
diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
index a2b3379..087b557 100644
--- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
+++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
@@ -128,6 +128,18 @@
   void      copyEntryPointAddress(NormalizedFile &file);
   void      copySectionContent(NormalizedFile &file);
 
+  bool allSourceFilesHaveMinVersions() const {
+    return _allSourceFilesHaveMinVersions;
+  }
+
+  uint32_t minVersion() const {
+    return _minVersion;
+  }
+
+  LoadCommandType minVersionCommandType() const {
+    return _minVersionCommandType;
+  }
+
 private:
   typedef std::map<DefinedAtom::ContentType, SectionInfo*> TypeToSection;
   typedef llvm::DenseMap<const Atom*, uint64_t> AtomToAddress;
@@ -183,6 +195,9 @@
   std::vector<const Atom *>     _machHeaderAliasAtoms;
   bool                          _hasTLVDescriptors;
   bool                          _subsectionsViaSymbols;
+  bool                          _allSourceFilesHaveMinVersions = true;
+  LoadCommandType               _minVersionCommandType = (LoadCommandType)0;
+  uint32_t                      _minVersion = 0;
 };
 
 Util::~Util() {
@@ -378,11 +393,25 @@
 }
 
 void Util::processAtomAttributes(const DefinedAtom *atom) {
-  // If the file doesn't use subsections via symbols, then make sure we don't
-  // add that flag to the final output file if we have a relocatable file.
-  if (auto *machoFile = dyn_cast<mach_o::MachOFile>(&atom->file()))
+  if (auto *machoFile = dyn_cast<mach_o::MachOFile>(&atom->file())) {
+    // If the file doesn't use subsections via symbols, then make sure we don't
+    // add that flag to the final output file if we have a relocatable file.
     if (!machoFile->subsectionsViaSymbols())
       _subsectionsViaSymbols = false;
+
+    // All the source files must have min versions for us to output an object
+    // file with a min version.
+    if (auto v = machoFile->minVersion())
+      _minVersion = std::max(_minVersion, v);
+    else
+      _allSourceFilesHaveMinVersions = false;
+
+    // If we don't have a platform load command, but one of the source files
+    // does, then take the one from the file.
+    if (!_minVersionCommandType)
+      if (auto v = machoFile->minVersionLoadCommandKind())
+        _minVersionCommandType = v;
+  }
 }
 
 void Util::assignAtomToSection(const DefinedAtom *atom) {
@@ -1245,14 +1274,31 @@
   normFile.currentVersion = context.currentVersion();
   normFile.compatVersion = context.compatibilityVersion();
   normFile.os = context.os();
-  normFile.minOSverson = context.osMinVersion();
-  // FIXME: We need to get the SDK version from the system.  For now the min
-  // OS version is better than nothing.
+
+  // If we are emitting an object file, then the min version is the maximum
+  // of the min's of all the source files and the cmdline.
+  if (normFile.fileType == llvm::MachO::MH_OBJECT)
+    normFile.minOSverson = std::max(context.osMinVersion(), util.minVersion());
+  else
+    normFile.minOSverson = context.osMinVersion();
+
+  normFile.minOSVersionKind = util.minVersionCommandType();
+
   normFile.sdkVersion = context.sdkVersion();
 
   if (context.generateVersionLoadCommand() &&
       context.os() != MachOLinkingContext::OS::unknown)
     normFile.hasMinVersionLoadCommand = true;
+  else if (normFile.fileType == llvm::MachO::MH_OBJECT &&
+           util.allSourceFilesHaveMinVersions() &&
+           ((normFile.os != MachOLinkingContext::OS::unknown) ||
+            util.minVersionCommandType())) {
+    // If we emit an object file, then it should contain a min version load
+    // command if all of the source files also contained min version commands.
+    // Also, we either need to have a platform, or found a platform from the
+    // source object files.
+    normFile.hasMinVersionLoadCommand = true;
+  }
   normFile.pageSize = context.pageSize();
   normFile.rpaths = context.rpaths();
   util.addDependentDylibs(atomFile, normFile);
diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
index 80f2038..3d4457a 100644
--- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
+++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
@@ -1061,6 +1061,8 @@
   file->setFlags(normalizedFile.flags);
   file->setArch(normalizedFile.arch);
   file->setOS(normalizedFile.os);
+  file->setMinVersion(normalizedFile.minOSverson);
+  file->setMinVersionLoadCommandKind(normalizedFile.minOSVersionKind);
 
   // Sort references in each atom to their canonical order.
   for (const DefinedAtom* defAtom : file->defined()) {
diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp
index 0b92a68..c795a72 100644
--- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp
+++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp
@@ -524,6 +524,14 @@
                         llvm::MachO::LC_LOAD_UPWARD_DYLIB);
     io.enumCase(value, "LC_LAZY_LOAD_DYLIB",
                         llvm::MachO::LC_LAZY_LOAD_DYLIB);
+    io.enumCase(value, "LC_VERSION_MIN_MACOSX",
+                        llvm::MachO::LC_VERSION_MIN_MACOSX);
+    io.enumCase(value, "LC_VERSION_MIN_IPHONEOS",
+                        llvm::MachO::LC_VERSION_MIN_IPHONEOS);
+    io.enumCase(value, "LC_VERSION_MIN_TVOS",
+                        llvm::MachO::LC_VERSION_MIN_TVOS);
+    io.enumCase(value, "LC_VERSION_MIN_WATCHOS",
+                        llvm::MachO::LC_VERSION_MIN_WATCHOS);
   }
 };
 
@@ -692,6 +700,7 @@
     io.mapOptional("source-version",   file.sourceVersion,  Hex64(0));
     io.mapOptional("OS",               file.os);
     io.mapOptional("min-os-version",   file.minOSverson,    PackedVersion(0));
+    io.mapOptional("min-os-version-kind",   file.minOSVersionKind, (LoadCommandType)0);
     io.mapOptional("sdk-version",      file.sdkVersion,     PackedVersion(0));
     io.mapOptional("segments",         file.segments);
     io.mapOptional("sections",         file.sections);