diff --git a/libpkg/pkg.c b/libpkg/pkg.c index f9fcf8742..6c11b7e07 100644 --- a/libpkg/pkg.c +++ b/libpkg/pkg.c @@ -22,6 +22,7 @@ #include "private/pkg.h" #include "private/pkgdb.h" #include "private/utils.h" +#include "xmalloc.h" #define dbg(x, ...) pkg_dbg(PKG_DBG_PACKAGE, x, __VA_ARGS__) @@ -898,34 +899,99 @@ pkg_addoption_description(struct pkg *pkg, const char *key, return (EPKG_OK); } +enum pkg_shlib_flags +pkg_shlib_flags_from_abi(const struct pkg_abi *shlib_abi) +{ + enum pkg_shlib_flags flags = PKG_SHLIB_FLAGS_NONE; + + if (ctx.abi.os == PKG_OS_FREEBSD) { + if (shlib_abi->os == PKG_OS_LINUX) { + flags |= PKG_SHLIB_FLAGS_COMPAT_LINUX; + } + + switch (ctx.abi.arch) { + case PKG_ARCH_AMD64: + if (shlib_abi->arch == PKG_ARCH_I386) { + flags |= PKG_SHLIB_FLAGS_COMPAT_32; + } + break; + case PKG_ARCH_AARCH64: + if (shlib_abi->arch == PKG_ARCH_ARMV7) { + flags |= PKG_SHLIB_FLAGS_COMPAT_32; + } + break; + case PKG_ARCH_POWERPC64: + if (shlib_abi->arch == PKG_ARCH_POWERPC) { + flags |= PKG_SHLIB_FLAGS_COMPAT_32; + } + break; + } + } + + return (flags); +} + +/* + * Format examples: + * + * libfoo.so.1.0.0 - native + * libfoo.so.1.0.0:32 - compat 32 + * libfoo.so.1.0.0:Linux - compat Linux + * libfoo.so.1.0.0:Linux:32 - compat Linux 32 + */ +static char * +pkg_shlib_name_with_flags(const char *name, enum pkg_shlib_flags flags) +{ + const char *compat_os = ""; + if ((flags & PKG_SHLIB_FLAGS_COMPAT_LINUX) != 0) { + compat_os = ":Linux"; + } + + const char *compat_arch = ""; + if ((flags & PKG_SHLIB_FLAGS_COMPAT_32) != 0) { + compat_arch = ":32"; + } + + char *ret; + xasprintf(&ret, "%s%s%s", name, compat_os, compat_arch); + return (ret); +} + int -pkg_addshlib_required(struct pkg *pkg, const char *name) +pkg_addshlib_required(struct pkg *pkg, const char *name, + enum pkg_shlib_flags flags) { assert(pkg != NULL); assert(name != NULL && name[0] != '\0'); - if (match_ucl_lists(name, + char *full_name = pkg_shlib_name_with_flags(name, flags); + + if (match_ucl_lists(full_name, pkg_config_get("SHLIB_REQUIRE_IGNORE_GLOB"), pkg_config_get("SHLIB_REQUIRE_IGNORE_REGEX"))) { - dbg(3, "ignoring shlib %s required by package %s", name, pkg->name); + dbg(3, "ignoring shlib %s required by package %s", full_name, pkg->name); + free(full_name); return (EPKG_OK); } /* silently ignore duplicates in case of shlibs */ tll_foreach(pkg->shlibs_required, s) { - if (STREQ(s->item, name)) + if (STREQ(s->item, full_name)) { + free(full_name); return (EPKG_OK); + } } - tll_push_back(pkg->shlibs_required, xstrdup(name)); + tll_push_back(pkg->shlibs_required, full_name); - dbg(3, "added shlib deps for %s on %s", pkg->name, name); + dbg(3, "added shlib deps for %s on %s", pkg->name, full_name); return (EPKG_OK); } int -pkg_addshlib_provided(struct pkg *pkg, const char *name) +pkg_addshlib_provided(struct pkg *pkg, const char *name, + enum pkg_shlib_flags flags) { assert(pkg != NULL); assert(name != NULL && name[0] != '\0'); @@ -934,15 +1000,19 @@ pkg_addshlib_provided(struct pkg *pkg, const char *name) if (strncmp(name, "lib", 3) != 0) return (EPKG_OK); + char *full_name = pkg_shlib_name_with_flags(name, flags); + /* silently ignore duplicates in case of shlibs */ tll_foreach(pkg->shlibs_provided, s) { - if (STREQ(s->item, name)) + if (STREQ(s->item, full_name)) { + free(full_name); return (EPKG_OK); + } } - tll_push_back(pkg->shlibs_provided, xstrdup(name)); + tll_push_back(pkg->shlibs_provided, full_name); - dbg(3, "added shlib provide %s for %s", name, pkg->name); + dbg(3, "added shlib provide %s for %s", full_name, pkg->name); return (EPKG_OK); } diff --git a/libpkg/pkg_abi_macho.c b/libpkg/pkg_abi_macho.c index bb3548cb7..64189f87e 100644 --- a/libpkg/pkg_abi_macho.c +++ b/libpkg/pkg_abi_macho.c @@ -381,9 +381,9 @@ analyse_macho(int fd, struct pkg *pkg) xasprintf(&lib_with_version, "%s-%"PRIuFAST16".%"PRIuFAST16, basename, dylib->current_version.major, dylib->current_version.minor); } if (LC_ID_DYLIB == loadcmd) { - pkg_addshlib_provided(pkg, lib_with_version); + pkg_addshlib_provided(pkg, lib_with_version, PKG_SHLIB_FLAGS_NONE); } else { - pkg_addshlib_required(pkg, lib_with_version); + pkg_addshlib_required(pkg, lib_with_version, PKG_SHLIB_FLAGS_NONE); } free(lib_with_version); free(dylib); diff --git a/libpkg/pkg_elf.c b/libpkg/pkg_elf.c index 416da5ee3..644aa917a 100644 --- a/libpkg/pkg_elf.c +++ b/libpkg/pkg_elf.c @@ -58,28 +58,7 @@ #define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */ #endif -static enum pkg_arch elf_parse_arch(Elf *elf, GElf_Ehdr *ehdr); - -#ifdef __FreeBSD__ -static bool -is_old_freebsd_armheader(const GElf_Ehdr *e) -{ - GElf_Word eabi; - - /* - * Old FreeBSD arm EABI binaries were created with zeroes in [EI_OSABI]. - * Attempt to identify them by the little bit of valid info that is - * present: 32-bit ARM with EABI version 4 or 5 in the flags. OABI - * binaries (prior to freebsd 10) have the correct [EI_OSABI] value. - */ - if (e->e_machine == EM_ARM && e->e_ident[EI_CLASS] == ELFCLASS32) { - eabi = e->e_flags & 0xff000000; - if (eabi == 0x04000000 || eabi == 0x05000000) - return (true); - } - return (false); -} -#endif +static void elf_parse_abi(Elf *elf, GElf_Ehdr *ehdr, struct pkg_abi *abi); #ifndef HAVE_ELF_NOTE typedef Elf32_Nhdr Elf_Note; @@ -142,9 +121,8 @@ analyse_elf(struct pkg *pkg, const char *fpath) goto cleanup; } - /* Elf file has sections header */ + /* Parse the needed information from the dynamic section header */ Elf_Scn *scn = NULL; - Elf_Scn *note = NULL; Elf_Scn *dynamic = NULL; size_t numdyn = 0; size_t sh_link = 0; @@ -156,20 +134,7 @@ analyse_elf(struct pkg *pkg, const char *fpath) elf_errmsg(-1)); goto cleanup; } - switch (shdr.sh_type) { - case SHT_NOTE:; - Elf_Data *data = elf_getdata(scn, NULL); - if (data == NULL) { - ret = EPKG_END; /* Some error occurred, ignore this file */ - goto cleanup; - } - if (data->d_buf != NULL) { - Elf_Note *en = (Elf_Note *)data->d_buf; - if (en->n_type == NT_ABI_TAG) - note = scn; - } - break; - case SHT_DYNAMIC: + if (shdr.sh_type == SHT_DYNAMIC) { dynamic = scn; sh_link = shdr.sh_link; if (shdr.sh_entsize == 0) { @@ -179,32 +144,31 @@ analyse_elf(struct pkg *pkg, const char *fpath) numdyn = shdr.sh_size / shdr.sh_entsize; break; } - - if (note != NULL && dynamic != NULL) - break; } - /* - * note == NULL usually means a shared object for use with dlopen(3) - * dynamic == NULL means not a dynamically linked elf - */ if (dynamic == NULL) { ret = EPKG_END; goto cleanup; /* not a dynamically linked elf: no results */ } - if (elf_parse_arch(e, &elfhdr) != ctx.abi.arch) { + /* A shared object for use with dlopen(3) may lack a NOTE section and + will therefore have unknown elf_abi.os. */ + struct pkg_abi elf_abi; + elf_parse_abi(e, &elfhdr, &elf_abi); + if (elf_abi.os == PKG_OS_UNKNOWN || elf_abi.arch == PKG_ARCH_UNKNOWN) { ret = EPKG_END; - goto cleanup; /* Invalid ABI */ + goto cleanup; } -#ifdef __FreeBSD__ - if (ctx.abi.os == PKG_OS_FREEBSD && elfhdr.e_ident[EI_OSABI] != ELFOSABI_FREEBSD && - !is_old_freebsd_armheader(&elfhdr)) { + enum pkg_shlib_flags flags = pkg_shlib_flags_from_abi(&elf_abi); + if ((flags & PKG_SHLIB_FLAGS_COMPAT_LINUX) == 0 && elf_abi.os != ctx.abi.os) { ret = EPKG_END; - goto cleanup; + goto cleanup; /* Incompatible OS */ + } + if ((flags & PKG_SHLIB_FLAGS_COMPAT_32) == 0 && elf_abi.arch != ctx.abi.arch) { + ret = EPKG_END; + goto cleanup; /* Incompatible architecture */ } -#endif Elf_Data *data = elf_getdata(dynamic, NULL); if (data == NULL) { @@ -226,10 +190,12 @@ analyse_elf(struct pkg *pkg, const char *fpath) continue; } + + if (dyn->d_tag == DT_SONAME) { - pkg_addshlib_provided(pkg, shlib); + pkg_addshlib_provided(pkg, shlib, flags); } else if (dyn->d_tag == DT_NEEDED) { - pkg_addshlib_required(pkg, shlib); + pkg_addshlib_required(pkg, shlib, flags); } } diff --git a/libpkg/pkg_manifest.c b/libpkg/pkg_manifest.c index 37acc8bf3..7cfe3f0d1 100644 --- a/libpkg/pkg_manifest.c +++ b/libpkg/pkg_manifest.c @@ -427,13 +427,13 @@ pkg_array(struct pkg *pkg, const ucl_object_t *obj, uint32_t attr) if (cur->type != UCL_STRING) pkg_emit_error("Skipping malformed required shared library"); else - pkg_addshlib_required(pkg, ucl_object_tostring(cur)); + pkg_addshlib_required(pkg, ucl_object_tostring(cur), PKG_SHLIB_FLAGS_NONE); break; case MANIFEST_SHLIBS_PROVIDED: if (cur->type != UCL_STRING) pkg_emit_error("Skipping malformed provided shared library"); else - pkg_addshlib_provided(pkg, ucl_object_tostring(cur)); + pkg_addshlib_provided(pkg, ucl_object_tostring(cur), PKG_SHLIB_FLAGS_NONE); break; case MANIFEST_CONFLICTS: if (cur->type != UCL_STRING) diff --git a/libpkg/pkgdb_iterator.c b/libpkg/pkgdb_iterator.c index a743ac08d..7e8d236d6 100644 --- a/libpkg/pkgdb_iterator.c +++ b/libpkg/pkgdb_iterator.c @@ -585,6 +585,13 @@ pkgdb_load_group(sqlite3 *sqlite, struct pkg *pkg) return (ret); } +static int +addshlib_required_raw(struct pkg *pkg, const char *name) +{ + tll_push_back(pkg->shlibs_required, xstrdup(name)); + return (EPKG_OK); +} + static int pkgdb_load_shlib_required(sqlite3 *sqlite, struct pkg *pkg) { @@ -598,9 +605,15 @@ pkgdb_load_shlib_required(sqlite3 *sqlite, struct pkg *pkg) assert(pkg != NULL); return (load_val(sqlite, pkg, sql, PKG_LOAD_SHLIBS_REQUIRED, - pkg_addshlib_required, PKG_ATTR_SHLIBS_REQUIRED)); + addshlib_required_raw, PKG_ATTR_SHLIBS_REQUIRED)); } +static int +addshlib_provided_raw(struct pkg *pkg, const char *name) +{ + tll_push_back(pkg->shlibs_provided, xstrdup(name)); + return (EPKG_OK); +} static int pkgdb_load_shlib_provided(sqlite3 *sqlite, struct pkg *pkg) @@ -615,7 +628,7 @@ pkgdb_load_shlib_provided(sqlite3 *sqlite, struct pkg *pkg) assert(pkg != NULL); return (load_val(sqlite, pkg, sql, PKG_LOAD_SHLIBS_PROVIDED, - pkg_addshlib_provided, PKG_SHLIBS_PROVIDED)); + addshlib_provided_raw, PKG_SHLIBS_PROVIDED)); } static int diff --git a/libpkg/private/pkg.h b/libpkg/private/pkg.h index 79db5c1da..c0dd1543b 100644 --- a/libpkg/private/pkg.h +++ b/libpkg/private/pkg.h @@ -792,8 +792,19 @@ int pkg_kv_add(kvlist_t *kv, const char *key, const char *value, const char *tit const char *pkg_kv_get(const kvlist_t *kv, const char *key); int pkg_adduser(struct pkg *pkg, const char *name); int pkg_addgroup(struct pkg *pkg, const char *group); -int pkg_addshlib_required(struct pkg *pkg, const char *name); -int pkg_addshlib_provided(struct pkg *pkg, const char *name); + +enum pkg_shlib_flags { + PKG_SHLIB_FLAGS_NONE = 0, + PKG_SHLIB_FLAGS_COMPAT_32 = 1 << 0, + PKG_SHLIB_FLAGS_COMPAT_LINUX = 1 << 1, +}; +/* Determine shlib flags by comparing the shlib abi with ctx.abi */ +enum pkg_shlib_flags pkg_shlib_flags_from_abi(const struct pkg_abi *shlib_abi); +int pkg_addshlib_required(struct pkg *pkg, const char *name, enum pkg_shlib_flags); +/* No checking for duplicates or filtering */ +int pkg_addshlib_required_raw(struct pkg *pkg, const char *name); +int pkg_addshlib_provided(struct pkg *pkg, const char *name, enum pkg_shlib_flags); + int pkg_addconflict(struct pkg *pkg, const char *name); int pkg_addprovide(struct pkg *pkg, const char *name); int pkg_addrequire(struct pkg *pkg, const char *name); diff --git a/tests/frontend/test_environment.sh.in b/tests/frontend/test_environment.sh.in index f02a5e041..f05237933 100755 --- a/tests/frontend/test_environment.sh.in +++ b/tests/frontend/test_environment.sh.in @@ -111,9 +111,7 @@ bin_meta() { XABI=FreeBSD:14:riscv64 XALTABI=freebsd:14:riscv:64:hf XFreeBSD_version=1401000 -# This riscv64 binary does not have the OS set to FreeBSD in its ELF header -# TODO: handle this in pkg_elf.c -# Xshlibs_required="libc.so.7" + Xshlibs_required="libc.so.7" ;; *dfly.bin) XABI=dragonfly:5.10:x86:64 diff --git a/tests/lib/pkg_elf.c b/tests/lib/pkg_elf.c index 6af751f66..f4e82b486 100644 --- a/tests/lib/pkg_elf.c +++ b/tests/lib/pkg_elf.c @@ -15,6 +15,7 @@ #include #include #include +#include "private/event.h" #ifndef __unused # ifdef __GNUC__ @@ -49,6 +50,7 @@ ATF_TC_BODY(analyse_elf, tc) struct pkg *p = NULL; char *binpath = NULL; + ctx.abi.os = PKG_OS_FREEBSD; ctx.abi.arch = PKG_ARCH_AMD64; xasprintf(&binpath, "%s/frontend/libtestfbsd.so.1", atf_tc_get_config_var(tc, "srcdir"));