Skip to content

Emit the LC_BUILD_VERSION load command when creating a mach object. #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions asm/nasm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -1338,6 +1342,23 @@ 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 (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);
Expand Down Expand Up @@ -2297,6 +2318,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: 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"
Expand Down
15 changes: 15 additions & 0 deletions output/macho.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
117 changes: 117 additions & 0 deletions output/outmacho.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 },
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -2376,6 +2414,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",
Expand Down