3939#define MACHO_SEGCMD64_SIZE 72
4040#define MACHO_SECTCMD64_SIZE 80
4141#define MACHO_NLIST64_SIZE 16
42+ #define MACHO_BUILD_VERSION_SIZE 24
4243
4344/* Mach-O relocations numbers */
4445
4546#define VM_PROT_DEFAULT (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE)
4647#define VM_PROT_ALL (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE)
4748
49+ /* Platforms enum */
50+ enum macho_platform {
51+ #define X(_platform, _id, _name) _platform = _id,
52+ MACHO_ALL_PLATFORMS
53+ #undef X
54+ };
55+
4856/* Our internal relocation types */
4957enum reltype {
5058 RL_ABS , /* Absolute relocation */
@@ -185,6 +193,10 @@ static uint64_t seg_vmsize = 0;
185193static uint32_t seg_nsects = 0 ;
186194static uint64_t rel_padcnt = 0 ;
187195
196+ static uint32_t buildver_platform = PLATFORM_UNKNOWN ;
197+ static uint32_t buildver_minos = 0 ; // x.y.z is 0xXXXXYYZZ
198+ static uint32_t buildver_sdk = 0 ; // x.y.z is 0xXXXXYYZZ
199+
188200/*
189201 * Functions for handling fixed-length zero-padded string
190202 * fields, that may or may not be null-terminated.
@@ -1235,6 +1247,11 @@ static void macho_calculate_sizes (void)
12351247
12361248 /* calculate size of all headers, load commands and sections to
12371249 ** get a pointer to the start of all the raw data */
1250+ if (buildver_platform != PLATFORM_UNKNOWN ) {
1251+ ++ head_ncmds ;
1252+ head_sizeofcmds += MACHO_BUILD_VERSION_SIZE ;
1253+ }
1254+
12381255 if (seg_nsects > 0 ) {
12391256 ++ head_ncmds ;
12401257 head_sizeofcmds += fmt .segcmd_size + seg_nsects * fmt .sectcmd_size ;
@@ -1617,6 +1634,16 @@ static void macho_write (void)
16171634
16181635 offset = fmt .header_size + head_sizeofcmds ;
16191636
1637+ /* emit the build_version command early, if desired */
1638+ if (buildver_platform != PLATFORM_UNKNOWN ) {
1639+ fwriteint32_t (LC_BUILD_VERSION , ofile ); /* cmd == LC_BUILD_VERSION */
1640+ fwriteint32_t (MACHO_BUILD_VERSION_SIZE , ofile ); /* size of load command */
1641+ fwriteint32_t (buildver_platform , ofile ); /* platform */
1642+ fwriteint32_t (buildver_minos , ofile ); /* minos */
1643+ fwriteint32_t (buildver_sdk , ofile ); /* sdk */
1644+ fwriteint32_t (0 , ofile ); /* ntools */
1645+ }
1646+
16201647 /* emit the segment load command */
16211648 if (seg_nsects > 0 )
16221649 offset = macho_write_segment (offset );
@@ -1764,6 +1791,124 @@ static enum directive_result macho_no_dead_strip(const char *labels)
17641791 return rv ;
17651792}
17661793
1794+ static bool macho_match_string (const char * * pp , const char * target_name )
1795+ {
1796+ const char * p = * pp ;
1797+ while (* target_name ) {
1798+ if (* p ++ != * target_name ++ )
1799+ return false;
1800+ }
1801+
1802+ /* must have exhausted the run of identifier characters */
1803+ if (nasm_isidchar (* p )) {
1804+ return false;
1805+ }
1806+
1807+ * pp = p ;
1808+ return true;
1809+ }
1810+
1811+ static bool macho_scan_number (const char * * pp , int64_t * result )
1812+ {
1813+ bool error = false;
1814+ const char * p = * pp ;
1815+ while (nasm_isdigit (* p ))
1816+ ++ p ;
1817+
1818+ if (p == * pp ) {
1819+ * result = 0 ;
1820+ return false;
1821+ }
1822+
1823+ * result = readnum (* pp , & error );
1824+ * pp = p ;
1825+ return !error ;
1826+ }
1827+
1828+ static bool macho_scan_version (const char * * pp , uint32_t * result )
1829+ {
1830+ int64_t major = 0 ;
1831+ int64_t minor = 0 ;
1832+ int64_t trailing = 0 ;
1833+
1834+ /* version: major, minor (, trailing)? */
1835+ * result = 0 ;
1836+
1837+ if (!macho_scan_number (pp , & major ) || major < 0 || major > 65535 )
1838+ return false;
1839+ * pp = nasm_skip_spaces (* pp );
1840+ if (* * pp != ',' ) /* comma after major ver is required */
1841+ return false;
1842+ * pp = nasm_skip_spaces (* pp + 1 );
1843+
1844+ if (!macho_scan_number (pp , & minor ) || minor < 0 || minor > 255 )
1845+ return false;
1846+ * pp = nasm_skip_spaces (* pp );
1847+
1848+ if (* * pp == ',' ) {
1849+ /* trailing version present */
1850+ * pp = nasm_skip_spaces (* pp + 1 );
1851+ if (!macho_scan_number (pp , & trailing ) || trailing < 0 || trailing > 255 )
1852+ return false;
1853+ }
1854+
1855+ * result = (uint32_t ) ((major << 16 ) | (minor << 8 ) | trailing );
1856+ return true;
1857+ }
1858+
1859+ /*
1860+ * Specify a build version
1861+ */
1862+ static enum directive_result macho_build_version (const char * buildversion )
1863+ {
1864+ /* Matching .build_version directive in LLVM-MC */
1865+ const char * p ;
1866+ uint32_t platform = PLATFORM_UNKNOWN ;
1867+ uint32_t minos = 0 ;
1868+ uint32_t sdk = 0 ;
1869+
1870+ p = nasm_skip_spaces (buildversion );
1871+
1872+ #define X (_platform ,_id ,_name ) if (macho_match_string(&p, _name)) platform = _platform;
1873+ MACHO_ALL_PLATFORMS
1874+ #undef X
1875+
1876+ if (platform == PLATFORM_UNKNOWN ) {
1877+ nasm_nonfatal ("unknown platform name" );
1878+ return DIRR_ERROR ;
1879+ }
1880+
1881+ p = nasm_skip_spaces (p );
1882+ if (* p != ',' ) {
1883+ nasm_nonfatal ("version number required, comma expected" );
1884+ return DIRR_ERROR ;
1885+ }
1886+ p = nasm_skip_spaces (p + 1 );
1887+
1888+ if (!macho_scan_version (& p , & minos )) {
1889+ nasm_nonfatal ("malformed version number" );
1890+ return DIRR_ERROR ;
1891+ }
1892+
1893+ p = nasm_skip_spaces (p );
1894+ if (* p ) {
1895+ if (macho_match_string (& p , "sdk_version" )) {
1896+ p = nasm_skip_spaces (p );
1897+
1898+ if (!macho_scan_version (& p , & sdk )) {
1899+ nasm_nonfatal ("malformed sdk_version" );
1900+ return DIRR_ERROR ;
1901+ }
1902+ } else
1903+ nasm_nonfatal ("extra characters in build_version" );
1904+ }
1905+
1906+ buildver_platform = platform ;
1907+ buildver_minos = minos ;
1908+ buildver_sdk = sdk ;
1909+ return DIRR_OK ;
1910+ }
1911+
17671912/*
17681913 * Mach-O pragmas
17691914 */
@@ -1786,6 +1931,12 @@ macho_pragma(const struct pragma *pragma)
17861931 case D_NO_DEAD_STRIP :
17871932 return macho_no_dead_strip (pragma -> tail );
17881933
1934+ case D_unknown :
1935+ if (!strcmp (pragma -> opname , "build_version" ))
1936+ return macho_build_version (pragma -> tail );
1937+
1938+ return DIRR_UNKNOWN ;
1939+
17891940 default :
17901941 return DIRR_UNKNOWN ; /* Not a Mach-O directive */
17911942 }
0 commit comments