Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
libpkg: rework internal ABI handling, fix bugs
Isaac Freund committed 1 year ago
commit 6c740ac388db9c0ea53688e2606ffff27bab2a03
parent 5f891a0
16 files changed +756 -554
modified libpkg/elfhints.c
@@ -28,6 +28,7 @@
 */

#include <bsd_compat.h>
+
#include "private/pkg_abi.h"
#include <sys/mman.h>
#include <sys/stat.h>
#ifdef HAVE_SYS_ENDIAN_H
@@ -315,7 +316,7 @@ int shlib_list_from_rpath(const char *rpath_str, const char *dirpath)
int
shlib_list_from_elf_hints(const char *hintsfile)
{
-
	if (ctx.oi->ostype == OS_FREEBSD || ctx.oi->ostype == OS_DRAGONFLY)
+
	if (ctx.abi.os == PKG_OS_FREEBSD || ctx.abi.os == PKG_OS_DRAGONFLY)
		read_elf_hints(hintsfile, false);

	return (scan_dirs_for_shlibs(&shlibs, ndirs, dirs, true));
modified libpkg/pkg_abi.c
@@ -1,28 +1,12 @@
/*-
 * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org>
 * Copyright (c) 2012-2013 Matthew Seaman <matthew@FreeBSD.org>
-
 * All rights reserved.
+
 * Copyright (c) 2024 The FreeBSD Foundation
 *
-
 * 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 was developed in part by Isaac Freund <ifreund@freebsdfoundation.org>
+
 * under sponsorship from the FreeBSD Foundation.
 *
-
 * 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.
+
 * SPDX-License-Identifier: BSD-2-Clause
 */
#ifdef HAVE_CONFIG_H
#include "pkg_config.h"
@@ -30,12 +14,15 @@

#include <ctype.h>
#include <paths.h>
+
#include <string.h>
#include <unistd.h>

#include "pkg.h"
+
#include "private/pkg_abi.h"
#include "private/binfmt.h"
#include "private/event.h"
#include "private/pkg.h"
+
#include "xmalloc.h"

#define _PATH_UNAME "/usr/bin/uname"

@@ -62,8 +49,256 @@ static struct arch_trans machine_arch_translation[] = { { "x86:32", "i386" },

	{ NULL, NULL } };

-
static int
-
pkg_get_myarch_fromfile(struct os_info *oi)
+
static struct {
+
	enum pkg_os os;
+
	const char *string;
+
} os_string_table[] = {
+
	{ PKG_OS_UNKNOWN, "Unknown" },
+
	{ PKG_OS_FREEBSD, "FreeBSD" },
+
	{ PKG_OS_NETBSD, "NetBSD" },
+
	{ PKG_OS_DRAGONFLY, "dragonfly" },
+
	{ PKG_OS_LINUX, "Linux" },
+
	{ PKG_OS_MACOS, "Darwin" },
+
	{ -1, NULL },
+
};
+

+
/* This table does not include PKG_ARCH_AMD64 as the string translation of
+
   that arch is os-dependent. */
+
static struct {
+
	enum pkg_arch arch;
+
	const char *string;
+
} arch_string_table[] = {
+
	{ PKG_ARCH_UNKNOWN, "unknown"},
+
	{ PKG_ARCH_I386, "i386"},
+
	{ PKG_ARCH_ARMV6, "armv6"},
+
	{ PKG_ARCH_ARMV7, "armv7"},
+
	{ PKG_ARCH_AARCH64, "aarch64"},
+
	{ PKG_ARCH_POWERPC, "powerpc"},
+
	{ PKG_ARCH_POWERPC64, "powerpc64"},
+
	{ PKG_ARCH_POWERPC64LE, "powerpc64le"},
+
	{ PKG_ARCH_RISCV32, "riscv32"},
+
	{ PKG_ARCH_RISCV64, "riscv64"},
+
	{ -1, NULL },
+
};
+

+
const char *
+
pkg_os_to_string(enum pkg_os os)
+
{
+
	for (size_t i = 0; os_string_table[i].string != NULL; i++) {
+
		if (os == os_string_table[i].os) {
+
			return os_string_table[i].string;
+
		}
+
	}
+
	assert(0);
+
}
+

+
enum pkg_os
+
pkg_os_from_string(const char *string)
+
{
+
	for (size_t i = 0; os_string_table[i].string != NULL; i++) {
+
		if (STREQ(string, os_string_table[i].string)) {
+
			return os_string_table[i].os;
+
		}
+
	}
+
	return (PKG_OS_UNKNOWN);
+
}
+

+
/* Returns true if the OS uses "amd64" rather than "x86_64" */
+
static bool
+
pkg_os_uses_amd64_name(enum pkg_os os)
+
{
+
	switch (os) {
+
	case PKG_OS_FREEBSD:
+
	case PKG_OS_MACOS:
+
		return (true);
+
	case PKG_OS_NETBSD:
+
	case PKG_OS_LINUX:
+
		return (false);
+
	case PKG_OS_DRAGONFLY:
+
	case PKG_OS_UNKNOWN:
+
	default:
+
		assert(0);
+
	}
+
}
+

+
const char *
+
pkg_arch_to_string(enum pkg_os os, enum pkg_arch arch)
+
{
+
	if (arch == PKG_ARCH_AMD64) {
+
		if (os == PKG_OS_DRAGONFLY) {
+
			return ("x86:64");
+
		} else if (pkg_os_uses_amd64_name(os)) {
+
			return ("amd64");
+
		} else {
+
			return ("x86_64");
+
		}
+
	}
+

+
	for (size_t i = 0; arch_string_table[i].string != NULL; i++) {
+
		if (arch == arch_string_table[i].arch) {
+
			return arch_string_table[i].string;
+
		}
+
	}
+

+
	assert(0);
+
}
+

+
enum pkg_arch
+
pkg_arch_from_string(enum pkg_os os, const char *string)
+
{
+
	if (os == PKG_OS_DRAGONFLY) {
+
		if (STREQ(string, "x86:64")) {
+
			return (PKG_ARCH_AMD64);
+
		}
+
	} else if (pkg_os_uses_amd64_name(os)) {
+
		if (STREQ(string, "amd64")) {
+
			return (PKG_ARCH_AMD64);
+
		}
+
	} else {
+
		if (STREQ(string, "x86_64")) {
+
			return (PKG_ARCH_AMD64);
+
		}
+
	}
+

+
	for (size_t i = 0; arch_string_table[i].string != NULL; i++) {
+
		if (STREQ(string, arch_string_table[i].string)) {
+
			return arch_string_table[i].arch;
+
		}
+
	}
+

+
	return (PKG_ARCH_UNKNOWN);
+
}
+

+
bool
+
pkg_abi_string_only_major_version(enum pkg_os os)
+
{
+
	switch (os) {
+
	case PKG_OS_FREEBSD:
+
	case PKG_OS_NETBSD:
+
	case PKG_OS_MACOS:
+
		return (true);
+
	case PKG_OS_DRAGONFLY:
+
	case PKG_OS_LINUX:
+
		return (false);
+
	case PKG_OS_UNKNOWN:
+
	default:
+
		assert (0);
+
	}
+
}
+

+
char *
+
pkg_abi_to_string(const struct pkg_abi *abi)
+
{
+
	char *ret;
+
	if (pkg_abi_string_only_major_version(abi->os)) {
+
		xasprintf(&ret, "%s:%d:%s", pkg_os_to_string(abi->os),
+
		    abi->major, pkg_arch_to_string(abi->os, abi->arch));
+
	} else {
+
		xasprintf(&ret, "%s:%d.%d:%s", pkg_os_to_string(abi->os),
+
		    abi->major, abi->minor,
+
		    pkg_arch_to_string(abi->os, abi->arch));
+
	}
+
	return (ret);
+
}
+

+
bool
+
pkg_abi_from_string(struct pkg_abi *abi, const char *string)
+
{
+
	*abi = (struct pkg_abi){0};
+

+
	bool ret = false;
+

+
	char *copy = xstrdup(string);
+

+
	char *iter = copy;
+
	char *os = strsep(&iter, ":");
+
	assert(os != NULL);
+
	abi->os = pkg_os_from_string(os);
+
	if (abi->os == PKG_OS_UNKNOWN) {
+
		pkg_emit_error("Unknown OS '%s' in ABI string", os);
+
		goto out;
+
	}
+

+
	char *version = strsep(&iter, ":");
+
	if (version == NULL) {
+
		pkg_emit_error("Invalid ABI string '%s', "
+
		    "missing version and architecture", string);
+
		goto out;
+
	}
+
	const char *errstr = NULL;
+
	if (pkg_abi_string_only_major_version(abi->os)) {
+
		abi->major = strtonum(version, 1, INT_MAX, &errstr);
+
	} else {
+
		/* XXX add tests for this */
+
		char *major = strsep(&version, ".");
+
		char *minor = strsep(&version, ".");
+

+
		assert(major != NULL);
+
		if (minor == NULL) {
+
			pkg_emit_error("Invalid ABI string %s, "
+
			    "missing minor OS version", string);
+
			goto out;
+
		}
+

+
		abi->major = strtonum(major, 1, INT_MAX, &errstr);
+
		if (errstr != NULL) {
+
			abi->minor = strtonum(minor, 1, INT_MAX, &errstr);
+
		}
+
	}
+
	if (errstr != NULL) {
+
		pkg_emit_error("Invalid version in ABI string '%s'", string);
+
		goto out;
+
	}
+

+
	/* DragonFlyBSD continues to use the legacy/altabi format.
+
	   For example: dragonfly:5.10:x86:64
+
	   This means we can't use strsep again since that would split the arch
+
	   string for dragonfly. */
+
	char *arch = iter;
+
	if (arch == NULL) {
+
		pkg_emit_error("Invalid ABI string '%s', "
+
		    "missing architecture", string);
+
		goto out;
+
	}
+

+
	abi->arch = pkg_arch_from_string(abi->os, arch);
+
	if (abi->arch == PKG_ARCH_UNKNOWN) {
+
		pkg_emit_error("Unknown architecture '%s' in ABI string", arch);
+
		goto out;
+
	}
+

+
	if (abi->os == PKG_OS_DRAGONFLY && abi->arch != PKG_ARCH_AMD64) {
+
		pkg_emit_error("Invalid ABI string '%s', "
+
		    "only x86:64 is supported on dragonfly.", string);
+
		goto out;
+
	}
+

+
	ret = true;
+
out:
+
	free(copy);
+
	return (ret);
+
}
+

+
void
+
pkg_abi_set_freebsd_osversion(struct pkg_abi *abi, int osversion)
+
{
+
	assert(abi->os == PKG_OS_FREEBSD);
+

+
	abi->major = osversion / 100000;
+
	abi->minor = (osversion / 1000) % 100;
+
	abi->patch = osversion % 1000;
+
}
+

+
int
+
pkg_abi_get_freebsd_osversion(struct pkg_abi *abi)
+
{
+
	assert(abi->os == PKG_OS_FREEBSD);
+

+
	return (abi->major * 100000) + (abi->minor * 1000) + abi->patch;
+
}
+

+
int
+
pkg_abi_from_file(struct pkg_abi *abi)
{
	char rooted_abi_file[PATH_MAX];
	const char *abi_files[] = {
@@ -125,26 +360,37 @@ pkg_get_myarch_fromfile(struct os_info *oi)
		return EPKG_FATAL;
	}

-
	if (work_arch_hint[0]) {
-
		snprintf(oi->abi, sizeof(oi->abi), "::%s",
-
		    work_arch_hint);
-
	}

-
	int ret = pkg_get_myarch_elfparse(fd, oi);
+
	int ret = pkg_elf_abi_from_fd(fd, abi);
	if (EPKG_OK != ret) {
		if (-1 == lseek(fd, 0, SEEK_SET)) {
			pkg_emit_errno("Error seeking file", work_abi_file);
			ret = EPKG_FATAL;
+
			goto close_out;
+
		}
+

+
		enum pkg_arch arch_hint = PKG_ARCH_UNKNOWN;
+
		if (work_arch_hint[0]) {
+
			arch_hint = pkg_arch_from_string(PKG_OS_MACOS, work_arch_hint);
+
			if (arch_hint == PKG_ARCH_UNKNOWN) {
+
				pkg_emit_error("Invalid ABI_FILE architecture hint %s",
+
				    work_arch_hint);
+
				ret = EPKG_FATAL;
+
				goto close_out;
+
			}
		}
-
		ret = pkg_get_myarch_macho(fd, oi);
+

+
		ret = pkg_macho_abi_from_fd(fd, abi, arch_hint);
		if (EPKG_OK != ret) {
			pkg_emit_error(
			    "Unable to determine ABI, %s cannot be parsed.",
			    work_abi_file);
			ret = EPKG_FATAL;
+
			goto close_out;
		}
	}

+
close_out:
	if (close(fd)) {
		pkg_emit_errno("Error closing file", work_abi_file);
		ret = EPKG_FATAL;
@@ -153,32 +399,6 @@ pkg_get_myarch_fromfile(struct os_info *oi)
}

int
-
pkg_get_myarch_with_legacy(struct os_info *oi)
-
{
-
	if (oi == NULL)
-
		return (EPKG_FATAL);
-
	int err = pkg_get_myarch_fromfile(oi);
-
	if (err) {
-
		pkg_debug(1, "Error %d when trying to determine myarch.", err);
-
		free(oi->name);
-
		return (err);
-
	}
-

-
	pkg_arch_to_legacy(oi->abi, oi->altabi, sizeof(oi->abi));
-

-
	if (oi->ostype == OS_DRAGONFLY) {
-
		size_t dsz;
-

-
		dsz = strlen(oi->abi);
-
		for (int i = 0; i < dsz; i++)
-
			oi->abi[i] = tolower(oi->abi[i]);
-
		return (0);
-
	}
-

-
	return (0);
-
}
-

-
int
pkg_arch_to_legacy(const char *arch, char *dest, size_t sz)
{
	int i = 0;
modified libpkg/pkg_abi_macho.c
@@ -9,6 +9,7 @@
#include "private/binfmt_macho.h"
#include "private/pkg.h"
#include "private/event.h"
+
#include "private/pkg_abi.h"

/**
 * Routines to support pkg_abi.c functions when dealing with Mach-O files.
@@ -18,17 +19,15 @@
 * architectures.
 */

-
/**** CPU -> FreeBSD MACHINE_ARCH conversion ****/
-

-
static const char *
-
cputype_to_freebsd_machine_arch(const cpu_type_subtype_t cpu)
+
static enum pkg_arch
+
cputype_to_pkg_arch(const cpu_type_subtype_t cpu)
{
	switch (cpu.type) {
	case CPU_TYPE_ARM:
		if (cpu.type_is64_32) {
-
			return "aarch64-x32";
+
			return (PKG_ARCH_UNKNOWN); /* aarch64-x32 */
		} else if (cpu.type_is64) {
-
			return "aarch64";
+
			return (PKG_ARCH_AARCH64);
		} else {
			switch (cpu.subtype_arm) {
			case CPU_SUBTYPE_ARM_V7:
@@ -36,104 +35,100 @@ cputype_to_freebsd_machine_arch(const cpu_type_subtype_t cpu)
			case CPU_SUBTYPE_ARM_V7K:
			case CPU_SUBTYPE_ARM_V7M:
			case CPU_SUBTYPE_ARM_V7EM:
-
				return "armv7";
+
				return (PKG_ARCH_ARMV7);
			case CPU_SUBTYPE_ARM_V6:
			case CPU_SUBTYPE_ARM_V6M:
-
				return "armv6";
+
				return (PKG_ARCH_ARMV6);
			case CPU_SUBTYPE_ARM_XSCALE:
			case CPU_SUBTYPE_ARM_V5:
			case CPU_SUBTYPE_ARM_V4T:
-
				return "armeb";
			case CPU_SUBTYPE_ARM_ALL:
			default:
-
				return "arm";
+
				return (PKG_ARCH_UNKNOWN);
			}
		}
	case CPU_TYPE_POWERPC:
		if (cpu.type_is64_32) {
-
			return "powerpc64-x32";
+
			return (PKG_ARCH_UNKNOWN); /* powerpc64-x32 */
		} else if (cpu.type_is64) {
-
			return "powerpc64";
+
			return (PKG_ARCH_POWERPC64);
		} else {
-
			return "powerpc";
+
			return (PKG_ARCH_POWERPC);
		}
	case CPU_TYPE_X86:
		if (cpu.type_is64_32) {
-
			return "amd64-x32";
+
			return (PKG_ARCH_UNKNOWN); /* amd64-x32 */
		} else if (cpu.type_is64) {
-
			return "amd64";
+
			return (PKG_ARCH_AMD64);
		} else {
-
			return "i386";
+
			return (PKG_ARCH_I386);
		}
	default:
-
		return "unknown";
+
		return (PKG_ARCH_UNKNOWN);
	}
}

static cpu_type_subtype_t
-
freebsd_machine_arch_to_cputype(const char *archname) {
+
pkg_arch_to_cputype(enum pkg_arch arch) {
	cpu_type_subtype_t cpu = { 0 };

-
	if (!strcmp("aarch64", archname)) {
+
	switch (arch) {
+
	case PKG_ARCH_AARCH64:
		cpu.type = CPU_TYPE_ARM;
		cpu.type_is64 = true;
-
	} else if (!strcmp("amd64", archname)) {
+
		break;
+
	case PKG_ARCH_AMD64:
		cpu.type = CPU_TYPE_X86;
		cpu.type_is64 = true;
		cpu.subtype_x86 = CPU_SUBTYPE_X86_ALL;
-
	} else if (!strcmp("arm", archname)) {
-
		cpu.type = CPU_TYPE_ARM;
-
		cpu.subtype_arm = CPU_SUBTYPE_ARM_ALL;
-
	} else if (!strcmp("armeb", archname)) {
-
		cpu.type = CPU_TYPE_ARM;
-
		cpu.subtype_arm = CPU_SUBTYPE_ARM_V5;
-
	} else if (!strcmp("armv6", archname)) {
+
		break;
+
	case PKG_ARCH_ARMV6:
		cpu.type = CPU_TYPE_ARM;
		cpu.subtype_arm = CPU_SUBTYPE_ARM_V6;
-
	} else if (!strcmp("armv7", archname)) {
+
		break;
+
	case PKG_ARCH_ARMV7:
		cpu.type = CPU_TYPE_ARM;
		cpu.subtype_arm = CPU_SUBTYPE_ARM_V7;
-
	} else if (!strcmp("i386", archname)) {
+
		break;
+
	case PKG_ARCH_I386:
		cpu.type = CPU_TYPE_X86;
		cpu.subtype_x86 = CPU_SUBTYPE_X86_ALL;
-
	} else if (!strcmp("powerpc", archname)) {
+
		break;
+
	case PKG_ARCH_POWERPC:
		cpu.type = CPU_TYPE_POWERPC;
		cpu.subtype_ppc = CPU_SUBTYPE_POWERPC_ALL;
-
	} else if (!strcmp("powerpc64", archname)) {
+
		break;
+
	case PKG_ARCH_POWERPC64:
		cpu.type = CPU_TYPE_POWERPC;
		cpu.type_is64 = true;
		cpu.subtype_ppc = CPU_SUBTYPE_POWERPC_ALL;
-
	} else {
-
		// alpha
-
		// ia64
-
		// mips*
-
		// pc98
-
		// sparc64
+
		break;
+
	case PKG_ARCH_POWERPC64LE:
+
	case PKG_ARCH_RISCV32:
+
	case PKG_ARCH_RISCV64:
+
	case PKG_ARCH_UNKNOWN:
		cpu.type = CPU_TYPE_ANY;
+
		break;
	}
+

	return cpu;
}


/**
-
 * Using the passed mf descriptor, match the best entry using oi->name as a hint.
+
 * Using the passed mf descriptor, match the best entry using the provided hint.
 * No hint or no architecture in hint -> first entry. Debug1 warning if this is not precise match (there were multiple to choose from)
 * Hint -> always match, even if single architecture in file. Notice if match fails and return null.
 */
static const fat_arch_t *
-
match_entry(macho_file_t *mf, struct os_info *oi)
+
match_entry(macho_file_t *mf, enum pkg_arch arch_hint)
{
	const fat_arch_t *p = mf->arch;
-
	char *abihint = xstrdup(oi->abi);
-
	char *abisep = abihint;
-
	/*const char *osname = */strsep(&abisep, ":");
-
	/*const char *version_str = */ strsep(&abisep, ":");
-
	const char *archname = strsep(&abisep, ":");
-
	if (archname) {	
-
		const cpu_type_subtype_t cpu_hint = freebsd_machine_arch_to_cputype(archname);
+
	if (arch_hint != PKG_ARCH_UNKNOWN) {
+
		const cpu_type_subtype_t cpu_hint = pkg_arch_to_cputype(arch_hint);
		const fat_arch_t *p_end = p + mf->narch;
		while (p < p_end) {
-
			// do not match cpu_hint.type == CPU_TYPE_ANY which is used if the 
+
			// do not match cpu_hint.type == CPU_TYPE_ANY which is used if the
			// archname hint was not recognized
			if (p->cpu.type == cpu_hint.type &&
			    p->cpu.type_is64 == cpu_hint.type_is64) {
@@ -173,18 +168,19 @@ match_entry(macho_file_t *mf, struct os_info *oi)
				}
			}
			pkg_debug(1, "Looking for %s, did not match %s",
-
		    archname, cputype_to_freebsd_machine_arch(p->cpu));
+
			    pkg_arch_to_string(PKG_OS_MACOS, arch_hint),
+
			    pkg_arch_to_string(PKG_OS_MACOS, cputype_to_pkg_arch(p->cpu)));
			p++;
		}
-
		pkg_emit_notice("Scanned %"PRIu32" entr%s, found none matching selector %s",
-
			mf->narch, mf->narch > 1 ? "ies" : "y", archname);
-
		p=0;
+
		pkg_emit_notice("Scanned %d entr%s, found none matching selector %s",
+
			mf->narch, mf->narch > 1 ? "ies" : "y",
+
			pkg_arch_to_string(PKG_OS_MACOS, arch_hint));
+
		return 0;
	} else if (mf->narch > 1 ) {
		pkg_debug(1,"Found %"PRIu32" entries in universal binary, picking first",
			mf->narch);
	}
matched:
-
	free(abihint);
	return p;
}

@@ -193,15 +189,17 @@ matched:
 * all members of os_info except altabi with values obtained by parsing the Mach-O
 * file passed with file descriptor.
 *
-
 * Third (architecture) component of oi->abi is used to determine the fat entry to be parsed
-
 * in a universal binary. when not set, the first entry is used.
+
 * The arch_hint is used to determine the fat entry to be parsed in a universal
+
 * binary. If arch_hint is PKG_ARCH_UNKNOWN, the first entry is used.
 *
 * Returns EPKG_OK if all went fine, EPKG_FATAL if anything went wrong.
 * Seeks the file descriptor to an arbitrary position.
 */
int
-
pkg_get_myarch_macho(int fd, struct os_info *oi)
+
pkg_macho_abi_from_fd(int fd, struct pkg_abi *abi, enum pkg_arch arch_hint)
{
+
	*abi = (struct pkg_abi){0};
+

	ssize_t x;
	pkg_error_t ret = EPKG_FATAL;

@@ -212,7 +210,7 @@ pkg_get_myarch_macho(int fd, struct os_info *oi)
		goto cleanup;
	}

-
	const fat_arch_t *p = match_entry(mf, oi);
+
	const fat_arch_t *p = match_entry(mf, arch_hint);

	if (!p) {
		goto cleanup;
@@ -279,31 +277,19 @@ pkg_get_myarch_macho(int fd, struct os_info *oi)
		macho_version_t darwin;
		map_platform_to_darwin(&darwin, bv->platform, bv->minos);

-
		oi->osversion = darwin.major * 100000 + darwin.minor * 1000 +
-
		    darwin.patch;
-
		oi->ostype = OS_MACOS;
-
		free(oi->name);
-
		oi->name = xstrdup("Darwin");
-
		free(oi->version);
-
		if (darwin.patch) {
-
			xasprintf(&oi->version, "%"PRIuFAST16".%"PRIuFAST16".%"PRIuFAST16"", darwin.major,
-
			    darwin.minor, darwin.patch);
+
		abi->os = PKG_OS_MACOS;
+

+
		abi->major = darwin.major;
+
		abi->minor = darwin.minor;
+
		abi->patch = darwin.patch;
+

+
		abi->arch = cputype_to_pkg_arch(mh.cpu);
+

+
		if (abi->arch == PKG_ARCH_UNKNOWN) {
+
			ret = EPKG_FATAL;
		} else {
-
			xasprintf(&oi->version, "%"PRIuFAST16".%"PRIuFAST16"", darwin.major,
-
			    darwin.minor);
+
			ret = EPKG_OK;
		}
-
		free(oi->version_major);
-
		xasprintf(&oi->version_major, "%"PRIuFAST16, darwin.major);
-
		free(oi->version_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:%"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);
-

-
		ret = EPKG_OK;
	} else {
		pkg_emit_notice("No OS version information found in binary.");
		ret = EPKG_WARN;
@@ -347,7 +333,7 @@ analyse_macho(int fd, struct pkg *pkg, const bool baselibs)
		goto cleanup;
	}

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

	if (!p) {
		goto cleanup;
@@ -395,7 +381,7 @@ analyse_macho(int fd, struct pkg *pkg, const bool baselibs)
			}
			n += x;
			if (!baselibs && system_dylib(dylib->path)) {
-
				pkg_debug(3, 
+
				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,
@@ -407,7 +393,7 @@ analyse_macho(int fd, struct pkg *pkg, const bool baselibs)
			} else {
				const char * basename = strrchr(dylib->path, '/');
				if (basename) {
-
					pkg_debug(3, 
+
					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,
modified libpkg/pkg_config.c
@@ -50,6 +50,7 @@

#include "pkg.h"
#include "private/pkg.h"
+
#include "private/pkg_abi.h"
#include "private/event.h"
#include "private/fetch.h"
#include "pkg_repos.h"
@@ -89,7 +90,6 @@ struct pkg_ctx ctx = {
	.compression_level = -1,
	.compression_threads = -1,
	.defer_triggers = false,
-
	.oi = NULL,
};

struct config_entry {
@@ -98,7 +98,6 @@ struct config_entry {
	const char *def;
};

-
static struct os_info oi = { 0 };
static struct pkg_repo *repos = NULL;
ucl_object_t *config = NULL;

@@ -159,16 +158,6 @@ static struct config_entry c[] = {
		"YES",
	},
	{
-
		PKG_STRING,
-
		"ABI",
-
		oi.abi,
-
	},
-
	{
-
		PKG_STRING,
-
		"ALTABI",
-
		oi.altabi,
-
	},
-
	{
		PKG_BOOL,
		"DEVELOPER_MODE",
		"NO",
@@ -380,11 +369,6 @@ static struct config_entry c[] = {
	},
#ifdef __FreeBSD__
	{
-
		PKG_INT,
-
		"OSVERSION",
-
		oi.str_osversion,
-
	},
-
	{
		PKG_BOOL,
		"IGNORE_OSVERSION",
		"NO",
@@ -797,40 +781,79 @@ walk_repo_obj(const ucl_object_t *obj, const char *file, pkg_init_flags flags)
	}
}

+
struct config_parser_vars {
+
	char *abi;
+
	char *altabi;
+
	char *osversion;
+
	char *release;
+
	char *version_major;
+
	char *version_minor;
+
};
+

+
/* Register parser variables based on ctx.abi.
+
 * The returned struct must be free'd with config_parser_variables_free()
+
 * after parsing is complete. */
+
static struct config_parser_vars *
+
config_parser_vars_register(struct ucl_parser *p)
+
{
+
	struct config_parser_vars *vars = xcalloc(1, sizeof(struct config_parser_vars));
+

+
	vars->abi = pkg_abi_to_string(&ctx.abi);
+
	ucl_parser_register_variable(p, "ABI", vars->abi);
+

+
	char altabi_buffer[BUFSIZ];
+
	pkg_arch_to_legacy(vars->abi, altabi_buffer, sizeof(altabi_buffer));
+
	vars->altabi = xstrdup(altabi_buffer);
+
	ucl_parser_register_variable(p, "ALTABI", vars->altabi);
+

+
	if (ctx.abi.os == PKG_OS_FREEBSD) {
+
		xasprintf(&vars->osversion, "%d",
+
		    pkg_abi_get_freebsd_osversion(&ctx.abi));
+
		ucl_parser_register_variable(p, "OSVERSION", vars->osversion);
+
	}
+
	ucl_parser_register_variable(p, "OSNAME", pkg_os_to_string(ctx.abi.os));
+

+
	if (pkg_abi_string_only_major_version(ctx.abi.os)) {
+
		xasprintf(&vars->release, "%d", ctx.abi.major);
+
	} else {
+
		xasprintf(&vars->release, "%d.%d", ctx.abi.major, ctx.abi.minor);
+
	}
+
	ucl_parser_register_variable(p, "RELEASE", vars->release);
+

+
	xasprintf(&vars->version_major, "%d", ctx.abi.major);
+
	ucl_parser_register_variable(p, "VERSION_MAJOR", vars->version_major);
+

+
	xasprintf(&vars->version_minor, "%d", ctx.abi.minor);
+
	ucl_parser_register_variable(p, "VERSION_MINOR", vars->version_minor);
+

+
	ucl_parser_register_variable(p, "ARCH",
+
	    pkg_arch_to_string(ctx.abi.os, ctx.abi.arch));
+

+
	return vars;
+
}
+

+
static void
+
config_parser_vars_free(struct config_parser_vars *vars)
+
{
+
	free(vars->abi);
+
	free(vars->altabi);
+
	free(vars->osversion);
+
	free(vars->release);
+
	free(vars->version_major);
+
	free(vars->version_minor);
+
	free(vars);
+
}
static void
load_repo_file(int dfd, const char *repodir, const char *repofile,
    pkg_init_flags flags)
{
	struct ucl_parser *p;
	ucl_object_t *obj = NULL;
-
	const char *myarch = NULL;
-
	const char *myarch_legacy = NULL;
	int fd;

	p = ucl_parser_new(0);

-
	myarch = pkg_object_string(pkg_config_get("ABI"));
-
	ucl_parser_register_variable (p, "ABI", myarch);
-

-
	myarch_legacy = pkg_object_string(pkg_config_get("ALTABI"));
-
	ucl_parser_register_variable (p, "ALTABI", myarch_legacy);
-
	if (oi.ostype == OS_FREEBSD)
-
		ucl_parser_register_variable(p, "OSVERSION", oi.str_osversion);
-
	if (oi.name != NULL) {
-
		ucl_parser_register_variable(p, "OSNAME", oi.name);
-
	}
-
	if (oi.version != NULL) {
-
		ucl_parser_register_variable(p, "RELEASE", oi.version);
-
	}
-
	if (oi.version_major != NULL) {
-
		ucl_parser_register_variable(p, "VERSION_MAJOR", oi.version_major);
-
	}
-
	if (oi.version_minor != NULL) {
-
		ucl_parser_register_variable(p, "VERSION_MINOR", oi.version_minor);
-
	}
-
	if (oi.arch != NULL) {
-
		ucl_parser_register_variable(p, "ARCH", oi.arch);
-
	}
+
	struct config_parser_vars *parser_vars = config_parser_vars_register(p);

	errno = 0;
	obj = NULL;
@@ -839,28 +862,28 @@ load_repo_file(int dfd, const char *repodir, const char *repofile,
	fd = openat(dfd, repofile, O_RDONLY);
	if (fd == -1) {
		pkg_errno("Unable to open '%s/%s'", repodir, repofile);
-
		return;
+
		goto out_parser_vars;
	}
	if (!ucl_parser_add_fd(p, fd)) {
		pkg_emit_error("Error parsing: '%s/%s': %s", repodir,
		    repofile, ucl_parser_get_error(p));
-
		ucl_parser_free(p);
-
		close(fd);
-
		return;
+
		goto out_fd;
	}
-
	close(fd);

	obj = ucl_parser_get_object(p);
	if (obj == NULL) {
-
		ucl_parser_free(p);
-
		return;
+
		goto out_fd;
	}

-
	ucl_parser_free(p);
	if (obj->type == UCL_OBJECT)
		walk_repo_obj(obj, repofile, flags);

	ucl_object_unref(obj);
+
out_fd:
+
	close(fd);
+
out_parser_vars:
+
	ucl_parser_free(p);
+
	config_parser_vars_free(parser_vars);
}

static int
@@ -922,23 +945,10 @@ bool
pkg_compiled_for_same_os_major(void)
{
#ifdef OSMAJOR
-
	const char	*myabi;
-
	int		 osmajor;
-

	if (getenv("IGNORE_OSMAJOR") != NULL)
		return (true);

-
	myabi = pkg_object_string(pkg_config_get("ABI"));
-
	myabi = strchr(myabi,':');
-
	if (myabi == NULL) {
-
		pkg_emit_error("Invalid ABI");
-
		return (false);
-
	}
-
	myabi++;
-

-
	osmajor = (int) strtol(myabi, NULL, 10);
-

-
	return (osmajor == OSMAJOR);
+
	return (ctx.abi.major == OSMAJOR);
#else
	return (true);		/* Can't tell, so assume yes  */
#endif
@@ -1001,6 +1011,63 @@ config_validate_debug_flags(const ucl_object_t *o)
	return (ret);
}

+
static bool
+
config_init_abi(struct pkg_abi *abi)
+
{
+
	if (getenv("ALTABI") != NULL) {
+
		pkg_emit_notice("Setting ALTABI manually is no longer supported, "
+
		    "set ABI and OSVERSION or ABI_FILE instead.");
+
	}
+

+
	const char *env_abi_file = getenv("ABI_FILE");
+
	const char *env_abi_string = getenv("ABI");
+
	const char *env_osversion_string = getenv("OSVERSION");
+

+
	if (env_abi_file != NULL && env_abi_string != NULL) {
+
		pkg_emit_notice("Both ABI_FILE and ABI are set, ABI_FILE overrides ABI");
+
	}
+

+
	if (env_abi_file != NULL && env_osversion_string != NULL) {
+
		pkg_emit_notice("Both ABI_FILE and OSVERSION are set, ABI_FILE overrides OSVERSION");
+
	}
+

+
	if (env_abi_string != NULL) {
+
		if (!pkg_abi_from_string(abi, env_abi_string)) {
+
			return (false);
+
		}
+

+
		if (abi->os == PKG_OS_FREEBSD) {
+
			if (env_osversion_string == NULL) {
+
				pkg_emit_error("Setting ABI requires setting OSVERSION as well");
+
				return (false);
+
			}
+

+
			const char *errstr = NULL;
+
			int env_osversion = strtonum(env_osversion_string, 1, INT_MAX, &errstr);
+
			if (errstr != NULL) {
+
				pkg_emit_error("Invalid OSVERSION %s, %s", env_osversion_string, errstr);
+
				return (false);
+
			}
+

+
			pkg_abi_set_freebsd_osversion(abi, env_osversion);
+
		} else {
+
			if (env_osversion_string != NULL) {
+
				pkg_emit_notice("OSVERSION is ignored on %s",
+
				    pkg_os_to_string(abi->os));
+
			}
+
		}
+
	} else if (env_osversion_string != NULL) {
+
		pkg_emit_error("Setting OSVERSION requires setting ABI as well");
+
		return (EPKG_FATAL);
+
	} else {
+
		if (pkg_abi_from_file(abi) != EPKG_OK) {
+
			return (false);
+
		}
+
	}
+

+
	return (true);
+
}
+

int
pkg_ini(const char *path, const char *reposdir, pkg_init_flags flags)
{
@@ -1024,38 +1091,27 @@ pkg_ini(const char *path, const char *reposdir, pkg_init_flags flags)
	char *tmp = NULL;
	size_t ukeylen;
	int err = EPKG_OK;
-
	const char *envabi;

	k = NULL;
	o = NULL;
	if (ctx.rootfd == -1 && (ctx.rootfd = open("/", O_DIRECTORY|O_RDONLY|O_CLOEXEC)) < 0) {
		pkg_emit_error("Impossible to open /");
-
		/* Note: Not goto out since oi.arch hasn't been initialized yet. */
		return (EPKG_FATAL);
	}

-
	memset(&oi, 0, sizeof(oi));
-
	envabi = getenv("ABI");
-
	if (envabi == NULL) {
-
		if (EPKG_OK != (err = pkg_get_myarch_with_legacy(&oi))) {
-
			goto out;
-
		}
-
	} else {
-
		strlcpy(oi.abi, envabi, sizeof(oi.abi));
-
		pkg_arch_to_legacy(oi.abi, oi.altabi, sizeof(oi.abi));
-
	}
-
	ctx.oi = &oi;
	if (parsed != false) {
		pkg_emit_error("pkg_init() must only be called once");
-
		err = EPKG_FATAL;
-
		goto out;
+
		return (EPKG_FATAL);
+
	}
+

+
	if (!config_init_abi(&ctx.abi)) {
+
		return (EPKG_FATAL);
	}

	if (((flags & PKG_INIT_FLAG_USE_IPV4) == PKG_INIT_FLAG_USE_IPV4) &&
	    ((flags & PKG_INIT_FLAG_USE_IPV6) == PKG_INIT_FLAG_USE_IPV6)) {
		pkg_emit_error("Invalid flags for pkg_init()");
-
		err = EPKG_FATAL;
-
		goto out;
+
		return (EPKG_FATAL);
	}
	if ((flags & PKG_INIT_FLAG_USE_IPV4) == PKG_INIT_FLAG_USE_IPV4)
		ctx.ip = IPV4;
@@ -1152,25 +1208,8 @@ pkg_ini(const char *path, const char *reposdir, pkg_init_flags flags)
	}

	p = ucl_parser_new(0);
-
	ucl_parser_register_variable (p, "ABI", oi.abi);
-
	ucl_parser_register_variable (p, "ALTABI", oi.altabi);
-
	if (oi.ostype == OS_FREEBSD)
-
		ucl_parser_register_variable(p, "OSVERSION", oi.str_osversion);
-
	if (oi.name != NULL) {
-
		ucl_parser_register_variable(p, "OSNAME", oi.name);
-
	}
-
	if (oi.version != NULL) {
-
		ucl_parser_register_variable(p, "RELEASE", oi.version);
-
	}
-
	if (oi.version_major != NULL) {
-
		ucl_parser_register_variable(p, "VERSION_MAJOR", oi.version_major);
-
	}
-
	if (oi.version_minor != NULL) {
-
		ucl_parser_register_variable(p, "VERSION_MINOR", oi.version_minor);
-
	}
-
	if (oi.arch != NULL) {
-
		ucl_parser_register_variable(p, "ARCH", oi.arch);
-
	}
+

+
	struct config_parser_vars *parser_vars = config_parser_vars_register(p);

	errno = 0;
	obj = NULL;
@@ -1203,6 +1242,16 @@ pkg_ini(const char *path, const char *reposdir, pkg_init_flags flags)
			continue;
		}

+
		if (strncasecmp(ukey->buf, "ABI", ukeylen) == 0 ||
+
		    strncasecmp(ukey->buf, "ALTABI", ukeylen) == 0 ||
+
		    strncasecmp(ukey->buf, "OSVERSION", ukeylen) == 0) {
+
			pkg_emit_error("Setting %s in pkg.conf is no longer supported. "
+
			    "Set ABI_FILE or ABI and OSVERSION with -o on the "
+
			    "command line or in the environment to configure ABI", ukey->buf);
+
			fatal_errors = true;
+
			continue;
+
		}
+

		/* ignore unknown keys */
		if (object == NULL)
			continue;
@@ -1334,11 +1383,28 @@ pkg_ini(const char *path, const char *reposdir, pkg_init_flags flags)
	ucl_object_unref(obj);
	ucl_parser_free(p);

-
	if (pkg_object_string(pkg_config_get("ABI")) == NULL ||
-
	    STREQ(pkg_object_string(pkg_config_get("ABI")), "unknown")) {
-
		pkg_emit_error("Unable to determine ABI");
-
		err = EPKG_FATAL;
-
		goto out;
+
	{
+
		/* Even though we no longer support setting ABI/ALTABI/OSVERSION
+
		   in the pkg.conf config file, we still need to expose these
+
		   values through e.g. `pkg config ABI`. */
+
		char *abi_string = pkg_abi_to_string(&ctx.abi);
+
		char altabi_string[BUFSIZ];
+
		pkg_arch_to_legacy(abi_string, altabi_string, sizeof(altabi_string));
+

+
		ucl_object_insert_key(config,
+
		    ucl_object_fromstring(abi_string), "ABI", 0, true);
+
		ucl_object_insert_key(config,
+
		    ucl_object_fromstring(altabi_string), "ALTABI", 0, true);
+

+
		free(abi_string);
+

+
		if (ctx.abi.os == PKG_OS_FREEBSD) {
+
			char *osversion;
+
			xasprintf(&osversion, "%d", pkg_abi_get_freebsd_osversion(&ctx.abi));
+
			ucl_object_insert_key(config,
+
			    ucl_object_fromstring(osversion), "OSVERSION", 0, true);
+
			free(osversion);
+
		}
	}

	dbg(1, "pkg initialized");
@@ -1431,11 +1497,8 @@ pkg_ini(const char *path, const char *reposdir, pkg_init_flags flags)
	}

out:
-
	free(oi.arch);
-
	free(oi.name);
-
	free(oi.version);
-
	free(oi.version_major);
-
	free(oi.version_minor);
+
	config_parser_vars_free(parser_vars);
+

	return err;

}
modified libpkg/pkg_create.c
@@ -40,6 +40,8 @@
#include "pkg.h"
#include "private/event.h"
#include "private/pkg.h"
+
#include "private/pkg_abi.h"
+
#include "xmalloc.h"

#define TICK	100

@@ -563,14 +565,15 @@ static void
fixup_abi(struct pkg *pkg, const char *rootdir, bool testing)
{
	bool defaultarch = false;
-
	const char *arch;

	/* if no arch autodetermine it */
	if (pkg->abi == NULL) {
-
		if (ctx.oi->ostype == OS_FREEBSD) {
-
			pkg_kv_add(&pkg->annotations, "FreeBSD_version", xstrdup(ctx.oi->str_osversion), "annotation");
+
		if (ctx.abi.os == PKG_OS_FREEBSD) {
+
			char *str_osversion;
+
			xasprintf(&str_osversion, "%d", pkg_abi_get_freebsd_osversion(&ctx.abi));
+
			pkg_kv_add(&pkg->annotations, "FreeBSD_version", str_osversion, "annotation");
		}
-
		pkg->abi = xstrdup(ctx.oi->abi);
+
		pkg->abi = pkg_abi_to_string(&ctx.abi);
		defaultarch = true;
	}

modified libpkg/pkg_elf.c
@@ -23,7 +23,6 @@
#include <sys/stat.h>

#include <assert.h>
-
#include <ctype.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <gelf.h>
@@ -43,8 +42,8 @@

#include "pkg.h"
#include "private/pkg.h"
+
#include "private/pkg_abi.h"
#include "private/event.h"
-
#include "private/elf_tables.h"
#include "private/ldconfig.h"
#include "private/binfmt.h"

@@ -52,6 +51,9 @@
#define NT_ABI_TAG 1
#endif

+
#define NT_VERSION	1
+
#define NT_ARCH		2
+
#define NT_GNU_ABI_TAG	1

/* FFR: when we support installing a 32bit package on a 64bit host */
#define _PATH_ELF32_HINTS       "/var/run/ld-elf32.so.hints"
@@ -60,8 +62,7 @@
#define roundup2(x, y)	(((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */
#endif

-
static const char * elf_corres_to_string(const struct _elf_corres* m, int e);
-
static int elf_string_to_corres(const struct _elf_corres* m, const char *s);
+
static enum pkg_arch elf_parse_arch(Elf *elf, GElf_Ehdr *ehdr);

static int
filter_system_shlibs(const char *name, char *path, size_t pathlen)
@@ -130,79 +131,6 @@ add_shlibs_to_pkg(struct pkg *pkg, const char *fpath, const char *name,
	}
}

-
static bool
-
shlib_valid_abi(const char *fpath, GElf_Ehdr *hdr)
-
{
-
	int semicolon;
-
	const char *p, *t;
-
	char arch[64], wordsize[64];
-
	int wclass;
-
	const char *shlib_arch;
-

-
	/*
-
	 * ALTABI string is in format:
-
	 * <osname>:<osversion>:<arch>:<wordsize>[.other]
-
	 * We need here arch and wordsize only
-
	 */
-
	arch[0] = '\0';
-
	wordsize[0] = '\0';
-
	p = pkg_object_string(pkg_config_get("ABI"));
-
	for(semicolon = 0; semicolon < 3 && p != NULL; semicolon ++, p ++) {
-
		p = strchr(p, ':');
-
		if (p != NULL) {
-
			switch(semicolon) {
-
			case 1:
-
				/* We have arch here */
-
				t = strchr(p + 1, ':');
-
				/* Abi line is likely invalid */
-
				if (t == NULL)
-
					return (true);
-
				strlcpy(arch, p + 1, MIN((long)sizeof(arch), t - p));
-
				break;
-
			case 2:
-
				t = strchr(p + 1, ':');
-
				if (t == NULL)
-
					strlcpy(wordsize, p + 1, sizeof(wordsize));
-
				else
-
					strlcpy(wordsize, p + 1, MIN((long)sizeof(wordsize), t - p));
-
				break;
-
			}
-
		}
-
	}
-
	/* Invalid ABI line */
-
	if (arch[0] == '\0' || wordsize[0] == '\0')
-
		return (true);
-

-
	shlib_arch = elf_corres_to_string(mach_corres, (int)hdr->e_machine);
-
	if (shlib_arch == NULL)
-
		return (true);
-

-
	wclass = elf_string_to_corres(wordsize_corres, wordsize);
-
	if (wclass == -1)
-
		return (true);
-

-

-
	/*
-
	 * Compare wordsize first as the arch for amd64/i386 is an abmiguous
-
	 * 'x86'
-
	 */
-
	if ((int)hdr->e_ident[EI_CLASS] != wclass) {
-
		pkg_debug(1, "not valid elf class for shlib: %s: %s",
-
		    elf_corres_to_string(wordsize_corres,
-
		    (int)hdr->e_ident[EI_CLASS]),
-
		    fpath);
-
		return (false);
-
	}
-

-
	if (!STREQ(shlib_arch, arch)) {
-
		pkg_debug(1, "not valid abi for shlib: %s: %s", shlib_arch,
-
		    fpath);
-
		return (false);
-
	}
-

-
	return (true);
-
}
-

#ifdef __FreeBSD__
static bool
is_old_freebsd_armheader(const GElf_Ehdr *e)
@@ -343,7 +271,7 @@ analyse_elf(struct pkg *pkg, const char *fpath)
		goto cleanup; /* not a dynamically linked elf: no results */
	}

-
	if (!shlib_valid_abi(fpath, &elfhdr)) {
+
	if (elf_parse_arch(e, &elfhdr) != ctx.abi.arch) {
		ret = EPKG_END;
		goto cleanup; /* Invalid ABI */
	}
@@ -450,31 +378,7 @@ analyse_fpath(struct pkg *pkg, const char *fpath)
	return (EPKG_OK);
}

-
static const char *
-
elf_corres_to_string(const struct _elf_corres* m, int e)
-
{
-
	int i = 0;
-

-
	for (i = 0; m[i].string != NULL; i++)
-
		if (m[i].elf_nb == e)
-
			return (m[i].string);
-

-
	return ("unknown");
-
}
-

-
static int
-
elf_string_to_corres(const struct _elf_corres* m, const char *s)
-
{
-
	int i = 0;
-

-
	for (i = 0; m[i].string != NULL; i++)
-
		if (STREQ(m[i].string, s))
-
			return (m[i].elf_nb);
-

-
	return (-1);
-
}
-

-
static const char *
+
static enum pkg_arch
aeabi_parse_arm_attributes(void *data, size_t length)
{
	uint32_t sect_len;
@@ -487,19 +391,19 @@ aeabi_parse_arm_attributes(void *data, size_t length)
} while (0)

	if (length == 0 || *section != 'A')
-
		return (NULL);
+
		return (PKG_ARCH_UNKNOWN);
	MOVE(1);

	/* Read the section length */
	if (length < sizeof(sect_len))
-
		return (NULL);
+
		return (PKG_ARCH_UNKNOWN);
	memcpy(&sect_len, section, sizeof(sect_len));

	/*
	 * The section length should be no longer than the section it is within
	 */
	if (sect_len > length)
-
		return (NULL);
+
		return (PKG_ARCH_UNKNOWN);

	MOVE(sizeof(sect_len));

@@ -510,7 +414,7 @@ aeabi_parse_arm_attributes(void *data, size_t length)
		MOVE(1);
	}
	if (length == 0)
-
		return (NULL);
+
		return (PKG_ARCH_UNKNOWN);
	MOVE(1);

	while (length != 0) {
@@ -520,21 +424,21 @@ aeabi_parse_arm_attributes(void *data, size_t length)
		case 1: /* Tag_File */
			MOVE(1);
			if (length < sizeof(tag_length))
-
				return (NULL);
+
				return (PKG_ARCH_UNKNOWN);
			memcpy(&tag_length, section, sizeof(tag_length));
			break;
		case 2: /* Tag_Section */
		case 3: /* Tag_Symbol */
		default:
-
			return (NULL);
+
			return (PKG_ARCH_UNKNOWN);
		}
		/* At least space for the tag and size */
		if (tag_length <= 5)
-
			return (NULL);
+
			return (PKG_ARCH_UNKNOWN);
		tag_length--;
		/* Check the tag fits */
		if (tag_length > length)
-
			return (NULL);
+
			return (PKG_ARCH_UNKNOWN);

#define	MOVE_TAG(len) do {		\
	assert(tag_length >= (len));	\
@@ -568,21 +472,21 @@ aeabi_parse_arm_attributes(void *data, size_t length)
				 * more than one byte.
				 */
				if (val & (1 << 7))
-
					return (NULL);
+
					return (PKG_ARCH_UNKNOWN);

				/* We have an ARMv4 or ARMv5 */
				if (val <= 5)
-
					return ("arm");
+
					return (PKG_ARCH_UNKNOWN);
				else if (val == 6) /* We have an ARMv6 */
-
					return ("armv6");
+
					return (PKG_ARCH_ARMV6);
				else /* We have an ARMv7+ */
-
					return ("armv7");
+
					return (PKG_ARCH_ARMV7);
			} else if (tag == 4 || tag == 5 || tag == 32 ||
			    tag == 65 || tag == 67) {
				while (*section != '\0' && length != 0)
					MOVE_TAG(1);
				if (tag_length == 0)
-
					return (NULL);
+
					return (PKG_ARCH_UNKNOWN);
				/* Skip the last byte */
				MOVE_TAG(1);
			} else if ((tag >= 7 && tag <= 31) || tag == 34 ||
@@ -592,41 +496,34 @@ aeabi_parse_arm_attributes(void *data, size_t length)
				while (*section & (1 << 7) && length != 0)
					MOVE_TAG(1);
				if (tag_length == 0)
-
					return (NULL);
+
					return (PKG_ARCH_UNKNOWN);
				/* Skip the last byte */
				MOVE_TAG(1);
			} else
-
				return (NULL);
+
				return (PKG_ARCH_UNKNOWN);
#undef MOVE_TAG
		}

		break;
	}
-
	return (NULL);
+
	return (PKG_ARCH_UNKNOWN);
#undef MOVE
}

-
static const char *
-
elf_parse_arch(os_type_t ostype, Elf *elf, GElf_Ehdr *ehdr)
+
static enum pkg_arch
+
elf_parse_arch(Elf *elf, GElf_Ehdr *ehdr)
{
	switch (ehdr->e_machine) {
	case EM_386:
-
		return ("i386");
+
		return (PKG_ARCH_I386);
	case EM_X86_64:
-
		switch (ostype) {
-
		case OS_FREEBSD:
-
			return ("amd64");
-
		case OS_DRAGONFLY:
-
			return ("x86:64");
-
		default:
-
			return ("x86_64");
-
		}
+
		return (PKG_ARCH_AMD64);
	case EM_AARCH64:
-
		return ("aarch64");
+
		return (PKG_ARCH_AARCH64);
	case EM_ARM:
		/* Only support EABI */
		if ((ehdr->e_flags & EF_ARM_EABIMASK) == 0) {
-
			return (NULL);
+
			return (PKG_ARCH_UNKNOWN);
		}

		size_t shstrndx;
@@ -649,38 +546,43 @@ elf_parse_arch(os_type_t ostype, Elf *elf, GElf_Ehdr *ehdr)
		}
		break;
	case EM_PPC:
-
		return ("powerpc");
+
		return (PKG_ARCH_POWERPC);
	case EM_PPC64:
		switch (ehdr->e_ident[EI_DATA]) {
		case ELFDATA2MSB:
-
			return ("powerpc64");
+
			return (PKG_ARCH_POWERPC64);
		case ELFDATA2LSB:
-
			return ("powerpc64le");
+
			return (PKG_ARCH_POWERPC64LE);
		}
		break;
	case EM_RISCV:
		switch (ehdr->e_ident[EI_CLASS]) {
		case ELFCLASS32:
-
			return ("riscv32");
+
			return (PKG_ARCH_RISCV32);
		case ELFCLASS64:
-
			return ("riscv64");
+
			return (PKG_ARCH_RISCV64);
		}
		break;
	}

-
	return (NULL);
+
	return (PKG_ARCH_UNKNOWN);
}

+
/* Returns true if the OS and version were successfully parsed */
static bool
-
elf_note_analyse(Elf_Data *data, GElf_Ehdr *elfhdr, struct os_info *oi)
+
elf_note_analyse(Elf_Data *data, GElf_Ehdr *elfhdr, struct pkg_abi *abi)
{
	Elf_Note note;
	char *src;
	uint32_t gnu_abi_tag[4];
-
	char *note_os[6] = {"Linux", "GNU", "Solaris", "FreeBSD", "NetBSD", "Syllable"};
-
	int note_ost[6] = {OS_LINUX, OS_GNU, OS_SOLARIS, OS_FREEBSD, OS_NETBSD, OS_SYLLABLE};
-
	char *(*pnote_os)[6] = &note_os;
-
	char invalid_osname[] = "Unknown";
+
	int note_ost[6] = {
+
		PKG_OS_LINUX,
+
		PKG_OS_UNKNOWN, /* GNU Hurd */
+
		PKG_OS_UNKNOWN, /* Solaris */
+
		PKG_OS_FREEBSD,
+
		PKG_OS_NETBSD,
+
		PKG_OS_UNKNOWN, /* Syllable */
+
	};
	uint32_t version = 0;
	int version_style = 1;

@@ -709,7 +611,6 @@ elf_note_analyse(Elf_Data *data, GElf_Ehdr *elfhdr, struct os_info *oi)
	if ((uintptr_t)src >= ((uintptr_t)data->d_buf + data->d_size)) {
		return (false);
	}
-
	free(oi->name);
	if (version_style == 2) {
		/*
		 * NT_GNU_ABI_TAG
@@ -733,24 +634,20 @@ elf_note_analyse(Elf_Data *data, GElf_Ehdr *elfhdr, struct os_info *oi)
			}
		}
		if (gnu_abi_tag[0] < 6) {
-
			oi->name = xstrdup((*pnote_os)[gnu_abi_tag[0]]);
-
			oi->ostype = note_ost[gnu_abi_tag[0]];
+
			abi->os= note_ost[gnu_abi_tag[0]];
		} else {
-
			oi->name = xstrdup(invalid_osname);
-
			oi->ostype = OS_UNKNOWN;
+
			abi->os = PKG_OS_UNKNOWN;
		}
	} else {
		if (note.n_namesz == 0) {
-
			oi->name = xstrdup(invalid_osname);
-
			oi->ostype = OS_UNKNOWN;
+
			abi->os = PKG_OS_UNKNOWN;
		} else {
-
			oi->name = xstrdup(src);
			if (STREQ(src, "FreeBSD"))
-
				oi->ostype = OS_FREEBSD;
+
				abi->os = PKG_OS_FREEBSD;
			else if (STREQ(src, "DragonFly"))
-
				oi->ostype = OS_DRAGONFLY;
+
				abi->os = PKG_OS_DRAGONFLY;
			else if (STREQ(src, "NetBSD"))
-
				oi->ostype = OS_NETBSD;
+
				abi->os = PKG_OS_NETBSD;
		}
		src += roundup2(note.n_namesz, 4);
		if (elfhdr->e_ident[EI_DATA] == ELFDATA2MSB)
@@ -759,28 +656,31 @@ elf_note_analyse(Elf_Data *data, GElf_Ehdr *elfhdr, struct os_info *oi)
			version = le32dec(src);
	}

-
	free(oi->version);
	if (version_style == 2) {
-
		if (oi->ostype == OS_LINUX) {
-
			xasprintf(&oi->version, "%d.%d", gnu_abi_tag[1],
-
			    gnu_abi_tag[2]);
+
		if (abi->os == PKG_OS_LINUX) {
+
			abi->major = gnu_abi_tag[1];
+
			abi->minor = gnu_abi_tag[2];
		} else {
-
			xasprintf(&oi->version, "%d.%d.%d", gnu_abi_tag[1],
-
			    gnu_abi_tag[2], gnu_abi_tag[3]);
+
			abi->major = gnu_abi_tag[1];
+
			abi->minor = gnu_abi_tag[2];
+
			abi->patch = gnu_abi_tag[3];
		}
	} else {
-
		if (oi->osversion == 0) {
-
			oi->osversion = version;
-
			snprintf(oi->str_osversion, sizeof(oi->str_osversion), "%d", version);
-
		}
-
		if (oi->ostype == OS_DRAGONFLY) {
-
			xasprintf(&oi->version, "%d.%d", version / 100000, (((version / 100 % 1000)+1)/2)*2);
-
		} else if (oi->ostype == OS_NETBSD) {
-
			xasprintf(&oi->version, "%d", (version + 1000000) / 100000000);
-
		} else {
-
			xasprintf(&oi->version_major, "%d", version / 100000);
-
			xasprintf(&oi->version_minor, "%d", (version / 1000 % 100));
-
			xasprintf(&oi->version, "%d", version / 100000);
+
		switch (abi->os) {
+
		case PKG_OS_UNKNOWN:
+
			break;
+
		case PKG_OS_FREEBSD:
+
			pkg_abi_set_freebsd_osversion(abi, version);
+
			break;
+
		case PKG_OS_DRAGONFLY:
+
			abi->major = version / 100000;
+
			abi->minor = (((version / 100 % 1000)+1)/2)*2;
+
			break;
+
		case PKG_OS_NETBSD:
+
			abi->major = (version + 1000000) / 100000000;
+
			break;
+
		default:
+
			assert(0);
		}
	}

@@ -788,16 +688,16 @@ elf_note_analyse(Elf_Data *data, GElf_Ehdr *elfhdr, struct os_info *oi)
}

int
-
pkg_get_myarch_elfparse(int fd, struct os_info *oi)
+
pkg_elf_abi_from_fd(int fd, struct pkg_abi *abi)
{
+
	*abi = (struct pkg_abi){0};
+

	Elf *elf = NULL;
	GElf_Ehdr elfhdr;
	GElf_Shdr shdr;
	Elf_Data *data;
	Elf_Scn *scn = NULL;
	int ret = EPKG_OK;
-
	char *dest = oi->abi;
-
	size_t sz = sizeof(oi->abi);

	if (elf_version(EV_CURRENT) == EV_NONE) {
		pkg_emit_error("ELF library initialization failed: %s",
@@ -830,25 +730,22 @@ pkg_get_myarch_elfparse(int fd, struct os_info *oi)
			 * loop over all the note section and override what
			 * should be overridden if any
			 */
-
			elf_note_analyse(data, &elfhdr, oi);
+
			elf_note_analyse(data, &elfhdr, abi);
		}
	}

-
	if (oi->name == NULL) {
+
	if (abi->os == PKG_OS_UNKNOWN) {
		ret = EPKG_FATAL;
-
		pkg_emit_error("failed to get the note section");
+
		pkg_emit_error("failed to determine the operating system");
		goto cleanup;
	}

-
	const char *arch = elf_parse_arch(oi->ostype, elf, &elfhdr);
-
	if (arch == NULL) {
+
	abi->arch = elf_parse_arch(elf, &elfhdr);
+
	if (abi->arch == PKG_ARCH_UNKNOWN) {
		ret = EPKG_FATAL;
		pkg_emit_error("failed to determine the architecture");
		goto cleanup;
	}
-
	oi->arch = xstrdup(arch);
-

-
	snprintf(dest, sz, "%s:%s:%s", oi->name, oi->version, oi->arch);

cleanup:
	if (elf != NULL)
modified libpkg/private/binfmt.h
@@ -8,13 +8,12 @@

#include "private/pkg.h"

-
int pkg_get_myarch_elfparse(int fd, struct os_info *oi);
+
int pkg_elf_abi_from_fd(int fd, struct pkg_abi *abi);
int pkg_analyse_init_elf(const char* stage);
int pkg_analyse_elf(const bool developer_mode, struct pkg *pkg, const char *fpath);
int pkg_analyse_close_elf();

-
int pkg_get_myarch_macho(int fd, struct os_info *oi);
+
int pkg_macho_abi_from_fd(int fd, struct pkg_abi *abi, enum pkg_arch arch_hint);
int pkg_analyse_init_macho(const char* stage);
int pkg_analyse_macho(const bool developer_mode, struct pkg *pkg, const char *fpath);
int pkg_analyse_close_macho();
-

deleted libpkg/private/elf_tables.h
@@ -1,59 +0,0 @@
-
/*-
-
 * Copyright (c) 2012 Olivier Houchard <cognet@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.
-
 */
-
#ifndef ELF_TABLES_H_
-
#define ELF_TABLES_H_
-
struct _elf_corres {
-
	int elf_nb;
-
	const char *string;
-
};
-

-
static const struct _elf_corres mach_corres[] = {
-
	{ EM_386, "i386" },
-
	{ EM_X86_64, "x86_64" },
-
	{ EM_AARCH64, "aarch64" },
-
	{ EM_ARM, "arm" },
-
	{ EM_MIPS, "mips" },
-
	{ EM_PPC, "powerpc" },
-
	{ EM_PPC64, "powerpc" },
-
	{ EM_RISCV, "riscv" },
-
	{ EM_SPARCV9, "sparc64" },
-
	{ EM_IA_64, "ia64" },
-
	{ -1, NULL },
-
};
-

-
static const struct _elf_corres wordsize_corres[] = {
-
	{ ELFCLASS32, "32" },
-
	{ ELFCLASS64, "64" },
-
	{ -1, NULL},
-
};
-

-
#define NT_VERSION	1
-
#define NT_ARCH	2
-
#define NT_GNU_ABI_TAG	1
-

-

-

-
#endif /* ELF_TABLES_H_ */
modified libpkg/private/pkg.h
@@ -23,6 +23,7 @@
#include <ucl.h>

#include "xmalloc.h"
+
#include "private/pkg_abi.h"
#include "private/utils.h"
#include "private/fetch.h"
#include "pkghash.h"
@@ -106,18 +107,6 @@ typedef enum {
	IPV6,
} ip_version_t;

-
typedef enum {
-
	OS_UNKNOWN = 0,
-
	OS_DRAGONFLY,
-
	OS_FREEBSD,
-
	OS_GNU,
-
	OS_LINUX,
-
	OS_MACOS,
-
	OS_NETBSD,
-
	OS_SYLLABLE,
-
	OS_SOLARIS,
-
} os_type_t;
-

struct pkg_kvlist {
	kvlist_t *list;
};
@@ -136,19 +125,6 @@ struct pkg_stringlist_iterator {
	void *cur;
};

-
struct os_info {
-
	int osversion;
-
	int ostype;
-
	char *name;
-
	char *version;
-
	char *version_major;
-
	char *version_minor;
-
	char *arch;
-
	char abi[BUFSIZ];
-
	char altabi[BUFSIZ];
-
	char str_osversion[BUFSIZ];
-
};
-

struct pkg_ctx {
	int eventpipe;
	int64_t debug_level;
@@ -175,7 +151,7 @@ struct pkg_ctx {
	bool defer_triggers;
	bool repo_accept_legacy_pkg;
	ip_version_t ip;
-
	struct os_info *oi;
+
	struct pkg_abi abi;
	bool ischrooted;
};

@@ -637,8 +613,6 @@ typedef enum {
	PKG_RC_STOP
} pkg_rc_attr;

-
int pkg_get_myarch_with_legacy(struct os_info *);
-

/**
 * Remove and unregister the package.
 * @param pkg An installed package to delete
added libpkg/private/pkg_abi.h
@@ -0,0 +1,123 @@
+
/*-
+
 * Copyright (c) 2024 The FreeBSD Foundation
+
 *
+
 * This software was developed by Isaac Freund <ifreund@freebsdfoundation.org>
+
 * under sponsorship from the FreeBSD Foundation.
+
 *
+
 * SPDX-License-Identifier: BSD-2-Clause
+
 */
+

+
#pragma once
+

+
#include <stdbool.h>
+
#include <stddef.h>
+
#include <stdint.h>
+

+
/*
+
 * This enum is not intended to contain all operating systems in the universe.
+
 * It is intended to contain only the operating systems for which pkg supports
+
 * ABI detection.
+
 * Adding a new OS to this enum should also add test cases for detection of the
+
 * OS through parsing ELF/Mach-O/etc.
+
 */
+
enum pkg_os {
+
	PKG_OS_UNKNOWN = 0,
+
	PKG_OS_FREEBSD,
+
	PKG_OS_NETBSD,
+
	PKG_OS_DRAGONFLY,
+
	PKG_OS_LINUX,
+
	PKG_OS_MACOS,
+
};
+

+
/*
+
 * Return the canonical string for the given operating system.
+
 */
+
const char *pkg_os_to_string(enum pkg_os os);
+

+
/*
+
 * This enum is not intended to contain all architectures in the universe.
+
 * It is intended to contain only the architectures for which pkg supports
+
 * ABI detection.
+
 * Adding a new architecture to this enum should also test cases for detection
+
 * of the architecture through parsing ELF/Mach-O/etc.
+
 */
+
enum pkg_arch {
+
	PKG_ARCH_UNKNOWN = 0,
+
	PKG_ARCH_I386,
+
	PKG_ARCH_AMD64,
+
	PKG_ARCH_ARMV6,
+
	PKG_ARCH_ARMV7,
+
	PKG_ARCH_AARCH64,
+
	PKG_ARCH_POWERPC,
+
	PKG_ARCH_POWERPC64,
+
	PKG_ARCH_POWERPC64LE,
+
	PKG_ARCH_RISCV32,
+
	PKG_ARCH_RISCV64,
+
};
+

+
/*
+
 * Return the canonical string for the given architecture.
+
 *
+
 * The string used for the arch depends on the OS in some cases.
+
 * For example, "amd64" is used for FreeBSD while "x86_64" is used for Linux
+
 * operating systems though both refer to the same physical architecture.
+
 */
+
const char *pkg_arch_to_string(enum pkg_os os, enum pkg_arch arch);
+

+
struct pkg_abi {
+
	enum pkg_os os;
+

+
	int major;
+
	int minor;
+
	int patch;
+

+
	enum pkg_arch arch;
+
};
+

+
/*
+
 * Attempts to determine the ABI by parsing /usr/bin/uname or /bin/sh.
+
 * If ABI_FILE is set in the environment, that file path is parsed instead.
+
 */
+
int pkg_abi_from_file(struct pkg_abi *abi);
+

+
/*
+
 * Serializes the ABI to a string with format OS:VERSION:ARCH.
+
 *
+
 * The caller is responsible for freeing the returned string.
+
 */
+
char *pkg_abi_to_string(const struct pkg_abi *abi);
+

+
/*
+
 * Validate and parse a string into a pkg_abi struct.
+
 * Returns false if the string is not a complete and valid ABI string
+
 * in the format OS:VERSION:ARCH
+
 */
+
bool pkg_abi_from_string(struct pkg_abi *abi, const char *string);
+

+
/*
+
 * Return true if the canonical ABI string format for the given OS uses only
+
 * the major version rather than both the the major and minor version.
+
 */
+
bool pkg_abi_string_only_major_version(enum pkg_os os);
+

+
/*
+
 * Set the version fields of the provided pkg_abi struct from the
+
 * FreeBSD-specific osversion value.
+
 *
+
 * Asserts that abi->os == PKG_OS_FREEBSD.
+
 *
+
 * See __FreeBSD_version in /usr/include/sys/param.h for a description
+
 * of the format.
+
 */
+
void pkg_abi_set_freebsd_osversion(struct pkg_abi *abi, int osversion);
+

+
/*
+
 * Returns the FreeBSD-specific osversion value derived from the
+
 * major/minor/patch fields of the provided pkg_abi struct.
+
 *
+
 * Asserts that abi->os == PKG_OS_FREEBSD.
+
 *
+
 * See __FreeBSD_version in /usr/include/sys/param.h for a description
+
 * of the format.
+
 */
+
int pkg_abi_get_freebsd_osversion(struct pkg_abi *abi);
modified libpkg/utils.c
@@ -55,6 +55,7 @@
#include "pkg.h"
#include "pkgvec.h"
#include "private/event.h"
+
#include "private/pkg_abi.h"
#include "private/utils.h"
#include "private/pkg.h"
#include "xmalloc.h"
@@ -351,7 +352,7 @@ check_for_hardlink(hardlinks_t *hl, struct stat *st)
bool
is_valid_abi(const char *testabi, bool emit_error)
{
-
	const char *abi = ctx.oi->abi;
+
	const char *abi = pkg_object_string(pkg_config_get("ABI"));

	if (strncasecmp(testabi, abi, strlen(testabi)) != 0 &&
	    fnmatch(testabi, abi, FNM_CASEFOLD) == FNM_NOMATCH) {
@@ -367,11 +368,10 @@ is_valid_abi(const char *testabi, bool emit_error)
bool
is_valid_os_version(struct pkg *pkg)
{
-
	if (ctx.oi->ostype != OS_FREEBSD)
+
	if (ctx.abi.os != PKG_OS_FREEBSD)
		return (true);
	const char *fbsd_version;
	const char *errstr = NULL;
-
	int fbsdver;
	char query_buf[512];
	/* -1: not checked, 0: not allowed, 1: allowed */
	static int osver_mismatch_allowed = -1;
@@ -380,14 +380,15 @@ is_valid_os_version(struct pkg *pkg)
	if (pkg_object_bool(pkg_config_get("IGNORE_OSVERSION")))
		return (true);
	if ((fbsd_version = pkg_kv_get(&pkg->annotations, "FreeBSD_version")) != NULL) {
-
		fbsdver = strtonum(fbsd_version, 1, INT_MAX, &errstr);
+
		int pkg_osversion = strtonum(fbsd_version, 1, INT_MAX, &errstr);
		if (errstr != NULL) {
			pkg_emit_error("Invalid FreeBSD version %s for package %s",
			    fbsd_version, pkg->name);
			return (false);
		}
-
		if (fbsdver > ctx.oi->osversion) {
-
			if (fbsdver - ctx.oi->osversion < 100000) {
+
		int abi_osversion = pkg_abi_get_freebsd_osversion(&ctx.abi);
+
		if (pkg_osversion > abi_osversion) {
+
			if (pkg_osversion - abi_osversion < 100000) {
				/* Negligible difference, ask user to enforce */
				if (osver_mismatch_allowed == -1) {
					snprintf(query_buf, sizeof(query_buf),
@@ -396,7 +397,7 @@ is_valid_os_version(struct pkg *pkg)
							"- package: %d\n"
							"- running userland: %d\n"
							"Ignore the mismatch and continue? ", pkg->name,
-
							fbsdver, ctx.oi->osversion);
+
							pkg_osversion, abi_osversion);
					ret = pkg_emit_query_yesno(false, query_buf);
					osver_mismatch_allowed = ret;
				}
@@ -409,7 +410,7 @@ is_valid_os_version(struct pkg *pkg)
					"- package: %d\n"
					"- running kernel: %d\n",
					pkg->name,
-
					fbsdver, ctx.oi->osversion);
+
					pkg_osversion, abi_osversion);
				return (false);
			}
		}
modified libpkg/xmalloc.h
@@ -3,6 +3,7 @@

#include <stdarg.h>
#include <stdio.h>
+
#include <stdlib.h>
#include <string.h>

static inline void *xmalloc(size_t size)
modified scripts/completion/_pkg.in
@@ -109,7 +109,6 @@ _pkg_config_opts() {
		'ABI[ABI of package you want to install]:string' \
		'ALIAS[define local aliases for various pkg(8) standard command lines]:key/value list' \
		'ALLOW_BASE_SHLIBS[enable base libraries analysis]:boolean:(yes no)' \
-
		'ALTABI[override the automatically detected old-form ABI]:string' \
		'AUTOCLEAN[cleanout content of cache directory after upgrades or installations]:boolean:(yes no)' \
		'AUTOMERGE[automatically merge configuration files]:boolean:(yes no)' \
		'DEFAULT_ALWAYS_YES[default to "yes" for all questions requiring user confirmation]:boolean:(yes no)' \
modified tests/frontend/abi.sh
@@ -49,13 +49,13 @@ override_body() {
	atf_check \
		-o inline:"${_expected}" \
		-e ignore \
-
		pkg -o ABI=FreeBSD:12:powerpc config abi
+
		pkg -o ABI=FreeBSD:12:powerpc -o OSVERSION=1201000 config abi

	_expected="freebsd:12:powerpc:32:eb\n"
	atf_check \
		-o inline:"${_expected}" \
		-e ignore \
-
		pkg -o ABI=FreeBSD:12:powerpc config altabi
+
		pkg -o ABI=FreeBSD:12:powerpc -o OSVERSION=1201000 config altabi
}

elfparse_body() {
@@ -105,27 +105,27 @@ machoparse_body() {
	atf_check \
		-s exit:1 \
		-o inline:"${_expected}" \
-
		-e match:"Unable to determine ABI" \
+
		-e match:"Unable to determine the ABI" \
		pkg -o IGNORE_OSMAJOR=1 -o ABI_FILE=$(atf_get_srcdir)/macosfat.bin#i386 config abi

	atf_check \
		-s exit:1 \
		-o inline:"${_expected}" \
-
		-e match:"Unable to determine ABI" \
+
		-e match:"Unable to determine the ABI" \
		pkg -o IGNORE_OSMAJOR=1 -o ABI_FILE=$(atf_get_srcdir)/macosfat.bin#i386 config altabi

	# explicitely select a fat entry that is not a valid architecture, hence not in the ABI_FILE
-
	_expected="Scanned 2 entries, found none matching selector abc\n"
+
	_expected=""
	atf_check \
		-s exit:1 \
		-o inline:"${_expected}" \
-
		-e match:"Unable to determine ABI" \
+
		-e match:"Invalid ABI_FILE architecture hint abc" \
		pkg -o IGNORE_OSMAJOR=1 -o ABI_FILE=$(atf_get_srcdir)/macosfat.bin#abc config abi

	atf_check \
		-s exit:1 \
		-o inline:"${_expected}" \
-
		-e match:"Unable to determine ABI" \
+
		-e match:"Invalid ABI_FILE architecture hint abc" \
		pkg -o IGNORE_OSMAJOR=1 -o ABI_FILE=$(atf_get_srcdir)/macosfat.bin#abc config altabi

	# if the binary is not universal, selecting the first entry is not commentable
@@ -141,18 +141,18 @@ machoparse_body() {
		-e not-match:"picking first" \
		pkg -d -o IGNORE_OSMAJOR=1 -o ABI_FILE=$(atf_get_srcdir)/macos.bin config altabi

-
	_expected="Scanned 1 entry, found none matching selector abc\n"
+
	_expected="Scanned 1 entry, found none matching selector i386\n"
	atf_check \
		-s exit:1 \
		-o inline:"${_expected}" \
-
		-e match:"Unable to determine ABI" \
-
		pkg -d -o IGNORE_OSMAJOR=1 -o ABI_FILE=$(atf_get_srcdir)/macos.bin#abc config abi
+
		-e match:"Unable to determine the ABI" \
+
		pkg -d -o IGNORE_OSMAJOR=1 -o ABI_FILE=$(atf_get_srcdir)/macos.bin#i386 config abi

	atf_check \
		-s exit:1 \
		-o inline:"${_expected}" \
-
		-e match:"Unable to determine ABI" \
-
		pkg -d -o IGNORE_OSMAJOR=1 -o ABI_FILE=$(atf_get_srcdir)/macos.bin#abc config altabi
+
		-e match:"Unable to determine the ABI" \
+
		pkg -d -o IGNORE_OSMAJOR=1 -o ABI_FILE=$(atf_get_srcdir)/macos.bin#i386 config altabi

	# if the binary is universal, selecting the first entry is to be commented
	_expected="Darwin:17:amd64\n"
modified tests/frontend/pkg.sh
@@ -21,15 +21,6 @@ pkg_no_database_body() {

pkg_config_defaults_body()
{
-
	case "${OS}" in
-
	FreeBSD|DragonFly)
-
		MATCH_ALTABI='^ *ALTABI = "[a-zA-Z0-9]+:[a-z\.A-Z0-9]+:[a-zA-Z0-9]+:[a-zA-Z0-9:]+";$'
-
		;;
-
	*)
-
		MATCH_ALTABI='^ *ALTABI = "[a-zA-Z0-9]+:[a-z\.A-Z0-9]+:[a-zA-Z0-9_]+;$'
-
		;;
-
	esac
-

	atf_check \
	    -o match:'^ *PKG_DBDIR = "/var/db/pkg";$' \
	    -o match:'^ *PKG_CACHEDIR = "/var/cache/pkg";$' \
@@ -39,7 +30,7 @@ pkg_config_defaults_body()
	    -o match:'^ *ASSUME_ALWAYS_YES = false;$' \
	    -o match:'^ *PLIST_KEYWORDS_DIR = "";$' \
	    -o match:'^ *SYSLOG = true;$' \
-
	    -o match:"${MATCH_ABI}" \
+
	    -o match:'^ *ABI = "[a-zA-Z0-9]+:[a-z\.A-Z0-9]+:[a-zA-Z0-9]+";$'\
	    -o match:'^ *DEVELOPER_MODE = false;$' \
	    -o match:'^ *VULNXML_SITE = "https://vuxml.freebsd.org/freebsd/vuln.xml.xz";$' \
	    -o match:'^ *FETCH_RETRY = 3;$' \
modified tests/lib/pkg_elf.c
@@ -10,6 +10,7 @@

#include <atf-c.h>
#include <private/pkg.h>
+
#include <private/pkg_abi.h>
#include <private/binfmt.h>
#include <xstring.h>
#include <tllist.h>
@@ -48,6 +49,8 @@ ATF_TC_BODY(analyse_elf, tc)
	struct pkg *p = NULL;
	char *binpath = NULL;

+
	ctx.abi.arch = PKG_ARCH_AMD64;
+

	xasprintf(&binpath, "%s/frontend/libtestfbsd.so.1", atf_tc_get_config_var(tc, "srcdir"));

	ATF_REQUIRE_EQ(EPKG_OK, pkg_new(&p, PKG_INSTALLED));