Skip to content

Commit

Permalink
libpkg: track lib32 and Linuxulator shlibs
Browse files Browse the repository at this point in the history
When scanning ELF files to generate the shlibs_provided/required lists,
pkg now generates entries for lib32 and Linux compat shlibs rather than
ignoring all non-native shlibs.

The following format is used for shlibs_provided/required entries:

libfoo.so.1.0.0          - native (no change to status quo)
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

This is only done if targeting FreeBSD for now.

References:	freebsd#2331
Sponsored by:	The FreeBSD Foundation
  • Loading branch information
ifreund committed Dec 11, 2024
1 parent 2194150 commit 0e628a4
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 71 deletions.
90 changes: 80 additions & 10 deletions libpkg/pkg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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__)

Expand Down Expand Up @@ -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');
Expand All @@ -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);
}
Expand Down
4 changes: 2 additions & 2 deletions libpkg/pkg_abi_macho.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
70 changes: 20 additions & 50 deletions libpkg/pkg_elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,26 +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);

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);
}
static void elf_parse_abi(Elf *elf, GElf_Ehdr *ehdr, struct pkg_abi *abi);

#ifndef HAVE_ELF_NOTE
typedef Elf32_Nhdr Elf_Note;
Expand Down Expand Up @@ -140,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;
Expand All @@ -154,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) {
Expand All @@ -177,29 +144,30 @@ 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;
}

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 */
}

Elf_Data *data = elf_getdata(dynamic, NULL);
Expand All @@ -222,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);
}
}

Expand Down
4 changes: 2 additions & 2 deletions libpkg/pkg_manifest.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
17 changes: 15 additions & 2 deletions libpkg/pkgdb_iterator.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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)
Expand All @@ -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
Expand Down
15 changes: 13 additions & 2 deletions libpkg/private/pkg.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 1 addition & 3 deletions tests/frontend/test_environment.sh.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions tests/lib/pkg_elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,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"));
Expand Down

0 comments on commit 0e628a4

Please sign in to comment.