From 3edae790572203f07a28448fedfda82d0629f4fb Mon Sep 17 00:00:00 2001
From: Mark Wielaard <mjw@redhat.com>
Date: Tue, 14 Jun 2016 17:07:13 +0200
Subject: [PATCH] Make it possible to have unique build-ids across build
versions/releases.
Introduce a new macro _unique_build_ids that when set will pass the
version and release to find-debuginfo.sh and debugedit to recalculate
the build-id of ELF files.
Includes two new testcases to make sure the new setting works as expected
both when set and unset.
Signed-off-by: Mark Wielaard <mjw@redhat.com>
(cherry picked from commit 5ef1166ad96e3545784fa5420a49e1b2cd481e8e)
---
macros.in | 8 +++-
scripts/find-debuginfo.sh | 20 ++++++++-
tests/data/SPECS/hello-r2.spec | 58 +++++++++++++++++++++++++
tests/rpmbuildid.at | 96 +++++++++++++++++++++++++++++++++++++++++-
tools/debugedit.c | 24 ++++++++++-
5 files changed, 201 insertions(+), 5 deletions(-)
create mode 100644 tests/data/SPECS/hello-r2.spec
diff --git a/macros.in b/macros.in
index e43d62b0a..dcd09612c 100644
--- a/macros.in
+++ b/macros.in
@@ -180,7 +180,7 @@
# the script. See the script for details.
#
%__debug_install_post \
- %{_rpmconfigdir}/find-debuginfo.sh %{?_missing_build_ids_terminate_build:--strict-build-id} %{?_include_minidebuginfo:-m} %{?_find_debuginfo_dwz_opts} %{?_find_debuginfo_opts} "%{_builddir}/%{?buildsubdir}"\
+ %{_rpmconfigdir}/find-debuginfo.sh %{?_missing_build_ids_terminate_build:--strict-build-id} %{?_include_minidebuginfo:-m} %{?_unique_build_ids:--ver-rel "%{version}-%{release}"} %{?_find_debuginfo_dwz_opts} %{?_find_debuginfo_opts} "%{_builddir}/%{?buildsubdir}"\
%{nil}
# Template for debug information sub-package.
@@ -476,6 +476,12 @@ package or when debugging this package.\
# ELF /usr/lib/debug/.build-id/xx/yyy -> /usr/lib/.build-id/xx/yyy
%_build_id_links compat
+# Whether build-ids should be made unique between package version/releases
+# when generating debuginfo packages. If set to 1 this will pass
+# --ver-rel "%{version}-%{release}" to find-debuginfo.sh which will pass it
+# onto debugedit --build-id-seed to be used to prime the build-id note hash.
+%_unique_build_ids 1
+
#
# Use internal dependency generator rather than external helpers?
%_use_internal_dependency_generator 1
diff --git a/scripts/find-debuginfo.sh b/scripts/find-debuginfo.sh
index c9e2293de..2cb9570ba 100644
--- a/scripts/find-debuginfo.sh
+++ b/scripts/find-debuginfo.sh
@@ -6,6 +6,7 @@
# [-o debugfiles.list]
# [--run-dwz] [--dwz-low-mem-die-limit N]
# [--dwz-max-die-limit N]
+# [--ver-rel VERSION-RELEASE]
# [[-l filelist]... [-p 'pattern'] -o debuginfo.list]
# [builddir]
#
@@ -26,6 +27,12 @@
# if available, and --dwz-low-mem-die-limit and --dwz-max-die-limit
# provide detailed limits. See dwz(1) -l and -L option for details.
#
+# If --ver-rel VERSION-RELEASE is given then debugedit is called to
+# update the build-ids it finds adding the VERSION-RELEASE string as
+# seed to recalculate the build-id hash. This makes sure the
+# build-ids in the ELF files are unique between versions and releases
+# of the same package.
+#
# All file names in switches are relative to builddir (. if not given).
#
@@ -49,6 +56,9 @@ run_dwz=false
dwz_low_mem_die_limit=
dwz_max_die_limit=
+# Version and release of the spec. Given by --ver-rel
+ver_rel=
+
BUILDDIR=.
out=debugfiles.list
nout=0
@@ -68,6 +78,10 @@ while [ $# -gt 0 ]; do
dwz_max_die_limit=$2
shift
;;
+ --ver-rel)
+ ver_rel=$2
+ shift
+ ;;
-g)
strip_g=true
;;
@@ -249,8 +263,12 @@ while read nlinks inum f; do
fi
echo "extracting debug info from $f"
+ build_id_seed=
+ if [ ! -z "$ver_rel" ]; then
+ build_id_seed="--build-id-seed=$ver_rel"
+ fi
id=$(${lib_rpm_dir}/debugedit -b "$RPM_BUILD_DIR" -d /usr/src/debug \
- -i -l "$SOURCEFILE" "$f") || exit
+ -i $build_id_seed -l "$SOURCEFILE" "$f") || exit
if [ $nlinks -gt 1 ]; then
eval linkedid_$inum=\$id
fi
diff --git a/tests/data/SPECS/hello-r2.spec b/tests/data/SPECS/hello-r2.spec
new file mode 100644
index 000000000..ca5091d10
--- /dev/null
+++ b/tests/data/SPECS/hello-r2.spec
@@ -0,0 +1,58 @@
+Summary: hello -- hello, world rpm
+Name: hello
+Version: 1.0
+Release: 2
+Group: Utilities
+License: GPL
+Distribution: RPM test suite.
+Vendor: Red Hat Software
+Packager: Red Hat Software <bugs@redhat.com>
+URL: http://www.redhat.com
+Source0: hello-1.0.tar.gz
+Patch0: hello-1.0-modernize.patch
+Excludearch: lsi
+Excludeos: cpm
+Provides: hi
+Conflicts: goodbye
+Obsoletes: howdy
+Prefix: /usr
+
+%description
+Simple rpm demonstration.
+
+%prep
+%setup -q
+%patch0 -p1 -b .modernize
+
+%build
+make
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/usr/local/bin
+make DESTDIR=$RPM_BUILD_ROOT install
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%pre
+
+%post
+
+%preun
+
+%postun
+
+%files
+%defattr(-,root,root)
+%doc FAQ
+#%readme README
+#%license COPYING
+%attr(0751,root,root) /usr/local/bin/hello
+
+%changelog
+* Wed Jun 8 2016 Mark Wielaard <mjw@redhat.com>
+- Update release for unique build-id generation tests.
+
+* Tue Oct 20 1998 Jeff Johnson <jbj@redhat.com>
+- create.
diff --git a/tests/rpmbuildid.at b/tests/rpmbuildid.at
index eddca969b..1da63022d 100644
--- a/tests/rpmbuildid.at
+++ b/tests/rpmbuildid.at
@@ -758,4 +758,98 @@ debug id in debug package
debug dup id in debug package
],
[])
-AT_CLEANUP
\ No newline at end of file
+AT_CLEANUP
+
+# ------------------------------
+# Check build-ids are unique between versions/releases
+# with _unique_build_ids defined.
+AT_SETUP([rpmbuild buildid unique r1 r2])
+AT_KEYWORDS([build] [debuginfo] [buildid])
+AT_CHECK([
+rm -rf ${TOPDIR}
+AS_MKDIR_P(${TOPDIR}/SOURCES)
+
+cp "${abs_srcdir}"/data/SOURCES/hello-1.0.tar.gz "${abs_srcdir}"/data/SOURCES/hello-1.0-modernize.patch ${TOPDIR}/SOURCES
+
+# No warnings for hard links
+run rpmbuild --quiet \
+ --macros=${abs_top_builddir}/macros:${abs_top_builddir}/tests/testing/usr/local/lib/rpm/platform/%{_target_cpu}-%{_target_os}/macros:${top_srcdir}/macros.debug \
+ --rcfile=${abs_top_builddir}/rpmrc \
+ --define="_unique_build_ids 1" \
+ -ba "${abs_srcdir}"/data/SPECS/hello.spec
+
+rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-1.0-1.*.rpm \
+ | cpio -diu --quiet
+
+hello_file=./usr/local/bin/hello
+
+# Extract the build-id from the main file
+id1=$(file $hello_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/')
+
+# Build the "next" release, which has no changes except for the release update.
+run rpmbuild --quiet \
+ --macros=${abs_top_builddir}/macros:${abs_top_builddir}/tests/testing/usr/local/lib/rpm/platform/%{_target_cpu}-%{_target_os}/macros:${top_srcdir}/macros.debug \
+ --rcfile=${abs_top_builddir}/rpmrc \
+ --define="_unique_build_ids 1" \
+ -ba "${abs_srcdir}"/data/SPECS/hello-r2.spec
+
+rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-1.0-2.*.rpm \
+ | cpio -diu --quiet
+
+# Extract the build-id from the main file
+id2=$(file $hello_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/')
+
+if test "$id1" == "$id2"; then echo "equal $id1"; else echo "unequal"; fi
+],
+[0],
+[unequal
+],
+[ignore])
+AT_CLEANUP
+
+# ------------------------------
+# Check build-ids are non-unique between versions/releases
+# with _unique_build_ids undefined (and exact same sources).
+AT_SETUP([rpmbuild buildid non-unique r1 r2])
+AT_KEYWORDS([build] [debuginfo] [buildid])
+AT_CHECK([
+rm -rf ${TOPDIR}
+AS_MKDIR_P(${TOPDIR}/SOURCES)
+
+cp "${abs_srcdir}"/data/SOURCES/hello-1.0.tar.gz "${abs_srcdir}"/data/SOURCES/hello-1.0-modernize.patch ${TOPDIR}/SOURCES
+
+# No warnings for hard links
+run rpmbuild --quiet \
+ --macros=${abs_top_builddir}/macros:${abs_top_builddir}/tests/testing/usr/local/lib/rpm/platform/%{_target_cpu}-%{_target_os}/macros:${top_srcdir}/macros.debug \
+ --rcfile=${abs_top_builddir}/rpmrc \
+ --undefine="_unique_build_ids" \
+ -ba "${abs_srcdir}"/data/SPECS/hello.spec
+
+rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-1.0-1.*.rpm \
+ | cpio -diu --quiet
+
+hello_file=./usr/local/bin/hello
+
+# Extract the build-id from the main file
+id1=$(file $hello_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/')
+
+# Build the "next" release, which has no changes except for the release update.
+run rpmbuild --quiet \
+ --macros=${abs_top_builddir}/macros:${abs_top_builddir}/tests/testing/usr/local/lib/rpm/platform/%{_target_cpu}-%{_target_os}/macros:${top_srcdir}/macros.debug \
+ --rcfile=${abs_top_builddir}/rpmrc \
+ --undefine="_unique_build_ids" \
+ -ba "${abs_srcdir}"/data/SPECS/hello-r2.spec
+
+rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-1.0-2.*.rpm \
+ | cpio -diu --quiet
+
+# Extract the build-id from the main file
+id2=$(file $hello_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/')
+
+if test "$id1" == "$id2"; then echo "equal"; else echo "unequal $id1 $id2"; fi
+],
+[0],
+[equal
+],
+[ignore])
+AT_CLEANUP
diff --git a/tools/debugedit.c b/tools/debugedit.c
index cf89312fa..c0147f086 100644
--- a/tools/debugedit.c
+++ b/tools/debugedit.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001, 2002, 2003, 2005, 2007, 2009, 2010, 2011 Red Hat, Inc.
+/* Copyright (C) 2001-2003, 2005, 2007, 2009-2011, 2016 Red Hat, Inc.
Written by Alexander Larsson <alexl@redhat.com>, 2002
Based on code by Jakub Jelinek <jakub@redhat.com>, 2001.
@@ -54,6 +54,7 @@ char *dest_dir = NULL;
char *list_file = NULL;
int list_file_fd = -1;
int do_build_id = 0;
+char *build_id_seed = NULL;
typedef struct
{
@@ -1296,6 +1297,8 @@ static struct poptOption optionsTable[] = {
"file where to put list of source and header file names", NULL },
{ "build-id", 'i', POPT_ARG_NONE, &do_build_id, 0,
"recompute build ID note and print ID on stdout", NULL },
+ { "build-id-seed", 's', POPT_ARG_STRING, &build_id_seed, 0,
+ "if recomputing the build ID note use this string as hash seed", NULL },
POPT_AUTOHELP
{ NULL, 0, 0, NULL, 0, NULL, NULL }
};
@@ -1400,7 +1403,7 @@ handle_build_id (DSO *dso, Elf_Data *build_id,
exit (1);
}
- if (!dirty_elf)
+ if (!dirty_elf && build_id_seed == NULL)
goto print;
if (elf_update (dso->elf, ELF_C_NULL) < 0)
@@ -1415,6 +1418,10 @@ handle_build_id (DSO *dso, Elf_Data *build_id,
ctx = rpmDigestInit(algorithm, 0);
+ /* If a seed string was given use it to prime the hash. */
+ if (build_id_seed != NULL)
+ rpmDigestUpdate(ctx, build_id_seed, strlen (build_id_seed));
+
/* Slurp the relevant header bits and section contents and feed them
into the hash function. The only bits we ignore are the offset
fields in ehdr and shdrs, since the semantically identical ELF file
@@ -1541,6 +1548,19 @@ main (int argc, char *argv[])
}
}
+ if (build_id_seed != NULL && do_build_id == 0)
+ {
+ fprintf (stderr, "--build-id-seed (-s) needs --build-id (-i)\n");
+ exit (1);
+ }
+
+ if (build_id_seed != NULL && strlen (build_id_seed) < 1)
+ {
+ fprintf (stderr,
+ "--build-id-seed (-s) string should be at least 1 char\n");
+ exit (1);
+ }
+
/* Ensure clean paths, users can muck with these */
if (base_dir)
canonicalize_path(base_dir, base_dir);