diff --git a/libpkg/elfhints.c b/libpkg/elfhints.c index 2c30c0ea33..dc041291a7 100644 --- a/libpkg/elfhints.c +++ b/libpkg/elfhints.c @@ -28,6 +28,7 @@ */ #include +#include "private/pkg_abi.h" #include #include #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, 1); return (scan_dirs_for_shlibs(&shlibs, ndirs, dirs, true)); diff --git a/libpkg/pkg_abi.c b/libpkg/pkg_abi.c index 9b4782128a..6a9f1629d5 100644 --- a/libpkg/pkg_abi.c +++ b/libpkg/pkg_abi.c @@ -1,28 +1,12 @@ /*- * Copyright (c) 2011-2012 Baptiste Daroussin * Copyright (c) 2012-2013 Matthew Seaman - * 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 + * 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 #include +#include #include #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 the 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; @@ -152,32 +398,6 @@ pkg_get_myarch_fromfile(struct os_info *oi) return ret; } -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) { diff --git a/libpkg/pkg_abi_macho.c b/libpkg/pkg_abi_macho.c index f24d13970b..097e492be8 100644 --- a/libpkg/pkg_abi_macho.c +++ b/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; - // we can change the content of oi->abi freely - char *abisep = oi->abi; - /*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,12 +168,14 @@ 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 %d entr%s, found none matching selector %s", - mf->narch, mf->narch > 1 ? "ies" : "y", archname); - return 0; + 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 %d entries in universal binary, picking first", mf->narch); @@ -191,15 +188,17 @@ match_entry(macho_file_t *mf, struct os_info *oi) * 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; @@ -210,7 +209,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; @@ -277,31 +276,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, "%d.%d.%d", 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, "%d.%d", darwin.major, - darwin.minor); + ret = EPKG_OK; } - free(oi->version_major); - xasprintf(&oi->version_major, "%d", darwin.major); - free(oi->version_minor); - xasprintf(&oi->version_minor, "%d", 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)); - // 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."); } diff --git a/libpkg/pkg_config.c b/libpkg/pkg_config.c index 76611c5b1f..8e3ac28ebc 100644 --- a/libpkg/pkg_config.c +++ b/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; @@ -158,16 +157,6 @@ static struct config_entry c[] = { "SYSLOG", "YES", }, - { - PKG_STRING, - "ABI", - oi.abi, - }, - { - PKG_STRING, - "ALTABI", - oi.altabi, - }, { PKG_BOOL, "DEVELOPER_MODE", @@ -379,11 +368,6 @@ static struct config_entry c[] = { NULL, }, #ifdef __FreeBSD__ - { - PKG_INT, - "OSVERSION", - oi.str_osversion, - }, { PKG_BOOL, "IGNORE_OSVERSION", @@ -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,36 +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) { - pkg_get_myarch_with_legacy(&oi); - } 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; @@ -1150,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; @@ -1201,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; @@ -1332,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"); @@ -1429,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; } diff --git a/libpkg/pkg_create.c b/libpkg/pkg_create.c index 37eaf7acf0..10d683ce2f 100644 --- a/libpkg/pkg_create.c +++ b/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; } diff --git a/libpkg/pkg_elf.c b/libpkg/pkg_elf.c index ee943410bb..ab6270ad36 100644 --- a/libpkg/pkg_elf.c +++ b/libpkg/pkg_elf.c @@ -23,7 +23,6 @@ #include #include -#include #include #include #include @@ -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: - * :::[.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(§_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] = ¬e_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) diff --git a/libpkg/private/binfmt.h b/libpkg/private/binfmt.h index 1775c0f1c7..fda509a3e3 100644 --- a/libpkg/private/binfmt.h +++ b/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(); - diff --git a/libpkg/private/elf_tables.h b/libpkg/private/elf_tables.h deleted file mode 100644 index dbe8ebab94..0000000000 --- a/libpkg/private/elf_tables.h +++ /dev/null @@ -1,59 +0,0 @@ -/*- - * Copyright (c) 2012 Olivier Houchard - * 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_ */ diff --git a/libpkg/private/pkg.h b/libpkg/private/pkg.h index d57dc7b957..1b53eb51a0 100644 --- a/libpkg/private/pkg.h +++ b/libpkg/private/pkg.h @@ -23,6 +23,7 @@ #include #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 diff --git a/libpkg/private/pkg_abi.h b/libpkg/private/pkg_abi.h new file mode 100644 index 0000000000..84ebc4e286 --- /dev/null +++ b/libpkg/private/pkg_abi.h @@ -0,0 +1,123 @@ +/*- + * Copyright (c) 2024 The FreeBSD Foundation + * + * This software was developed by Isaac Freund + * under sponsorship from the FreeBSD Foundation. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +/* + * 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); diff --git a/libpkg/utils.c b/libpkg/utils.c index 71c18da512..98cabb96d5 100644 --- a/libpkg/utils.c +++ b/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); } } diff --git a/libpkg/xmalloc.h b/libpkg/xmalloc.h index 623a02ae3f..ba77d32806 100644 --- a/libpkg/xmalloc.h +++ b/libpkg/xmalloc.h @@ -3,6 +3,7 @@ #include #include +#include #include static inline void *xmalloc(size_t size) diff --git a/scripts/completion/_pkg.in b/scripts/completion/_pkg.in index dcad75a42c..6a13055440 100644 --- a/scripts/completion/_pkg.in +++ b/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)' \ diff --git a/tests/frontend/abi.sh b/tests/frontend/abi.sh index e2c712f93f..d55158a975 100644 --- a/tests/frontend/abi.sh +++ b/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() { @@ -241,27 +241,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 @@ -277,18 +277,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:24:amd64\n" diff --git a/tests/frontend/pkg.sh b/tests/frontend/pkg.sh index 46ead39624..ef0115107b 100755 --- a/tests/frontend/pkg.sh +++ b/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;$' \ diff --git a/tests/lib/pkg_elf.c b/tests/lib/pkg_elf.c index ef38d07fff..39aff5aaec 100644 --- a/tests/lib/pkg_elf.c +++ b/tests/lib/pkg_elf.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -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));