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",