summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorByoungchan Lee <daniel.l@hpcnt.com>2021-07-27 05:09:09 +0900
committerByoungchan Lee <daniel.l@hpcnt.com>2021-07-27 05:09:09 +0900
commit3dc3d47978ef567966cb21a7b6d617fb688e6029 (patch)
tree40a20c571ea2709c7e5e976253842c66934f21a9
parente9be5fd6d723a435ca2da162f9e0ffcb688747c1 (diff)
downloadnasm-3dc3d47978ef567966cb21a7b6d617fb688e6029.tar.gz
Emit the LC_BUILD_VERSION load command when creating a mach object.
LC_BUILD_VERSION 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. Also, emit the __LLVM segment, __asm section to tell the Apple toolchain that this object is from assembler and has no bitcode. This trick is used in Kotlin/Native, Rust, Flutter, Golang and yasm. Bug: 1145197, webrtc:11516 Change-Id: I6859cc171f80d1aa86d9d7187c2f8ceac7b8c4f3
-rw-r--r--asm/nasm.c27
-rw-r--r--nasm_assemble.gni17
-rw-r--r--output/macho.h15
-rw-r--r--output/outmacho.c117
4 files changed, 174 insertions, 2 deletions
diff --git a/asm/nasm.c b/asm/nasm.c
index 7f24b1b7..82789f8c 100644
--- a/asm/nasm.c
+++ b/asm/nasm.c
@@ -168,6 +168,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
*/
@@ -943,7 +947,8 @@ enum text_options {
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 @@ static const struct textargs textopts[] = {
{"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 @@ static bool process_arg(char *p, char *q, int pass)
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 @@ static void help(FILE *out)
" --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 902a6320..2b380dbf 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 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 08147883..d1568a71 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.
@@ -744,6 +754,7 @@ static const struct macho_known_section {
{ ".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 @@ 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 +1664,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);
@@ -1687,9 +1714,20 @@ static void macho_cleanup(void)
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 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",