Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Add shared library parsing to Mach-O.
Keve committed 1 year ago
commit 6b088baed291c158f752c2de5ebcb2cc39eb135d
parent b646211
8 files changed +227 -431
modified libpkg/binfmt_macho.c
@@ -212,7 +212,7 @@ read_min_version(const int fd, const bool swap, const uint32_t loadcmd,
	return n;
}

-
static ssize_t
+
ssize_t
read_path(const int fd, const bool swap, const uint32_t loadcmdsize,
    char **dest)
{
@@ -235,7 +235,7 @@ read_path(const int fd, const bool swap, const uint32_t loadcmdsize,
	return n;
}

-
static ssize_t
+
ssize_t
read_dylib(const int fd, const bool swap, const uint32_t loadcmdsize,
    dylib_t **dest)
{
modified libpkg/pkg_abi.c
@@ -227,10 +227,20 @@ pkg_analyse_files(struct pkgdb *db __unused, struct pkg *pkg, const char *stage)
	const char *lib;
	bool failures = false;

-
	int (*pkg_analyse_init)(const char *stage) = pkg_analyse_init_elf;
+
	int (*pkg_analyse_init)(const char *stage);
	int (*pkg_analyse)(const bool developer_mode, struct pkg *pkg,
-
	    const char *fpath) = pkg_analyse_elf;
-
	int (*pkg_analyse_close)() = pkg_analyse_close_elf;
+
	    const char *fpath);
+
	int (*pkg_analyse_close)();
+

+
	if (0 == strncmp(pkg->abi, "Darwin", 6)) {
+
		pkg_analyse_init=pkg_analyse_init_macho;
+
		pkg_analyse=pkg_analyse_macho;
+
		pkg_analyse_close=pkg_analyse_close_macho;
+
	} else {
+
		pkg_analyse_init=pkg_analyse_init_elf;
+
		pkg_analyse=pkg_analyse_elf;
+
		pkg_analyse_close=pkg_analyse_close_elf;
+
	}

	if (tll_length(pkg->shlibs_required) != 0) {
		tll_free_and_free(pkg->shlibs_required, free);
modified libpkg/pkg_abi_macho.c
@@ -124,8 +124,8 @@ static const fat_arch_t *
match_entry(macho_file_t *mf, struct os_info *oi)
{
	const fat_arch_t *p = mf->arch;
-
	// we can change the content of oi->abi freely
-
	char *abisep = oi->abi;
+
	char *abihint = xstrdup(oi->abi);
+
	char *abisep = abihint;
	/*const char *osname = */strsep(&abisep, ":");
	/*const char *version_str = */ strsep(&abisep, ":");
	const char *archname = strsep(&abisep, ":");
@@ -145,7 +145,7 @@ match_entry(macho_file_t *mf, struct os_info *oi)
						CPU_SUBTYPE_ARM_ALL ||
					    p->cpu.subtype_arm ==
						cpu_hint.subtype_arm) {
-
						return p;
+
							goto matched;
					}
					break;
				case CPU_TYPE_POWERPC:
@@ -155,7 +155,7 @@ match_entry(macho_file_t *mf, struct os_info *oi)
						CPU_SUBTYPE_POWERPC_ALL ||
					    p->cpu.subtype_ppc ==
						cpu_hint.subtype_ppc) {
-
						return p;
+
							goto matched;
					}
					break;
				case CPU_TYPE_X86:
@@ -165,7 +165,7 @@ match_entry(macho_file_t *mf, struct os_info *oi)
						CPU_SUBTYPE_X86_ALL ||
					    p->cpu.subtype_x86 ==
						cpu_hint.subtype_x86) {
-
						return p;
+
							goto matched;
					}
					break;
				default:
@@ -176,13 +176,15 @@ match_entry(macho_file_t *mf, struct os_info *oi)
		    archname, cputype_to_freebsd_machine_arch(p->cpu));
			p++;
		}
-
		pkg_emit_notice("Scanned %d entr%s, found none matching selector %s",
+
		pkg_emit_notice("Scanned %"PRIu32" entr%s, found none matching selector %s",
			mf->narch, mf->narch > 1 ? "ies" : "y", archname);
-
		return 0;	
+
		p=0;
	} else if (mf->narch > 1 ) {
-
		pkg_debug(1,"Found %d entries in universal binary, picking first",
+
		pkg_debug(1,"Found %"PRIu32" entries in universal binary, picking first",
			mf->narch);
	}
+
matched:
+
	free(abihint);
	return p;
}

@@ -284,19 +286,19 @@ pkg_get_myarch_macho(int fd, struct os_info *oi)
		oi->name = xstrdup("Darwin");
		free(oi->version);
		if (darwin.patch) {
-
			xasprintf(&oi->version, "%d.%d.%d", darwin.major,
+
			xasprintf(&oi->version, "%"PRIuFAST16".%"PRIuFAST16".%"PRIuFAST16"", darwin.major,
			    darwin.minor, darwin.patch);
		} else {
-
			xasprintf(&oi->version, "%d.%d", darwin.major,
+
			xasprintf(&oi->version, "%"PRIuFAST16".%"PRIuFAST16"", darwin.major,
			    darwin.minor);
		}
		free(oi->version_major);
-
		xasprintf(&oi->version_major, "%d", darwin.major);
+
		xasprintf(&oi->version_major, "%"PRIuFAST16, darwin.major);
		free(oi->version_minor);
-
		xasprintf(&oi->version_minor, "%d", darwin.minor);
+
		xasprintf(&oi->version_minor, "%"PRIuFAST16, darwin.minor);
		free(oi->arch);
		oi->arch = xstrdup(cputype_to_freebsd_machine_arch(mh.cpu));
-
		snprintf(oi->abi, sizeof(oi->abi), "Darwin:%d:%s", darwin.major, cputype_to_freebsd_machine_arch(mh.cpu)); 
+
		snprintf(oi->abi, sizeof(oi->abi), "Darwin:%"PRIuFAST16":%s", darwin.major, cputype_to_freebsd_machine_arch(mh.cpu)); 
		// not populating oi->altabi, derived later by caller.
		snprintf(oi->str_osversion, sizeof(oi->str_osversion), "%d",
		    oi->osversion);
@@ -313,6 +315,147 @@ cleanup:
	return ret;
}

+
static const char * const system_dylib_prefixes[] = {
+
	"/System/",
+
	"/usr/lib/",
+
	"/lib/",
+
};
+

+
static bool
+
system_dylib(const char *libname)
+
{
+
	const char * const *p = system_dylib_prefixes;
+
	const char * const *p_end = p + NELEM(system_dylib_prefixes);
+
	while (p < p_end) {
+
		if (strncmp(libname, *p, strlen(*p)) == 0) {
+
			return true;
+
		}
+
		p++;
+
	}
+
	return false;
+
}
+

+
static int
+
analyse_macho(int fd, struct pkg *pkg, const bool baselibs)
+
{
+
	ssize_t x;
+
	pkg_error_t ret = EPKG_END;
+

+
	macho_file_t *mf = 0;
+

+
	if ((x = read_macho_file(fd, &mf)) < 0) {
+
		goto cleanup;
+
	}
+

+
	const fat_arch_t *p = match_entry(mf, ctx.oi);
+

+
	if (!p) {
+
		goto cleanup;
+
	}
+

+
	if (-1 == (x = lseek(fd, p->offset, SEEK_SET))) {
+
		goto cleanup;
+
	}
+
	size_t n = 0;
+
	macho_header_t mh;
+
	if ((x = read_macho_header(fd, &mh)) < 0) {
+
		goto cleanup;
+
	}
+
	const bool swap = mh.swap;
+
	n = 0;
+
	for (uint32_t ui = mh.ncmds; ui-- > 0;) {
+
		size_t n0 = n;
+
		uint32_t loadcmdtype;
+
		uint32_t loadcmdsize;
+
		READ(u32, loadcmdtype);
+
		READ(u32, loadcmdsize);
+
		enum MachOLoadCommand loadcmd = loadcmdtype & ~LC_REQ_DYLD;
+
		switch (loadcmd) {
+
		case LC_RPATH:
+
		case LC_LOAD_DYLINKER:;
+
			char *dylinker = 0;
+
			if ((x = read_path(fd, swap, loadcmdsize,
+
					&dylinker)) < 0) {
+
				goto cleanup;
+
			}
+
			n += x;
+
			pkg_debug(3, "load_dylinker %d: %s\n", loadcmd, dylinker);
+
			free(dylinker);
+
			break;
+
		case LC_ID_DYLIB:   // provides
+
		case LC_LOAD_DYLIB: // requires...
+
		case LC_LOAD_WEAK_DYLIB:
+
		case LC_REEXPORT_DYLIB:
+
		case LC_LAZY_LOAD_DYLIB:
+
		case LC_LOAD_UPWARD_DYLIB:;
+
			dylib_t *dylib = 0;
+
			if ((x = read_dylib(fd, swap, loadcmdsize,
+
					&dylib)) < 0) {
+
				goto cleanup;
+
			}
+
			n += x;
+
			if (!baselibs && system_dylib(dylib->path)) {
+
				pkg_debug(3, 
+
					"Skipping System dynamic library path: %s ts %"PRIu32" current(%"PRIuFAST16", %"PRIuFAST16", %"PRIuFAST16") compat(%"PRIuFAST16", %"PRIuFAST16", %"PRIuFAST16")\n",
+
					dylib->path, dylib->timestamp,
+
					dylib->current_version.major,
+
					dylib->current_version.minor,
+
					dylib->current_version.patch,
+
					dylib->compatibility_version.major,
+
					dylib->compatibility_version.minor,
+
					dylib->compatibility_version.patch);
+
			} else {
+
				const char * basename = strrchr(dylib->path, '/');
+
				if (basename) {
+
					pkg_debug(3, 
+
						"Adding dynamic library path: %s ts %"PRIu32" current(%"PRIuFAST16", %"PRIuFAST16", %"PRIuFAST16") compat(%"PRIuFAST16", %"PRIuFAST16", %"PRIuFAST16")\n",
+
						dylib->path, dylib->timestamp,
+
						dylib->current_version.major,
+
						dylib->current_version.minor,
+
						dylib->current_version.patch,
+
						dylib->compatibility_version.major,
+
						dylib->compatibility_version.minor,
+
						dylib->compatibility_version.patch);
+

+
					basename++;
+

+
					char *lib_with_version;
+
					if (dylib->current_version.patch) {
+
						xasprintf(&lib_with_version, "%s-%"PRIuFAST16".%"PRIuFAST16".%"PRIuFAST16, basename, dylib->current_version.major, dylib->current_version.minor, dylib->current_version.patch);
+
					} else {
+
						xasprintf(&lib_with_version, "%s-%"PRIuFAST16".%"PRIuFAST16, basename, dylib->current_version.major, dylib->current_version.minor);
+
					}
+
					if (LC_ID_DYLIB == loadcmd) {
+
						pkg_addshlib_provided(pkg, lib_with_version);
+
					} else {
+
						pkg_addshlib_required(pkg, lib_with_version);
+
					}
+
					free(lib_with_version);
+
				}
+
			}
+
			free(dylib);
+
			break;
+
		default:
+
			break;
+
		}
+
		const uint32_t fill = loadcmdsize - (n - n0);
+
		if (fill && -1 == (x = lseek(fd, fill, SEEK_CUR))) {
+
			goto cleanup;
+
		}
+
		n += fill;
+
		if (n > mh.sizeofcmds) {
+
			// we passed the frame boundary of the load commands
+
			pkg_emit_error("Mach-O structure misread.");
+
			errno = EINVAL;
+
			goto cleanup;
+
		}
+
	}
+

+
cleanup:
+
	free(mf);
+
	return ret;
+
}
+

int
pkg_analyse_init_macho(__unused const char *stage)
{
@@ -320,10 +463,23 @@ pkg_analyse_init_macho(__unused const char *stage)
}

int
-
pkg_analyse_macho(const bool developer_mode, __unused struct pkg *pkg, __unused const char *fpath)
+
pkg_analyse_macho(const bool developer_mode, struct pkg *pkg, const char *fpath)
{
	int ret = EPKG_OK;
-
	// int ret = analyse_macho(pkg, fpath);
+
	bool baselibs = pkg_object_bool(pkg_config_get("ALLOW_BASE_SHLIBS"));
+
	pkg_debug(1, "Analysing Mach-O %s %d", fpath, baselibs);
+

+
	int fd = open(fpath, O_RDONLY);
+
	if (-1 == fd) {
+
		pkg_emit_errno("open", fpath);
+
		ret = EPKG_FATAL;
+
	} else {
+
		ret = analyse_macho(fd, pkg, baselibs);
+
		if (-1 == close(fd)) {
+
			pkg_emit_errno("open", fpath);
+
			ret = EPKG_FATAL;
+
		}
+
	}
	if (developer_mode) {
		if (ret != EPKG_OK && ret != EPKG_END) {
			return EPKG_WARN;
deleted libpkg/pkg_macho.c
@@ -1,394 +0,0 @@
-
/*-
-
 * Copyright (c) 2014 Landon Fuller <landon@landonf.org>
-
 * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org>
-
 * Copyright (c) 2012-2013 Matthew Seaman <matthew@FreeBSD.org>
-
 * All rights reserved.
-
 *
-
 * Redistribution and use in source and binary forms, with or without
-
 * modification, are permitted provided that the following conditions
-
 * are met:
-
 * 1. Redistributions of source code must retain the above copyright
-
 *    notice, this list of conditions and the following disclaimer
-
 *    in this position and unchanged.
-
 * 2. Redistributions in binary form must reproduce the above copyright
-
 *    notice, this list of conditions and the following disclaimer in the
-
 *    documentation and/or other materials provided with the distribution.
-
 *
-
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
-
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
-
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 */
-

-
#ifdef HAVE_CONFIG_H
-
#include "pkg_config.h"
-
#endif
-

-
#include <sys/types.h>
-
#include <sys/sysctl.h>
-
#include <sys/utsname.h>
-

-
#include <mach/machine.h>
-
#include <mach-o/arch.h>
-
#include <mach-o/loader.h>
-

-
#include <ctype.h>
-
#include <limits.h>
-

-
#include <libmachista.h>
-
#include <bsd_compat.h>
-

-
#include "pkg.h"
-
#include "private/pkg.h"
-
#include "private/event.h"
-
#include "private/binfmt.h"
-

-
static const char * const system_dylib_prefixes[] = {
-
	"/System/",
-
	"/usr/lib",
-
	"/lib",
-
	NULL
-
 };
-

-
static int
-
analyse_macho(struct pkg *pkg, const char *fpath,
-
	      cpu_type_t cpu_type, macho_handle_t *macho_handle,
-
	      int (action)(void *, struct pkg *, const char *, const char *, bool),
-
	      void *actdata)
-
{
-
	const macho_t *macho = NULL;
-
	struct stat sb;
-
	int mret;
-

-
	/* We're only interested in the generic CPU type */
-
	cpu_type = cpu_type & ~CPU_ARCH_MASK;
-

-
	if (lstat(fpath, &sb) != 0)
-
		pkg_emit_errno("fstat() failed for", fpath);
-

-
	/* ignore empty files and non regular files */
-
	if (sb.st_size == 0 || !S_ISREG(sb.st_mode))
-
		return EPKG_END; /* Empty file or sym-link: no results */
-

-

-
	/* Try to parse the file */
-
	if ((mret = macho_parse_file(macho_handle, fpath, &macho)) != MACHO_SUCCESS) {
-
		if (mret != MACHO_EMAGIC && mret != MACHO_ERANGE) {
-
			pkg_emit_error("macho_parse_file() for %s failed: %s", fpath, macho_strerror(mret));
-
			return EPKG_FATAL; /* Empty file or sym-link: no results */
-
		}
-

-
		/* Not a Mach-O file; no results */
-
		return EPKG_END;
-
	}
-

-
	for (macho_arch_t *march = macho->mt_archs; march != NULL; march = march->next) {
-
		const NXArchInfo *ai;
-
		bool is_shlib = false;
-

-
		/* Determine the architecture name */
-
		ai = NXGetArchInfoFromCpuType(march->mat_cputype, march->mat_cpusubtype);
-
		if (ai == NULL) {
-
			pkg_emit_notice("Could not determine architecture type for cpu %d subtype %d", march->mat_cputype, march->mat_cpusubtype);
-
			continue;
-
		}
-

-
		/* Register non-relative libraries as provided. */
-
		// MACTODO: How to handle @rpath/@loader_path/etc?
-
		if (march->mat_install_name != NULL && march->mat_install_name[0] != '/') {
-
			// XXX MACTODO: To get things working, we're shoving library metadata into the library name;
-
			// these should instead be added as supported attributes of package shared library declarations.
-
			char *libname;
-
			xasprintf(&libname, "%s.%s", march->mat_install_name, ai->name);
-
			pkg_addshlib_provided(pkg, libname);
-
			is_shlib = true;
-

-

-
		/* Now find all dependencies */
-
		for (macho_loadcmd_t *cmd = march->mat_loadcmds; cmd != NULL; cmd = cmd->next) {
-
			/* Skip everything except for non-weak dylib references */
-
			if (cmd->mlt_type != LC_LOAD_DYLIB)
-
				continue;
-

-
			/* Prevent cyclic self-references. A valid dylib shouldn't include a
-
			 * LC_LOAD_DYLIB referencing itself, but there's nothing that would
-
			 * actually prevent it */
-
			if (STREQ(cmd->mlt_install_name, march->mat_install_name))
-
				continue;
-

-
			/* Skip non-resolvable library paths. */
-
			switch (cmd->mlt_install_name[0]) {
-
				case '/':
-
					break;
-
				case '@':
-
					// MACTODO: How to handle @rpath/@loader_path/etc?
-
					continue;
-
				default:
-
					continue;
-
			}
-

-
			// XXX MACTODO: To get things working, we're shoving library metadata into the library name;
-
			// these should instead be added as supported attributes of package shared library declarations.
-
			// XXX: This duplicates (and must be kept identical to) the libname construction above.
-
			char *libname;
-
			xasprintf(&libname, "%s.%s", cmd->mlt_install_name, ai->name);
-

-
			action(actdata, pkg, fpath, libname, is_shlib);
-
		}
-
		}
-
	}
-

-
	return EPKG_OK;
-
}
-

-
static int
-
add_dylibs_to_pkg(__unused void *actdata, struct pkg *pkg, const char *fpath,
-
		  const char *name, bool is_shlib)
-
{
-
	/* Skip references to system libraries */
-
	for (size_t i = 0; i < NELEM(system_dylib_prefixes); i++) {
-
		const char *prefix = system_dylib_prefixes[i];
-
		if (strncmp(name, prefix, strlen(prefix)) == 0)
-
			return EPKG_OK;
-
	}
-

-
	/* Record the library requirement. */
-
	pkg_addshlib_required(pkg, name);
-

-
	return EPKG_OK;
-
}
-

-
/**
-
 * Extract the major release number from an XNU kernel
-
 * version returned by uname().
-
 *
-
 * @param src A major.minor.revision version string, e.g., as returned from uname(3).
-
 * @param release On success, the parsed major version.
-
 */
-
static int
-
parse_major_release(const char *src, long long *release)
-
{
-
	int ret = EPKG_OK;
-
	char *parsed = NULL;
-
	const char *errstr;
-
	char *eos;
-

-
	parsed = xstrdup(src);
-
	eos = strchr(parsed, '.');
-
	if (eos == NULL) {
-
		pkg_emit_error("failed to parse major release version from %s", src);
-
		ret = EPKG_FATAL;
-
		goto cleanup;
-
	}
-

-
	*eos = '\0';
-
	*release = strtonum(parsed, 1, LONG_LONG_MAX, &errstr);
-
	if (errstr != NULL) {
-
		pkg_emit_error("failed to parse major release version from %s: %s", src, errstr);
-
		ret = EPKG_FATAL;
-
		goto cleanup;
-
	}
-

-
cleanup:
-
	free(parsed);
-
	return ret;
-
}
-

-
/**
-
 * Fetch the host's CPU type.
-
 *
-
 * @param result On success, the fetched CPU type.
-
 */
-
static int
-
host_cpu_type(cpu_type_t *result)
-
{
-
	size_t len;
-
	int resint;
-

-
	/* Fetch CPU type */
-
	len = sizeof(resint);
-
	if (sysctlbyname("hw.cputype", &resint, &len, NULL, 0) != 0) {
-
		pkg_emit_errno("sysctlbyname", "hw.cputype");
-
		return EPKG_FATAL;
-
	}
-

-
	*result = resint;
-
	return EPKG_OK;
-
}
-

-
/**
-
 * Fetch the OS name and major version.
-
 *
-
 * @param osname On success, the OS name (e.g. Darwin).
-
 * @param sz The maximum number of bytes to be written to osname.
-
 * @param major_version On success, the major version of the host.
-
 */
-
static int
-
host_os_info(char *osname, size_t sz, long long *major_version)
-
{
-
	struct utsname ut;
-

-
	/* Fetch OS info from uname() */
-
	if (uname(&ut) != 0) {
-
		pkg_emit_errno("uname", "&ut");
-
		return EPKG_FATAL;
-
	}
-

-
	/* Provide the OS name to the caller. */
-
	if (sz < strlen(ut.sysname) + 1) {
-
		pkg_emit_error("provided buffer is too small for os name: %d",
-
				(int)strlen(ut.sysname));
-
		return EPKG_FATAL;
-
	}
-

-
	strlcpy(osname, ut.sysname, sz);
-

-
	/* Parse the major release version */
-
	return parse_major_release(ut.release, major_version);
-
}
-

-
int
-
pkg_analyse_files(struct pkgdb *db, struct pkg *pkg, const char *stage)
-
{
-
	macho_handle_t *macho_handle = NULL;
-
	struct pkg_file *file = NULL;
-
	bool failures = false;
-
	cpu_type_t cpu_type;
-
	char *fpath = NULL;
-
	int ret = EPKG_OK;
-

-
	/* Determine our system's CPU type */
-
	if ((ret = host_cpu_type(&cpu_type)) != EPKG_OK)
-
		return (EPKG_FATAL);
-

-
	/* Create our mach-o handle */
-
	macho_handle = macho_create_handle();
-
	if (macho_handle == NULL) {
-
		pkg_emit_error("macho_create_handle() failed");
-
		return (EPKG_FATAL);
-
	}
-

-
	/* Evaluate all package files */
-
	while ((ret = pkg_files(pkg, &file)) == EPKG_OK) {
-
		if (stage != NULL) {
-
			free(fpath);
-
			xasprintf(&fpath, "%s/%s", stage, file->path);
-
		} else {
-
			if ((fpath = file->path) == NULL) {
-
				pkg_emit_error("pkg_analyse_files(): path allocation failed");
-
				macho_destroy_handle(macho_handle);
-
				return (EPKG_FATAL);
-
			}
-
		}
-

-
		ret = analyse_macho(pkg, fpath, cpu_type, macho_handle, add_dylibs_to_pkg, db);
-
		if (ret != EPKG_OK && ret != EPKG_END) {
-
			failures = true;
-
		}
-
	}
-

-
	macho_destroy_handle(macho_handle);
-

-
	if (stage != NULL)
-
		free(fpath);
-

-
	return (failures ? EPKG_FATAL : ret);
-
}
-

-
int
-
pkg_arch_to_legacy(const char *arch, char *dest, size_t sz)
-
{
-
	int i = 0;
-
	const NXArchInfo *ai;
-
	const char *arch_name;
-
	bool is64;
-

-
	memset(dest, '\0', sz);
-
	/* Lower case the OS */
-
	while (arch[i] != ':' && arch[i] != '\0') {
-
		dest[i] = tolower(arch[i]);
-
		i++;
-
	}
-
	if (arch[i] == '\0')
-
		return (0);
-

-
	dest[i++] = ':';
-

-
	/* Copy the version */
-
	while (arch[i] != ':' && arch[i] != '\0') {
-
		dest[i] = arch[i];
-
		i++;
-
	}
-
	if (arch[i] == '\0')
-
		return (0);
-

-
	dest[i++] = ':';
-

-
	/* Map the architecture name to its CPU type */
-
	ai = NXGetArchInfoFromName(arch + i);
-
	if (ai == NULL) {
-
		pkg_emit_error("could not find architecture info for %s", arch + i);
-
		return EPKG_FATAL;
-
	}
-

-
	/* Fetch the base architecture name */
-
	arch_name = macho_get_arch_name(ai->cputype & ~CPU_ARCH_ABI64);
-
	if (arch_name == NULL) {
-
		pkg_emit_error("macho_get_arch_name() failed for %x", ai->cputype);
-
		return EPKG_FATAL;
-
	}
-

-
	/* Determine word size */
-
	is64 = (ai->cputype & CPU_ARCH_ABI64) != 0;
-

-
	/* Emit the result */
-
	snprintf(dest + i, sz - (arch + i - dest), "%s:%s", arch_name, is64 ? "64" : "32");
-
	return EPKG_OK;
-
}
-

-
int
-
pkg_get_myarch_legacy(char *dest, size_t sz)
-
{
-
	char current[sz];
-
	int ret;
-

-
	if ((ret = pkg_get_myarch(current, sizeof(current), NULL)) != EPKG_OK)
-
		return ret;
-

-
	return pkg_arch_to_legacy(current, dest, sz);
-
}
-

-
int
-
pkg_get_myarch(char *dest, size_t sz, struct os_info *__unused u)
-
{
-
	cpu_type_t cpu_type;
-
	const char *cpu_name = NULL;
-
	long long major_version;
-
	char os_name[BUFSIZ];
-
	char *spec = NULL;
-
	int ret = EPKG_OK;
-

-
	/* Fetch basic OS info */
-
	if ((ret = host_os_info(os_name, sizeof(os_name), &major_version)) != EPKG_OK)
-
	        return ret;
-

-
	/* Fetch host CPU type */
-
	if ((ret = host_cpu_type(&cpu_type)) != EPKG_OK)
-
	        return ret;
-

-
	/* Fetch the name for the base CPU family */
-
	cpu_name = macho_get_arch_name(cpu_type);
-

-
	/* Produce the result */
-
	xasprintf(&spec, "%s:%lld:%s", os_name, major_version, cpu_name);
-
	strlcpy(dest, spec, sz);
-

-
	free(spec);
-
	return ret;
-
}
modified libpkg/private/binfmt_macho.h
@@ -294,6 +294,10 @@ ssize_t read_build_version(const int fd, const bool swap,
    build_version_t **dest);
ssize_t read_min_version(const int fd, const bool swap, const uint32_t loadcmd,
    build_version_t **dest);
+
ssize_t read_path(const int fd, const bool swap, const uint32_t loadcmdsize, 
+
    char **dest);
+
ssize_t read_dylib(const int fd, const bool swap, const uint32_t loadcmdsize,
+
    dylib_t **dest);

#define READ(f, var)                              \
	if ((x = read_##f(fd, swap, &var)) < 0) { \
modified tests/Makefile.autosetup
@@ -92,6 +92,7 @@ TESTS_SHELL_BINS= \
	frontend/macos106.bin \
	frontend/macos150.bin \
	frontend/macosfat.bin \
+
	frontend/macosfatlib.bin \
	frontend/libtestfbsd.so.1 \
	frontend/libtest2fbsd.so.1 \
	frontend/libfoo.so.1
modified tests/frontend/create-parsebin.sh
@@ -63,10 +63,20 @@ categories [
    "test",
]
EOF
-
    if [ x"${ALLOW_BASE_SHLIBS}" = xyes -a -n "${Xshlibs_required_base}" ]; then
-
        echo "shlibs_required: [" >> ${PKG_NAME}.expected
-
        for i in ${Xshlibs_required_base}; do
-
            echo ${NL}"    "\"$i\" >> ${PKG_NAME}.expected
+
    if [ x"${ALLOW_BASE_SHLIBS}" = xyes ]; then
+
        Xshlibs_required="${Xshlibs_required_base}"
+
    fi 
+
    if [ -n "${Xshlibs_required}" ]; then
+
        echo "shlibs_required [" >> ${PKG_NAME}.expected
+
        for i in ${Xshlibs_required}; do
+
            echo ${NL}"    "\"$i\", >> ${PKG_NAME}.expected
+
        done
+
        echo "]" >> ${PKG_NAME}.expected
+
    fi
+
    if [ -n "${Xshlibs_provided}" ]; then
+
        echo "shlibs_provided [" >> ${PKG_NAME}.expected
+
        for i in ${Xshlibs_provided}; do
+
            echo ${NL}"    "\"$i\", >> ${PKG_NAME}.expected
        done
        echo "]" >> ${PKG_NAME}.expected
    fi
@@ -93,8 +103,9 @@ create_from_bin_body() {
        freebsd-aarch64.bin freebsd-amd64.bin freebsd-armv6.bin freebsd-armv7.bin \
		freebsd-i386.bin freebsd-powerpc.bin freebsd-powerpc64.bin freebsd-powerpc64le.bin \
		freebsd-riscv64.bin dfly.bin linux.bin \
-
        macos.bin macos106.bin macos150.bin macosfat.bin \
-
		"macosfat.bin#amd64" "macosfat.bin#aarch64"
+
        macos.bin macos106.bin macos150.bin \
+
        macosfat.bin "macosfat.bin#amd64" "macosfat.bin#aarch64" \
+
        macosfatlib.bin "macosfatlib.bin#amd64" "macosfatlib.bin#aarch64"
    do
        local file1=$(atf_get_srcdir)/$bin

@@ -124,12 +135,15 @@ create_from_bin_body() {
create_from_binbase_body() {
    local PKG_NAME=testbinbase

+
    # FIXME: All ELF readers are failing on non-native runs.
    for bin in \
+
        macos.bin macos106.bin macos150.bin \
+
        macosfat.bin "macosfat.bin#amd64" "macosfat.bin#aarch64" \
+
        macosfatlib.bin "macosfatlib.bin#amd64" "macosfatlib.bin#aarch64" \
        freebsd-aarch64.bin freebsd-amd64.bin freebsd-armv6.bin freebsd-armv7.bin \
-
		freebsd-i386.bin freebsd-powerpc.bin freebsd-powerpc64.bin freebsd-powerpc64le.bin \
-
		freebsd-riscv64.bin dfly.bin linux.bin \
-
        macos.bin macos106.bin macos150.bin macosfat.bin \
-
		"macosfat.bin#amd64" "macosfat.bin#aarch64"
+
        freebsd-i386.bin freebsd-powerpc.bin freebsd-powerpc64.bin freebsd-powerpc64le.bin \
+
        freebsd-riscv64.bin \
+
        linux.bin dfly.bin
    do
        local file1=$(atf_get_srcdir)/$bin

modified tests/frontend/test_environment.sh.in
@@ -53,7 +53,11 @@ atf_require() {

bin_meta() {
	local file="$1"
+
	XABI=""
+
	XALTABI=""
	XFreeBSD_version=""
+
	Xshlibs_provided=""
+
	Xshlibs_required=""
	Xshlibs_required_base=""
	case "${file}" in 
		*freebsd-aarch64.bin)
@@ -124,26 +128,27 @@ bin_meta() {
		*macos.bin)
			XABI=Darwin:24:aarch64
			XALTABI=darwin:24:aarch64:64
-
			Xshlibs_required_base="libSystem.B.dylib"
+
			Xshlibs_required_base="libSystem.B.dylib-1351.0"
			;;
		*macos106.bin)
			XABI=Darwin:10:amd64
			XALTABI=darwin:10:x86:64
-
			Xshlibs_required_base="libSystem.B.dylib"
+
			Xshlibs_required_base="libSystem.B.dylib-125.2.11"
			;;
		*macos150.bin)
			XABI=Darwin:24:amd64
			XALTABI=darwin:24:x86:64
-
			Xshlibs_required_base="libSystem.B.dylib"
+
			Xshlibs_required_base="libSystem.B.dylib-1351.0"
			;;

		# macosfat.bin has amd64 as its first entry
		*macosfat.bin|*macosfat.bin#amd64)
-
			XABI=Darwin:24:amd64
-
			XALTABI=darwin:24:x86:64
-
			Xshlibs_required_base="libSystem.B.dylib"
+
			XABI=Darwin:17:amd64
+
			XALTABI=darwin:17:x86:64
+
			Xshlibs_required="libAnswer.A.dylib-1.2"
+
			Xshlibs_required_base="libAnswer.A.dylib-1.2 libSystem.B.dylib-1351.0"
			;;
-
			
+

		# macosfat also has an aarch64 entry
		*macosfat.bin#aarch64)
			XABI=Darwin:20:aarch64