/ libpkg / binfmt_macho.c
binfmt_macho.c
  1  /*-
  2   * Copyright (c) 2024 Keve Müller <kevemueller@users.github.com>
  3   *
  4   * Redistribution and use in source and binary forms, with or without
  5   * modification, are permitted provided that the following conditions
  6   * are met:
  7   * 1. Redistributions of source code must retain the above copyright
  8   *    notice, this list of conditions and the following disclaimer
  9   *    in this position and unchanged.
 10   * 2. Redistributions in binary form must reproduce the above copyright
 11   *    notice, this list of conditions and the following disclaimer in the
 12   *    documentation and/or other materials provided with the distribution.
 13   *
 14   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 15   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 16   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 17   * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 18   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 19   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 20   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 21   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 22   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 23   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 24   */
 25  
 26  #ifdef HAVE_CONFIG_H
 27  #include "pkg_config.h"
 28  #endif
 29  
 30  #if __has_include(<sys/endian.h>)
 31  #include <sys/endian.h>
 32  #elif __has_include(<endian.h>)
 33  #include <endian.h>
 34  #elif __has_include(<machine/endian.h>)
 35  #include <machine/endian.h>
 36  #endif
 37  #include <errno.h>
 38  #include <fcntl.h>
 39  #include <stdio.h>
 40  #include <stdlib.h>
 41  #include <string.h>
 42  #include <unistd.h>
 43  
 44  #include <bsd_compat.h>
 45  #include <xmalloc.h>
 46  #include "private/binfmt_macho.h"
 47  
 48  /**
 49   * Minimal Mach-O binary file parser for both FAT as well as plain binaries with
 50   * sufficient functionality to handle architecture, OS, file type, library
 51   * dependencies.
 52   * As well as utility functions to convert data into different formats.
 53   */
 54  
 55  /**** Readers ****/
 56  
 57  static ssize_t
 58  read_fully(const int fd, const size_t len, void *dest)
 59  {
 60  	unsigned char *p = dest;
 61  	size_t n = len;
 62  	ssize_t x;
 63  	while (n > 0) {
 64  		if ((x = read(fd, p, n)) < 0) {
 65  			if ( EAGAIN == errno) {
 66  				continue;
 67  			}
 68  			return x;
 69  		}
 70  		if ( 0 == x) {
 71  			return -1;
 72  		}
 73  		n -= x;
 74  		p += x;
 75  	}
 76  	return len;
 77  }
 78  
 79  ssize_t
 80  read_u32(const int fd, const bool swap, uint32_t *dest)
 81  {
 82  	unsigned char buf[4];
 83  	ssize_t x;
 84  	if ((x = read_fully(fd, sizeof(buf), buf)) < 0) {
 85  		return x;
 86  	}
 87  	if (swap) {
 88  		*dest = le32dec(buf);
 89  	} else {
 90  		*dest = be32dec(buf);
 91  	}
 92  	return x;
 93  }
 94  
 95  static ssize_t
 96  read_u64(const int fd, const bool swap, uint64_t *dest)
 97  {
 98  	unsigned char buf[8];
 99  	ssize_t x;
100  	if ((x = read_fully(fd, sizeof(buf), buf)) < 0) {
101  		return x;
102  	}
103  	if (swap) {
104  		*dest = le64dec(buf);
105  	} else {
106  		*dest = be64dec(buf);
107  	}
108  	return x;
109  }
110  
111  static ssize_t
112  read_cpu_type(const int fd, const bool swap, cpu_type_subtype_t *dest)
113  {
114  	ssize_t n = 0, x;
115  	uint32_t cputype;
116  	uint32_t cpusubtype;
117  
118  	READ(u32, cputype);
119  	READ(u32, cpusubtype);
120  	dest->type = cputype & ~CPU_ARCH_MASK;
121  	dest->type_is64 = (cputype & CPU_ARCH_MASK) == CPU_ARCH_ABI64;
122  	dest->type_is64_32 = (cputype & CPU_ARCH_MASK) == CPU_ARCH_ABI64_32;
123  	dest->subtype_islib64 = (cpusubtype & CPU_SUBTYPE_MASK) ==
124  	    CPU_SUBTYPE_LIB64;
125  	switch (dest->type) {
126  	case CPU_TYPE_ARM:
127  		dest->subtype_arm = cpusubtype & ~CPU_SUBTYPE_MASK;
128  		break;
129  	case CPU_TYPE_X86:
130  		dest->subtype_x86 = cpusubtype & ~CPU_SUBTYPE_MASK;
131  		break;
132  	case CPU_TYPE_POWERPC:
133  		dest->subtype_ppc = cpusubtype & ~CPU_SUBTYPE_MASK;
134  		break;
135  	default:
136  		errno = EINVAL;
137  		return -1;
138  	}
139  	return n;
140  }
141  
142  static ssize_t
143  read_fat_arch(const int fd, const uint32_t magic, fat_arch_t *dest)
144  {
145  	ssize_t n = 0, x;
146  	const bool swap = magic == FAT_CIGAM || magic == FAT_CIGAM_64;
147  
148  	READ(cpu_type, dest->cpu);
149  	uint32_t align;
150  	uint32_t reserved;
151  
152  	switch (magic) {
153  	case FAT_MAGIC:
154  	case FAT_CIGAM:;
155  		uint32_t offset32;
156  		uint32_t size32;
157  		READ(u32, offset32);
158  		READ(u32, size32);
159  		READ(u32, align); // bits
160  
161  		dest->offset = offset32;
162  		dest->size = size32;
163  		dest->align = align;
164  		break;
165  	case FAT_MAGIC_64:
166  	case FAT_CIGAM_64:
167  		READ(u64, dest->offset);
168  		READ(u64, dest->size);
169  		READ(u32, align);
170  		READ(u32, reserved);
171  		dest->align = align;
172  		break;
173  	default:
174  		errno = EINVAL;
175  		return -1;
176  	}
177  	return n;
178  }
179  
180  static ssize_t
181  read_version(const int fd, const bool swap, macho_version_t *dest)
182  {
183  	ssize_t n = 0, x;
184  
185  	uint32_t version;
186  	READ(u32, version);
187  	dest->major = (version >> 16) & 0xffff;
188  	dest->minor = (version >> 8) & 0xff;
189  	dest->patch = version & 0xff;
190  	return n;
191  }
192  
193  ssize_t
194  read_min_version(const int fd, const bool swap, const uint32_t loadcmd,
195      build_version_t **dest)
196  {
197  	ssize_t n = 0, x;
198  
199  	*dest = xmalloc(sizeof(build_version_t));
200  	(*dest)->ntools = 0;
201  	switch (loadcmd) {
202  	case LC_VERSION_MIN_IPHONEOS:
203  		(*dest)->platform = PLATFORM_IOS;
204  		break;
205  	case LC_VERSION_MIN_MACOSX:
206  		(*dest)->platform = PLATFORM_MACOS;
207  		break;
208  	case LC_VERSION_MIN_TVOS:
209  		(*dest)->platform = PLATFORM_TVOS;
210  		break;
211  	case LC_VERSION_MIN_WATCHOS:
212  		(*dest)->platform = PLATFORM_WATCHOS;
213  		break;
214  	default:
215  		return -1;
216  	}
217  	READ(version, (*dest)->minos);
218  	READ(version, (*dest)->sdk);
219  	return n;
220  }
221  
222  ssize_t
223  read_path(const int fd, const bool swap, const uint32_t loadcmdsize,
224      char **dest)
225  {
226  	ssize_t n = 0, x;
227  
228  	uint32_t name_ofs;
229  	READ(u32, name_ofs);
230  	if (-1 == (x = lseek(fd, name_ofs - 12, SEEK_CUR))) {
231  		return x;
232  	}
233  	n += name_ofs - 12;
234  	*dest = xmalloc(loadcmdsize - name_ofs + 1);
235  	if ((x = read_fully(fd, loadcmdsize - name_ofs, *dest)) < 0) {
236  		free(*dest);
237  		*dest = 0;
238  		return x;
239  	}
240  	n += x;
241  	(*dest)[loadcmdsize - name_ofs] = '\0';
242  	return n;
243  }
244  
245  ssize_t
246  read_dylib(const int fd, const bool swap, const uint32_t loadcmdsize,
247      dylib_t **dest)
248  {
249  	ssize_t n = 0, x;
250  
251  	uint32_t name_ofs;
252  	uint32_t timestamp;
253  	macho_version_t current_version;
254  	macho_version_t compatibility_version;
255  
256  	READ(u32, name_ofs);
257  	READ(u32, timestamp);
258  	READ(version, current_version);
259  	READ(version, compatibility_version);
260  
261  	if (-1 == (x = lseek(fd, name_ofs - 24, SEEK_CUR))) {
262  		return x;
263  	}
264  	n += name_ofs - 24;
265  
266  	*dest = xmalloc(sizeof(dylib_t) + loadcmdsize - name_ofs + 1);
267  	(*dest)->timestamp = timestamp;
268  	(*dest)->current_version = current_version;
269  	(*dest)->compatibility_version = compatibility_version;
270  	if ((x = read_fully(fd, loadcmdsize - name_ofs, (*dest)->path)) < 0) {
271  		free(*dest);
272  		*dest = 0;
273  		return x;
274  	}
275  	n += x;
276  	(*dest)->path[loadcmdsize - name_ofs] = '\0';
277  	return n;
278  }
279  
280  ssize_t
281  read_build_version(const int fd, const bool swap, build_version_t **dest)
282  {
283  	ssize_t n = 0, x;
284  
285  	uint32_t platform;
286  	macho_version_t minos;
287  	macho_version_t sdk;
288  	uint32_t ntools;
289  
290  	READ(u32, platform);
291  	READ(version, minos);
292  	READ(version, sdk);
293  	READ(u32, ntools);
294  
295  	*dest = xmalloc(
296  	    sizeof(build_version_t) + ntools * sizeof(tool_version_t));
297  	(*dest)->platform = platform;
298  	(*dest)->minos = minos;
299  	(*dest)->sdk = sdk;
300  	(*dest)->ntools = ntools;
301  	tool_version_t *p = (*dest)->tools;
302  
303  	for (; ntools-- > 0; p++) {
304  		uint32_t tool;
305  		READ(u32, tool);
306  		p->tool = tool;
307  		READ(version, p->version);
308  	}
309  	return n;
310  }
311  
312  ssize_t
313  read_macho_header(const int fd, macho_header_t *dest)
314  {
315  	ssize_t n = 0, x;
316  	uint32_t reserved;
317  
318  	if ((x = read_u32(fd, false, &dest->magic)) < 0) {
319  		return x;
320  	}
321  	n += x;
322  
323  	const bool swap = dest->swap = dest->magic == MH_CIGAM ||
324  	    dest->magic == MH_CIGAM_64;
325  
326  	READ(cpu_type, dest->cpu);
327  	READ(u32, dest->filetype);
328  	READ(u32, dest->ncmds);
329  	READ(u32, dest->sizeofcmds);
330  	READ(u32, dest->flags);
331  	switch (dest->magic) {
332  	case MH_MAGIC_64:
333  	case MH_CIGAM_64:
334  		READ(u32, reserved);
335  		break;
336  	default:
337  		break;
338  	}
339  	return n;
340  }
341  
342  ssize_t
343  read_macho_file(const int fd, macho_file_t **dest)
344  {
345  	ssize_t n = 0, x;
346  
347  	uint32_t magic;
348  	if ((x = read_u32(fd, false, &magic)) < 0) {
349  		return x;
350  	}
351  	n += x;
352  
353  	const bool swap = magic == FAT_CIGAM || magic == FAT_CIGAM_64 ||
354  	    magic == MH_CIGAM || magic == MH_CIGAM_64;
355  
356  	uint32_t nfat_arch;
357  	fat_arch_t *p;
358  	switch (magic) {
359  	case FAT_MAGIC:
360  	case FAT_MAGIC_64:
361  	case FAT_CIGAM:
362  	case FAT_CIGAM_64:
363  		READ(u32, nfat_arch);
364  		*dest = xmalloc(
365  		    sizeof(macho_file_t) + nfat_arch * sizeof(fat_arch_t));
366  		(*dest)->magic = magic;
367  		(*dest)->narch = nfat_arch;
368  		p = (*dest)->arch;
369  
370  		while (nfat_arch-- > 0) {
371  			if ((x = read_fat_arch(fd, magic, p)) < 0) {
372  				free(*dest);
373  				*dest = 0;
374  				return x;
375  			}
376  			n += x;
377  			p++;
378  		}
379  		break;
380  
381  	case MH_MAGIC:
382  	case MH_MAGIC_64:
383  	case MH_CIGAM:
384  	case MH_CIGAM_64:
385  		nfat_arch = 1;
386  		*dest = xmalloc(
387  		    sizeof(macho_file_t) + nfat_arch * sizeof(fat_arch_t));
388  		(*dest)->magic = magic;
389  		(*dest)->narch = nfat_arch;
390  		p = (*dest)->arch;
391  		READ(cpu_type, p->cpu);
392  		off_t xo;
393  		if (-1 == (xo = lseek(fd, 0, SEEK_END))) {
394  			free(*dest);
395  			*dest = 0;
396  			return xo;
397  		}
398  		p->offset = 0;
399  		p->size = xo;
400  		p->align = 0; // number of trailing zero bits in size;
401  		n = xo;
402  		break;
403  	default:
404  		errno = EINVAL;
405  		return -1;
406  	}
407  	return n;
408  }
409  
410  /**** OS -> Kernel conversion ****/
411  
412  static macho_version_t macos_to_darwin[][2] = {
413  	// macOS Sequoia
414  	{ { 15, 2, 0 }, { 24, 2, 0 } },
415  	{ { 15, 1, 0 }, { 24, 1, 0 } },
416  	{ { 15, 0, 0 }, { 24, 0, 0 } },
417  	// macOS Sonoma
418  	{ { 14, 6, 0 }, { 23, 6, 0 } },
419  	{ { 14, 5, 0 }, { 23, 4, 0 } },
420  	{ { 14, 4, 0 }, { 23, 5, 0 } },
421  	{ { 14, 3, 0 }, { 23, 3, 0 } },
422  	{ { 14, 2, 0 }, { 23, 2, 0 } },
423  	{ { 14, 1, 0 }, { 23, 1, 0 } },
424  	{ { 14, 0, 0 }, { 23, 0, 0 } },
425  	// macOS Ventura
426  	{ { 13, 5, 0 }, { 22, 6, 0 } },
427  	{ { 13, 4, 0 }, { 22, 5, 0 } },
428  	{ { 13, 3, 0 }, { 22, 4, 0 } },
429  	{ { 13, 2, 0 }, { 22, 3, 0 } },
430  	{ { 13, 1, 0 }, { 22, 2, 0 } },
431  	{ { 13, 0, 0 }, { 22, 1, 0 } },
432  	// macOS Monterey
433  	{ { 12, 5, 0 }, { 21, 6, 0 } },
434  	{ { 12, 4, 0 }, { 21, 5, 0 } },
435  	{ { 12, 3, 0 }, { 21, 4, 0 } },
436  	{ { 12, 2, 0 }, { 21, 3, 0 } },
437  	{ { 12, 1, 0 }, { 21, 2, 0 } },
438  	{ { 12, 0, 1 }, { 21, 1, 0 } },
439  	{ { 12, 0, 0 }, { 21, 0, 1 } },
440  	// macOS Big Sur
441  	{ { 11, 5, 0 }, { 20, 6, 0 } },
442  	{ { 11, 4, 0 }, { 20, 5, 0 } },
443  	{ { 11, 3, 0 }, { 20, 4, 0 } },
444  	{ { 11, 2, 0 }, { 20, 3, 0 } },
445  	{ { 11, 1, 0 }, { 20, 2, 0 } },
446  	{ { 11, 0, 0 }, { 20, 1, 0 } },
447  	// macOS Catalina
448  	{ { 10, 15, 6 }, { 19, 6, 0 } },
449  	{ { 10, 15, 5 }, { 19, 5, 0 } },
450  	{ { 10, 15, 4 }, { 19, 4, 0 } },
451  	{ { 10, 15, 3 }, { 19, 3, 0 } },
452  	{ { 10, 15, 2 }, { 19, 2, 0 } },
453  	{ { 10, 15, 0 }, { 19, 0, 0 } },
454  	// macOS Mojave
455  	{ { 10, 14, 6 }, { 18, 7, 0 } },
456  	{ { 10, 14, 5 }, { 18, 6, 0 } },
457  	{ { 10, 14, 4 }, { 18, 5, 0 } },
458  	{ { 10, 14, 1 }, { 18, 2, 0 } },
459  	{ { 10, 14, 0 }, { 18, 0, 0 } },
460  	// macOS High Sierra
461  	{ { 10, 13, 6 }, { 17, 7, 0 } },
462  	{ { 10, 13, 5 }, { 17, 6, 0 } },
463  	{ { 10, 13, 4 }, { 17, 5, 0 } },
464  	{ { 10, 13, 3 }, { 17, 4, 0 } },
465  	{ { 10, 13, 2 }, { 17, 3, 0 } },
466  	{ { 10, 13, 1 }, { 17, 2, 0 } },
467  	{ { 10, 13, 0 }, { 17, 0, 0 } },
468  	// macOS Sierra
469  	{ { 10, 12, 6 }, { 16, 7, 0 } },
470  	{ { 10, 12, 5 }, { 16, 6, 0 } },
471  	{ { 10, 12, 4 }, { 16, 5, 0 } },
472  	{ { 10, 12, 3 }, { 16, 4, 0 } },
473  	{ { 10, 12, 2 }, { 16, 3, 0 } },
474  	{ { 10, 12, 1 }, { 16, 1, 0 } },
475  	{ { 10, 12, 0 }, { 16, 0, 0 } },
476  	// OS X El Capitan
477  	{ { 10, 11, 6 }, { 15, 6, 0 } },
478  	{ { 10, 11, 5 }, { 15, 5, 0 } },
479  	{ { 10, 11, 4 }, { 15, 4, 0 } },
480  	{ { 10, 11, 3 }, { 15, 3, 0 } },
481  	{ { 10, 11, 2 }, { 15, 2, 0 } },
482  	{ { 10, 11, 0 }, { 15, 0, 0 } },
483  	// OS X Yosemite
484  	{ { 10, 10, 5 }, { 14, 5, 0 } },
485  	{ { 10, 10, 4 }, { 14, 4, 0 } },
486  	{ { 10, 10, 3 }, { 14, 3, 0 } },
487  	{ { 10, 10, 2 }, { 14, 1, 0 } },
488  	{ { 10, 10, 0 }, { 14, 0, 0 } },
489  	// OS X Mavericks
490  	{ { 10, 9, 5 }, { 13, 4, 0 } },
491  	{ { 10, 9, 4 }, { 13, 3, 0 } },
492  	{ { 10, 9, 3 }, { 13, 2, 0 } },
493  	{ { 10, 9, 2 }, { 13, 1, 0 } },
494  	{ { 10, 9, 0 }, { 13, 0, 0 } },
495  	// OS X Mountain Lion
496  	{ { 10, 8, 5 }, { 12, 5, 0 } }, // Build 12F45 switched to 12.6
497  	{ { 10, 8, 4 }, { 12, 4, 0 } },
498  	{ { 10, 8, 3 }, { 12, 3, 0 } },
499  	{ { 10, 8, 2 }, { 12, 2, 0 } },
500  	{ { 10, 8, 1 }, { 12, 1, 0 } },
501  	{ { 10, 8, 0 }, { 12, 0, 0 } },
502  	// OS X Lion
503  	{ { 10, 7, 5 }, { 11, 4, 2 } },
504  	{ { 10, 7, 4 }, { 11, 4, 0 } },
505  	{ { 10, 7, 3 }, { 11, 3, 0 } },
506  	{ { 10, 7, 2 }, { 11, 2, 0 } },
507  	{ { 10, 7, 1 }, { 11, 1, 0 } },
508  	{ { 10, 7, 0 }, { 11, 0, 0 } },
509  	// Mac OS X Snow Leopard
510  	{ { 10, 6, 8 }, { 10, 8, 0 } },
511  	{ { 10, 6, 7 }, { 10, 7, 0 } },
512  	{ { 10, 6, 6 }, { 10, 6, 0 } },
513  	{ { 10, 6, 5 }, { 10, 5, 0 } },
514  	{ { 10, 6, 4 }, { 10, 4, 0 } },
515  	{ { 10, 6, 3 }, { 10, 3, 0 } },
516  	{ { 10, 6, 2 }, { 10, 2, 0 } },
517  	{ { 10, 6, 1 }, { 10, 1, 0 } },
518  	{ { 10, 6, 0 }, { 10, 0, 0 } },
519  	// Mac OS X Leopard
520  	{ { 10, 5, 8 }, { 9, 8, 0 } },
521  	{ { 10, 5, 7 }, { 9, 7, 0 } },
522  	{ { 10, 5, 6 }, { 9, 6, 0 } },
523  	{ { 10, 5, 5 }, { 9, 5, 0 } },
524  	{ { 10, 5, 4 }, { 9, 4, 0 } },
525  	{ { 10, 5, 3 }, { 9, 3, 0 } },
526  	{ { 10, 5, 2 }, { 9, 2, 0 } },
527  	{ { 10, 5, 1 }, { 9, 1, 0 } }, // Build 9B2117 switched to 9.1.1
528  	{ { 10, 5, 0 }, { 9, 0, 0 } },
529  	// Mac OS X Tiger
530  	{ { 10, 4, 11 }, { 8, 11, 0 } },
531  	{ { 10, 4, 10 }, { 8, 10, 0 } },
532  	{ { 10, 4, 9 }, { 8, 9, 0 } },
533  	{ { 10, 4, 8 }, { 8, 8, 0 } },
534  	{ { 10, 4, 7 }, { 8, 7, 0 } },
535  	{ { 10, 4, 6 }, { 8, 6, 0 } },
536  	{ { 10, 4, 5 }, { 8, 5, 0 } },
537  	{ { 10, 4, 4 }, { 8, 4, 0 } },
538  	{ { 10, 4, 3 }, { 8, 3, 0 } },
539  	{ { 10, 4, 2 }, { 8, 2, 0 } },
540  	{ { 10, 4, 1 }, { 8, 1, 0 } },
541  	{ { 10, 4, 0 }, { 8, 0, 0 } },
542  	// Mac OS X Panther
543  	{ { 10, 3, 9 }, { 7, 9, 0 } },
544  	{ { 10, 3, 8 }, { 7, 8, 0 } },
545  	{ { 10, 3, 7 }, { 7, 7, 0 } },
546  	{ { 10, 3, 6 }, { 7, 6, 0 } },
547  	{ { 10, 3, 5 }, { 7, 5, 0 } },
548  	{ { 10, 3, 4 }, { 7, 4, 0 } },
549  	{ { 10, 3, 3 }, { 7, 3, 0 } },
550  	{ { 10, 3, 2 }, { 7, 2, 0 } },
551  	{ { 10, 3, 1 }, { 7, 1, 0 } },
552  	{ { 10, 3, 0 }, { 7, 0, 0 } },
553  	// Mac OS X Jaguar
554  	{ { 10, 2, 8 }, { 6, 8, 0 } },
555  	{ { 10, 2, 7 }, { 6, 7, 0 } },
556  	{ { 10, 2, 6 }, { 6, 6, 0 } },
557  	{ { 10, 2, 5 }, { 6, 5, 0 } },
558  	{ { 10, 2, 4 }, { 6, 4, 0 } },
559  	{ { 10, 2, 3 }, { 6, 3, 0 } },
560  	{ { 10, 2, 2 }, { 6, 2, 0 } },
561  	{ { 10, 2, 1 }, { 6, 1, 0 } },
562  	{ { 10, 2, 0 }, { 6, 0, 0 } },
563  	// Mac OS X 10.1 Puma
564  	{ { 10, 1, 5 }, { 5, 5, 0 } },
565  	{ { 10, 1, 4 }, { 5, 4, 0 } },
566  	{ { 10, 1, 3 }, { 5, 3, 0 } },
567  	{ { 10, 1, 2 }, { 5, 2, 0 } },
568  	{ { 10, 1, 1 }, { 5, 1, 0 } },
569  	{ { 10, 1, 0 }, { 1, 4, 1 } },
570  	// Mac OS X 10.0 Cheetah
571  	{ { 10, 0, 1 }, { 1, 3, 1 } },
572  	{ { 10, 0, 0 }, { 1, 3, 0 } },
573  	// Mac OS X Public Beta
574  	// {{x,y,z}}, {1,2,1}},
575  	// Mac OS X Server 1.0
576  	{ { 1, 0, 2 }, { 0, 3, 0 } },
577  	{ { 1, 0, 1 }, { 0, 2, 0 } },
578  	{ { 1, 0, 0 }, { 0, 1, 0 } },
579  	// EOA
580  	{ { 0, 0, 0 }, { 0, 0, 0 } },
581  };
582  
583  static macho_version_t ios_to_darwin[][2] = {
584  	// iOS 18, iPadOS 18, tvOS 18
585  	{ { 18, 0, 0 }, { 24, 0, 0 } },
586  	// iOS 17, iPadOS 17, tvOS 17
587  	{ { 17, 5, 0 }, { 23, 5, 0 } },
588  	{ { 17, 4, 0 }, { 23, 4, 0 } },
589  	{ { 17, 3, 0 }, { 23, 3, 0 } },
590  	{ { 17, 2, 0 }, { 23, 2, 0 } },
591  	{ { 17, 1, 0 }, { 23, 1, 0 } },
592  	{ { 17, 0, 0 }, { 23, 0, 0 } },
593  	// iOS 16, iPadOS 16, tvOS 16
594  	{ { 16, 6, 0 }, { 22, 6, 0 } },
595  	{ { 16, 5, 0 }, { 22, 5, 0 } },
596  	{ { 16, 4, 0 }, { 22, 4, 0 } },
597  	{ { 16, 3, 0 }, { 22, 3, 0 } },
598  	{ { 16, 2, 0 }, { 22, 2, 0 } },
599  	{ { 16, 1, 0 }, { 22, 1, 0 } },
600  	{ { 16, 0, 0 }, { 22, 0, 0 } },
601  	// iOS 15, iPadOS 15, tvOS 15
602  	{ { 15, 6, 0 }, { 21, 6, 0 } },
603  	{ { 15, 5, 0 }, { 21, 5, 0 } },
604  	{ { 15, 4, 0 }, { 21, 4, 0 } },
605  	{ { 15, 3, 0 }, { 21, 3, 0 } },
606  	{ { 15, 2, 0 }, { 21, 2, 0 } },
607  	{ { 15, 0, 0 }, { 21, 1, 0 } },
608  	// iOS 15.0 beta 1 -> 21.0.0
609  	// iOS 14, iPadOS 14, tvOS 14
610  	{ { 14, 7, 0 }, { 20, 6, 0 } },
611  	{ { 14, 6, 0 }, { 20, 5, 0 } },
612  	{ { 14, 5, 0 }, { 20, 4, 0 } },
613  	{ { 14, 4, 0 }, { 20, 3, 0 } },
614  	{ { 14, 3, 0 }, { 20, 2, 0 } },
615  	{ { 14, 0, 0 }, { 20, 0, 0 } },
616  	// iOS 13
617  	{ { 13, 6, 0 }, { 19, 6, 0 } },
618  	{ { 13, 5, 0 }, { 19, 5, 0 } },
619  	{ { 13, 3, 1 }, { 19, 3, 0 } },
620  	{ { 13, 3, 0 }, { 19, 2, 0 } },
621  	// iOS 12
622  	{ { 12, 1, 0 }, { 18, 2, 0 } },
623  	// iOS 11
624  	{ { 11, 4, 1 }, { 17, 7, 0 } },
625  	// iOS 10
626  	{ { 10, 3, 3 }, { 16, 6, 0 } },
627  	{ { 10, 3, 0 }, { 16, 3, 0 } },
628  	{ { 10, 0, 1 }, { 16, 0, 0 } },
629  	// iOS 9
630  	{ { 9, 3, 3 }, { 15, 6, 0 } },
631  	{ { 9, 0, 0 }, { 15, 0, 0 } },
632  	// iOS 7, iOS 8
633  	{ { 7, 0, 0 }, { 14, 0, 0 } },
634  	// iOS 6
635  	{ { 6, 0, 0 }, { 13, 0, 0 } },
636  	// iOS 4.3
637  	{ { 4, 3, 0 }, { 11, 0, 0 } },
638  	// iPhone OS 3
639  	{ { 3, 0, 0 }, { 10, 0, 0 } },
640  	// iPhone OS 1
641  	{ { 1, 0, 0 }, { 9, 0, 0 } },
642  	// EOA
643  	{ { 0, 0, 0 }, { 0, 0, 0 } },
644  };
645  
646  int
647  map_platform_to_darwin(macho_version_t *darwin,
648      const enum MachoPlatform platform, const macho_version_t version)
649  {
650  	macho_version_t *p;
651  	switch (platform) {
652  	case PLATFORM_MACOS:
653  		p = macos_to_darwin[0];
654  		break;
655  
656  	case PLATFORM_IOS:
657  	case PLATFORM_IOSSIMULATOR:
658  	case PLATFORM_TVOS:
659  	case PLATFORM_TVOSSIMULATOR:
660  		p = ios_to_darwin[0];
661  		break;
662  
663  	case PLATFORM_WATCHOS:
664  	case PLATFORM_WATCHOSSIMULATOR:
665  		darwin->major = version.major + 13;
666  		darwin->minor = version.minor;
667  		darwin->patch = 0;
668  		return 0;
669  
670  	default:
671  		return -1;
672  	}
673  	while (p->major > version.major || p->minor > version.minor ||
674  	    p->patch > version.patch) {
675  		p += 2;
676  	}
677  	p++;
678  	if (0 == p->major && 0 == p->minor && 0 == p->patch) {
679  		return -1;
680  	}
681  	*darwin = *p;
682  	return 0;
683  }