Merge "Emit the LC_BUILD_VERSION load command when creating a mach object." into main
diff --git a/asm/nasm.c b/asm/nasm.c
index 7f24b1b..82789f8 100644
--- a/asm/nasm.c
+++ b/asm/nasm.c
@@ -168,6 +168,10 @@
 static char *quote_for_wmake(const char *str);
 static char *(*quote_for_make)(const char *) = quote_for_pmake;
 
+#if defined(OF_MACHO) || defined(OF_MACHO64)
+extern bool macho_set_min_os(const char *str);
+#endif
+
 /*
  * Execution limits that can be set via a command-line option or %pragma
  */
@@ -943,7 +947,8 @@
     OPT_LIMIT,
     OPT_KEEP_ALL,
     OPT_NO_LINE,
-    OPT_DEBUG
+    OPT_DEBUG,
+    OPT_MACHO_MIN_OS
 };
 enum need_arg {
     ARG_NO,
@@ -975,6 +980,7 @@
     {"keep-all", OPT_KEEP_ALL, ARG_NO, 0},
     {"no-line",  OPT_NO_LINE, ARG_NO, 0},
     {"debug",    OPT_DEBUG, ARG_MAYBE, 0},
+    {"macho-min-os", OPT_MACHO_MIN_OS, ARG_YES, 0},
     {NULL, OPT_BOGUS, ARG_NO, 0}
 };
 
@@ -1337,6 +1343,23 @@
                 case OPT_DEBUG:
                     debug_nasm = param ? strtoul(param, NULL, 10) : debug_nasm+1;
                     break;
+                case OPT_MACHO_MIN_OS:
+                    if (pass == 2) {
+                        if (strstr(ofmt->shortname, "macho") != ofmt->shortname) {
+                            nasm_error(
+                                ERR_WARNING | WARN_OTHER | ERR_USAGE,
+                                "macho-min-os is only valid for macho format, current: %s",
+                                ofmt->shortname);
+                            break;
+                        }
+#if defined(OF_MACHO) || defined(OF_MACHO64)
+                        if (!macho_set_min_os(param)) {
+                            nasm_fatalf(ERR_USAGE, "failed to set minimum os for mach-o '%s'",
+                                        param);
+                        }
+#endif
+                    }
+                    break;
                 case OPT_HELP:
                     help(stdout);
                     exit(0);
@@ -2298,6 +2321,8 @@
         "   --lprefix str  prepend the given string to local symbols\n"
         "   --lpostfix str append the given string to local symbols\n"
         "\n"
+        "   --macho-min-os minos minimum os version for mach-o format(example: macos-11.0)\n"
+        "\n"
         "    -w+x          enable warning x (also -Wx)\n"
         "    -w-x          disable warning x (also -Wno-x)\n"
         "    -w[+-]error   promote all warnings to errors (also -Werror)\n"
diff --git a/nasm_assemble.gni b/nasm_assemble.gni
index 902a632..2b380db 100644
--- a/nasm_assemble.gni
+++ b/nasm_assemble.gni
@@ -42,13 +42,28 @@
 #   }
 
 import("//build/compiled_action.gni")
+import("//build/config/ios/config.gni")
+import("//build/config/ios/ios_sdk_overrides.gni")
+if (is_mac) {
+  import("//build/config/mac/mac_sdk.gni")
+}
 
-if (is_mac || is_ios) {
+if ((is_mac || is_ios) && (current_cpu == "x86" || current_cpu == "x64")) {
   if (current_cpu == "x86") {
     _nasm_flags = [ "-fmacho32" ]
   } else if (current_cpu == "x64") {
     _nasm_flags = [ "-fmacho64" ]
   }
+  if (is_mac) {
+    _nasm_flags += [ "--macho-min-os=macos$mac_min_system_version" ]
+  } else if (is_ios) {
+    if (target_environment == "device") {
+      _nasm_flags += [ "--macho-min-os=ios$ios_deployment_target" ]
+    } else {
+      _nasm_flags +=
+          [ "--macho-min-os=ios$ios_deployment_target-$target_environment" ]
+    }
+  }
 } else if (is_posix || is_fuchsia) {
   if (current_cpu == "x86") {
     _nasm_flags = [ "-felf32" ]
diff --git a/output/macho.h b/output/macho.h
index 538c531..6a5883e 100644
--- a/output/macho.h
+++ b/output/macho.h
@@ -60,6 +60,7 @@
 #define LC_SEGMENT			0x1
 #define LC_SEGMENT_64			0x19
 #define LC_SYMTAB			0x2
+#define LC_BUILD_VERSION		0x32
 
 /* Symbol type bits */
 #define N_STAB				0xe0
@@ -144,6 +145,20 @@
 #define R_ABS		0
 #define R_SCATTERED	0x80000000
 
+/* Known values for the platform field in LC_BUILD_VERSION */
+#define PLATFORM_MACOS				1
+#define PLATFORM_IOS				2
+#define PLATFORM_TVOS				3
+#define PLATFORM_WATCHOS			4
+#define PLATFORM_BRIDGEOS			5
+#define PLATFORM_MACCATALYST		6
+#define PLATFORM_IOSSIMULATOR		7
+#define PLATFORM_TVOSSIMULATOR		8
+#define PLATFORM_WATCHOSSIMULATOR	9
+#define PLATFORM_DRIVERKIT			10
+
+#define PLATFORM_INVALID			0
+
 /* VM permission constants */
 #define	VM_PROT_NONE			0x00
 #define VM_PROT_READ			0x01
diff --git a/output/outmacho.c b/output/outmacho.c
index 0814788..d1568a7 100644
--- a/output/outmacho.c
+++ b/output/outmacho.c
@@ -64,6 +64,7 @@
 #define MACHO_SYMCMD_SIZE		24
 #define MACHO_NLIST_SIZE		12
 #define MACHO_RELINFO_SIZE		8
+#define MACHO_BUILDVERSION_SIZE		24
 
 #define MACHO_HEADER64_SIZE		32
 #define MACHO_SEGCMD64_SIZE		72
@@ -215,6 +216,15 @@
 static uint32_t seg_nsects = 0;
 static uint64_t rel_padcnt = 0;
 
+/* build_version_platform information
+   PLATFORM_INVALID implies that build_version_platform won't emitted. */
+static uint32_t build_version_platform = PLATFORM_INVALID;
+/* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+static uint32_t build_version_minos = 0;
+
+/* used in nasm.c */
+bool macho_set_min_os(const char *str);
+
 /*
  * Functions for handling fixed-length zero-padded string
  * fields, that may or may not be null-terminated.
@@ -744,6 +754,7 @@
     { ".data",          "__DATA",   "__data",           S_REGULAR       },
     { ".rodata",        "__DATA",   "__const",          S_REGULAR       },
     { ".bss",           "__DATA",   "__bss",            S_ZEROFILL      },
+    {".llvmasm",        "__LLVM",   "__asm",            S_REGULAR       },
     { ".debug_abbrev",  "__DWARF",  "__debug_abbrev",   S_ATTR_DEBUG    },
     { ".debug_info",    "__DWARF",  "__debug_info",     S_ATTR_DEBUG    },
     { ".debug_line",    "__DWARF",  "__debug_line",     S_ATTR_DEBUG    },
@@ -1263,6 +1274,12 @@
 	}
     }
 
+    /* for build_version_command */
+    if (build_version_platform != PLATFORM_INVALID) {
+        ++head_ncmds;
+        head_sizeofcmds = MACHO_BUILDVERSION_SIZE;
+    }
+
     /* calculate size of all headers, load commands and sections to
     ** get a pointer to the start of all the raw data */
     if (seg_nsects > 0) {
@@ -1647,6 +1664,16 @@
 
     offset = fmt.header_size + head_sizeofcmds;
 
+    if (build_version_platform != PLATFORM_INVALID) {
+        /* emit build_version_command */
+        fwriteint32_t(LC_BUILD_VERSION, ofile);
+        fwriteint32_t(MACHO_BUILDVERSION_SIZE, ofile);
+        fwriteint32_t(build_version_platform, ofile);
+        fwriteint32_t(build_version_minos, ofile);
+        fwriteint32_t(0 /* sdk */, ofile);
+        fwriteint32_t(0 /* ntools */, ofile);
+    }
+
     /* emit the segment load command */
     if (seg_nsects > 0)
 	offset = macho_write_segment (offset);
@@ -1687,9 +1714,20 @@
     struct section *s;
     struct reloc *r;
     struct symbol *sym;
+    int bits;
 
     dfmt->cleanup();
 
+    /* create a dummy __asm section with a single zero byte.
+     * this is a workaround for making binaries compatible with
+     * bitcode enabled, which is required for watchOS and tvOS */
+    macho_section(".llvmasm", &bits);
+    s = get_section_by_name("__LLVM", "__asm");
+    if (s != NULL) {
+        saa_write8(s->data, 0);
+        s->size += 1;
+    }
+
     /* Sort all symbols.  */
     macho_layout_symbols (&nsyms, &strslen);
 
@@ -2366,6 +2404,85 @@
 static const struct dfmt * const macho64_df_arr[2] =
  { &macho64_df_dwarf, NULL };
 
+bool macho_set_min_os(const char *str) {
+    nasm_assert(str != NULL);
+
+    const char *platform_ver = nasm_strdup(str);
+    const char *environment = "";
+    char *sep = strchr(platform_ver, '-');
+    if (sep != NULL) {
+        sep[0] = '\0';
+        environment = sep + 1;
+    }
+
+    const char *version = platform_ver;
+    while (*version) {
+        if (*version >= '0' && *version <= '9') {
+            break;
+        }
+        ++version;
+    }
+    if (*version == '\0') {
+        nasm_free((char *)platform_ver);
+        return false;
+    }
+
+    /* Mimic clang's target triple */
+    int platform = PLATFORM_INVALID;
+    if (strstr(platform_ver, "macos") == platform_ver) {
+        platform = PLATFORM_MACOS;
+    } else if ((strstr(platform_ver, "ios") == platform_ver)) {
+        if (environment[0] == '\0') {
+            platform = PLATFORM_IOS;
+        } else if ((strstr(environment, "simulator") == environment)) {
+            platform = PLATFORM_IOSSIMULATOR;
+        } else if ((strstr(environment, "catalyst") == environment)) {
+            platform = PLATFORM_MACCATALYST;
+        } else {
+            nasm_free((char *)platform_ver);
+            return false;
+        }
+    } else if ((strstr(platform_ver, "tvos") == platform_ver)) {
+        if (environment[0] == '\0') {
+            platform = PLATFORM_TVOS;
+        } else if ((strstr(environment, "simulator") == environment)) {
+            platform = PLATFORM_TVOSSIMULATOR;
+        } else {
+            nasm_free((char *)platform_ver);
+            return false;
+        }
+    } else if (xstrncmp("watchos", platform_ver) == 0) {
+        if (environment[0] == '\0') {
+            platform = PLATFORM_WATCHOS;
+        } else if ((strstr(environment, "simulator") == environment)) {
+            platform = PLATFORM_WATCHOSSIMULATOR;
+        } else {
+            nasm_free((char *)platform_ver);
+            return false;
+        }
+    } else if (xstrncmp("bridgeos", platform_ver) == 0) {
+        platform = PLATFORM_BRIDGEOS;
+    } else {
+        nasm_free((char *)platform_ver);
+        return false;
+    }
+
+    unsigned short major = 0, minor = 0, subminor = 0;
+    int count = sscanf(version, "%hu.%hu.%hu", &major, &minor, &subminor);
+    /* at least major and minor must be given */
+    if (count < 2) {
+        nasm_free((char *)platform_ver);
+        return false;
+    }
+
+    build_version_platform = platform;
+    build_version_minos =
+        ((major & 0xffff) << 16) | ((minor & 0xff) << 8) | (subminor & 0xff);
+
+    nasm_free((char *)platform_ver);
+    return true;
+}
+
 const struct ofmt of_macho64 = {
     "Mach-O x86-64 (Mach, including MacOS X and variants)",
     "macho64",