Skip to content

Commit 96fd2fc

Browse files
committed
Enhance requires with version information from the build root.
The --libtool-version-fallback option will cause elfdeps to try to use the version information in the shared object's filename for shared objects that don't provide versioned symbols. This additional information allows rpm to track minor-version dependencies.
1 parent 1ca57c6 commit 96fd2fc

File tree

12 files changed

+375
-17
lines changed

12 files changed

+375
-17
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ include(CheckVariableExists)
190190
set(OPTFUNCS
191191
stpcpy stpncpy putenv mempcpy fdatasync lutimes mergesort
192192
getauxval setprogname __progname syncfs sched_getaffinity unshare
193-
secure_getenv __secure_getenv mremap strchrnul
193+
secure_getenv __secure_getenv mremap strchrnul dlmopen dlinfo
194194
)
195195
set(REQFUNCS
196196
mkstemp getcwd basename dirname realpath setenv unsetenv regcomp

build/files.cc

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#ifdef WITH_CAP
2020
#include <sys/capability.h>
2121
#endif
22+
#include <sys/xattr.h>
2223

2324
#ifdef HAVE_LIBDW
2425
#include <libelf.h>
@@ -55,6 +56,8 @@
5556
#define DEBUG_ID_DIR "/usr/lib/debug/.build-id"
5657
#define DEBUG_DWZ_DIR "/usr/lib/debug/.dwz"
5758

59+
#define XATTR_NAME_SOVERS "user.rpm_elf_so_version"
60+
5861
/**
5962
*/
6063
enum specfFlags_e {
@@ -1708,6 +1711,63 @@ static void argvAddAttr(ARGV_t *filesp, rpmfileAttrs attrs, const char *path)
17081711
free(line);
17091712
}
17101713

1714+
static int generateElfSoVers(FileList fl)
1715+
{
1716+
int rc = 0;
1717+
int i;
1718+
FileListRec flp;
1719+
1720+
/* How are we supposed to create the build-id links? */
1721+
char *elf_so_version_macro = rpmExpand("%{?_elf_so_version}", NULL);
1722+
if (*elf_so_version_macro == '\0') {
1723+
rc = 1;
1724+
rpmlog(RPMLOG_WARNING,
1725+
_("_elf_so_version macro not set, skipping elf-version generation\n"));
1726+
}
1727+
1728+
if (rc != 0) {
1729+
free(elf_so_version_macro);
1730+
return rc;
1731+
}
1732+
1733+
/* Save _elf_so_version for ELF shared objects in this package. */
1734+
for (i = 0, flp = fl->files.data(); i < fl->files.size(); i++, flp++) {
1735+
struct stat sbuf;
1736+
if (lstat(flp->diskPath, &sbuf) == 0 && S_ISREG (sbuf.st_mode)) {
1737+
/* We determine whether this is a main or
1738+
debug ELF based on path. */
1739+
int isDbg = strncmp (flp->cpioPath,
1740+
DEBUG_LIB_PREFIX, strlen (DEBUG_LIB_PREFIX)) == 0;
1741+
1742+
/* Only save elf_so_version executable files in the main package. */
1743+
if (isDbg
1744+
|| (sbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
1745+
continue;
1746+
1747+
int fd = open (flp->diskPath, O_RDONLY);
1748+
if (fd >= 0) {
1749+
/* Only real ELF files, that are ET_DYN should have _elf_so_version. */
1750+
GElf_Ehdr ehdr;
1751+
#ifdef HAVE_DWELF_ELF_BEGIN
1752+
Elf *elf = dwelf_elf_begin (fd);
1753+
#else
1754+
Elf *elf = elf_begin (fd, ELF_C_READ, NULL);
1755+
#endif
1756+
if (elf != NULL && elf_kind (elf) == ELF_K_ELF
1757+
&& gelf_getehdr (elf, &ehdr) != NULL
1758+
&& (ehdr.e_type == ET_DYN)) {
1759+
fsetxattr(fd, XATTR_NAME_SOVERS,
1760+
elf_so_version_macro, strlen(elf_so_version_macro), 0);
1761+
}
1762+
elf_end (elf);
1763+
close (fd);
1764+
}
1765+
}
1766+
}
1767+
free(elf_so_version_macro);
1768+
return rc;
1769+
}
1770+
17111771
#ifdef HAVE_LIBDW
17121772
/* How build id links are generated. See macros.in for description. */
17131773
#define BUILD_IDS_NONE 0
@@ -2582,13 +2642,25 @@ static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
25822642
if (fl.processingFailed)
25832643
goto exit;
25842644

2585-
#ifdef HAVE_LIBDW
25862645
{
25872646
/* Check build-ids and add build-ids links for files to package list. */
25882647
const char *arch = headerGetString(pkg->header, RPMTAG_ARCH);
25892648
if (rpmExpandNumeric("%{?__debug_package}") && !rstreq(arch, "noarch")) {
25902649
/* Go through the current package list and generate a files list. */
25912650
ARGV_t idFiles = NULL;
2651+
/* Go through the current package list and tag shared object files. */
2652+
if (generateElfSoVers (&fl) != 0) {
2653+
rpmlog(RPMLOG_ERR, _("Generating elf-so-version failed\n"));
2654+
fl.processingFailed = 1;
2655+
goto exit;
2656+
}
2657+
2658+
if (fl.processingFailed)
2659+
goto exit;
2660+
2661+
#ifdef HAVE_LIBDW
2662+
/* Check build-ids and add build-ids links for files to package list. */
2663+
/* Go through the current package list and generate a files list. */
25922664
if (generateBuildIDs (&fl, &idFiles) != 0) {
25932665
rpmlog(RPMLOG_ERR, _("Generating build-id links failed\n"));
25942666
fl.processingFailed = 1;
@@ -2605,8 +2677,8 @@ static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
26052677
if (fl.processingFailed)
26062678
goto exit;
26072679
}
2608-
}
26092680
#endif
2681+
}
26102682

26112683
/* Verify that file attributes scope over hardlinks correctly. */
26122684
if (checkHardLinks(fl.files))

config.h.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#cmakedefine HAVE_DIRENT_H @HAVE_DIRENT_H@
1414
#cmakedefine HAVE_DIRNAME @HAVE_DIRNAME@
1515
#cmakedefine HAVE_DLFCN_H @HAVE_DLFCN_H@
16+
#cmakedefine HAVE_DLINFO @HAVE_DLINFO@
17+
#cmakedefine HAVE_DLMOPEN @HAVE_DLMOPEN@
1618
#cmakedefine HAVE_DSA_SET0_KEY @HAVE_DSA_SET0_KEY@
1719
#cmakedefine HAVE_DSA_SET0_PQG @HAVE_DSA_SET0_PQG@
1820
#cmakedefine HAVE_DSA_SIG_SET0 @HAVE_DSA_SIG_SET0@

docs/manual/more_dependencies.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,16 @@ This has two "side-effects":
3939
It's a fairly common mistake to replace legacy PreReq dependencies with Requires(pre), but this is not the same, due to the latter point above!
4040

4141
## Automatic Dependencies
42-
To reduce the amount of work required by the package builder, RPM scans the file list of a package when it is being built. Any files in the file list which require shared libraries to work (as determined by ldd) cause that package to require the shared library.
42+
To reduce the amount of work required by the package builder, RPM scans the file list of a package when it is being built. Any files in the file list which require shared libraries to work (as determined by elfdeps for ELF objects) cause that package to require the shared library.
4343

44-
For example, if your package contains /bin/vi, RPM will add dependencies for both libtermcap.so.2 and libc.so.5. These are treated as virtual packages, so no version numbers are used.
44+
For example, if your package contains /bin/vi, RPM will add dependencies for both libtermcap.so.2 and libc.so.5. These are treated as virtual packages.
4545

4646
A similar process allows RPM to add Provides information automatically. Any shared library in the file list is examined for its soname (the part of the name which must match for two shared libraries to be considered equivalent) and that soname is automatically provided by the package. For example, the libc-5.3.12 package has provides information added for libm.so.5 and libc.so.5. We expect this automatic dependency generation to eliminate the need for most packages to use explicit Requires: lines.
4747

48+
As of rpm version (TBD), the internal dependency generator provides optional support for generating versioned ELF object dependencies on objects that do not provide versioned symbols. Distributions that wish to make use of this support should enable the \_elf\_provide\_fallback\_versions and \_elf\_require\_fallback\_versions macros. When these macros are enabled, the dependency generator will attempt to resolve shared object dependencies to a full path. If the shared object does not provide versioned symbols, and the soname is a symlink to a full name that differs from the soname, and the full name ends in ".so." followed by a sequence of numbers optionally separated by the '.' character, then that trailing sequence will be treated as a version and included in the automatically generated dependencies. While shared objects with versioned symbols are well supported, and are the preferred approach to ensuring that a dependency actually provides the ABI that another package requires, this fallback approach to versioning dependencies provides reasonably good coverage for the large body of ELF shared objects that do not yet maintain versioned symbols.
49+
50+
The most likely case where the fallback version system will break is a shared object being updated from a purely numeric version suffix to a version suffix with non-numeric characters (e.g. "libfoo.so.3.0.1" is updated to "libfoo.so.3.0.1a"). In that case, the dependency generator would not provide a version for that shared object, and the package maintainer would need to manually add a Provides: tag to the package in order to continue satisfying existing package dependencies.
51+
4852
## Custom Automatic Dependency
4953
Customizing automatic dependency generation is covered in [dependency generator documentation]().
5054

fileattrs/elf.attr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
%__elf_provides %{_rpmconfigdir}/elfdeps --provides --multifile
2-
%__elf_requires %{_rpmconfigdir}/elfdeps --requires --multifile
1+
%__elf_provides %{_rpmconfigdir}/elfdeps %{?_elf_provide_fallback_versions} --provides --multifile
2+
%__elf_requires %{_rpmconfigdir}/elfdeps %{?_elf_require_fallback_versions} --requires --multifile
33
%__elf_magic ^(setuid,? )?(setgid,? )?(sticky )?ELF (32|64)-bit.*$
44
%__elf_exclude_path ^/lib/modules/.*\\.ko?(\\.[[:alnum:]]*)$
55
%__elf_protocol multifile

macros.in

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,18 @@ Supplements: (%{name} = %{version}-%{release} and langpacks-%{1})\
551551
# Use internal dependency generator rather than external helpers?
552552
%_use_internal_dependency_generator 1
553553

554+
#
555+
# Generate minimum versions for ELF libraries that don't provide
556+
# versioned symbol?
557+
#%_elf_provide_fallback_versions --full-name-version-fallback
558+
#%_elf_require_fallback_versions --full-name-version-fallback
559+
560+
#
561+
# ELF libraries will usually inherit a version from their package,
562+
# but this can be overridden by defining _elf_so_version,
563+
# especially for interchangeable implementations.
564+
%_elf_so_version %{version}
565+
554566
# Directories whose contents should be considered as documentation.
555567
%__docdir_path %{_datadir}/doc:%{_datadir}/man:%{_datadir}/info:%{_datadir}/gtk-doc/html:%{_datadir}/gnome/help:%{?_docdir}:%{?_mandir}:%{?_infodir}:%{?_javadocdir}:/usr/doc:/usr/man:/usr/info:/usr/X11R6/man
556568

tests/atlocal.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ if [ "@WITH_IMAEVM@" == "ON" ]; then
5050
else
5151
IMA_DISABLED=true;
5252
fi
53+
if [ "@HAVE_DLINFO@" == 1 ] && [ "@HAVE_DLMOPEN@" == 1 ]; then
54+
HAVE_GNU_DLFCN=true;
55+
else
56+
HAVE_GNU_DLFCN=false;
57+
fi
5358
if mknod foodev c 123 123 2>/dev/null; then
5459
MKNOD_DISABLED=false
5560
rm -f foodev
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.0.0

tests/data/misc/libhello.so

-15.9 KB
Binary file not shown.

tests/data/misc/libhello.so

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
libhello.so.1.0.0

0 commit comments

Comments
 (0)