[Nasm-devel] [PATCH 1/2] outmacho: Introduce a option for build_version_command

Byoungchan Lee byoungchan.lee at gmx.com
Sat Jul 17 18:11:00 PDT 2021


From: Byoungchan Lee <daniel.l at hpcnt.com>

The build_version_command is contains the min OS version on which this binary
was built to run for its platform in mach object. It is required for targets
like iOS Catalyst.

Signed-off-by: Byoungchan Lee <daniel.l at hpcnt.com>
---
 asm/nasm.c        |  24 ++++++++++-
 output/macho.h    |  15 +++++++
 output/outmacho.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 143 insertions(+), 1 deletion(-)

diff --git a/asm/nasm.c b/asm/nasm.c
index 6af92754..0f3b7edc 100644
--- a/asm/nasm.c
+++ b/asm/nasm.c
@@ -167,6 +167,10 @@ static char *quote_for_pmake(const char *str);
 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
  */
@@ -942,7 +946,8 @@ enum text_options {
     OPT_KEEP_ALL,
     OPT_NO_LINE,
     OPT_DEBUG,
-    OPT_REPRODUCIBLE
+    OPT_REPRODUCIBLE,
+    OPT_MACHO_MIN_OS
 };
 enum need_arg {
     ARG_NO,
@@ -975,6 +980,7 @@ static const struct textargs textopts[] = {
     {"no-line",  OPT_NO_LINE, ARG_NO, 0},
     {"debug",    OPT_DEBUG, ARG_MAYBE, 0},
     {"reproducible", OPT_REPRODUCIBLE, ARG_NO, 0},
+    {"macho-min-os", OPT_MACHO_MIN_OS, ARG_YES, 0},
     {NULL, OPT_BOGUS, ARG_NO, 0}
 };
 
@@ -1338,6 +1344,20 @@ static bool process_arg(char *p, char *q, int pass)
                 case OPT_REPRODUCIBLE:
                     reproducible = true;
                     break;
+                case OPT_MACHO_MIN_OS:
+                    if (pass == 2) {
+                        if (strnstr(ofmt->shortname, "macho", 5) != 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);
@@ -2297,6 +2317,8 @@ static void help(FILE *out)
         "\n"
         "   --reproducible attempt to produce run-to-run identical output\n"
         "\n"
+        "   --macho-min-os minos minimum os version for mach-o format(example: macos11.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/output/macho.h b/output/macho.h
index 538c531e..6a5883e3 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 d041df64..61f68fcf 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 uint64_t seg_vmsize = 0;
 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.
@@ -1263,6 +1273,12 @@ static void macho_calculate_sizes (void)
 	}
     }
 
+    /* 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 +1663,16 @@ static void macho_write (void)
 
     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);
@@ -2376,6 +2402,85 @@ static const struct dfmt macho64_df_dwarf = {
 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",
-- 
2.30.0



More information about the Nasm-devel mailing list