From 5b4d9835cd5259214ad957e26e32b97926630837 Mon Sep 17 00:00:00 2001 From: Panu Matilainen Date: Mar 08 2017 09:33:04 +0000 Subject: Mark Wielaard's backports for debuginfo parallel installation etc (#1427970) --- diff --git a/0001-Add-build-id-links-to-rpm-for-all-ELF-files.patch b/0001-Add-build-id-links-to-rpm-for-all-ELF-files.patch new file mode 100644 index 0000000..274c317 --- /dev/null +++ b/0001-Add-build-id-links-to-rpm-for-all-ELF-files.patch @@ -0,0 +1,1566 @@ +From 189b4f88c8e100155ec23a1e0b214bdc8473532a Mon Sep 17 00:00:00 2001 +Message-Id: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +From: Mark Wielaard +Date: Tue, 14 Jun 2016 17:07:12 +0200 +Subject: [PATCH 01/11] Add build-id links to rpm for all ELF files. + +This patch moves the main ELF file build-id symlinks from the +debuginfo package into the main package. And uses different +base directories for the main ELF file build-id symlink. +For the main build-id use /usr/lib/.build-id and for the debug +build-id use /usr/lib/debug/.build-id. + +There are two reasons for doing this. The main package and the +debuginfo package might get out of sync, or the debuginfo package +might not be installed at all. In which case finding the main ELF +file through the build-id symlink becomes impossible. Secondly by +moving the main ELF build-id symlink in its own directory the +/usr/lib/debug directory gets populated with only debuginfo files +which is convenient if the user might want to have that directory +populated through a network mountpoint. + +To support the new logic the symlink code has been moved from +find-debuginfo.sh to build/files.c. + +This also includes support for a new config %_build_id_links that +defaults to compat. The other settings are none, alldebug (the old +style) and separate. compat is like separate, but adds a compatibility +link under /usr/lib/debug/.build-id for the main build-id symlink. + +There are several new testcases added to test the various settings +using the new keyword "buildid". + +Signed-off-by: Mark Wielaard +--- + build/Makefile.am | 4 + + build/files.c | 375 ++++++++++++++++++++ + configure.ac | 15 + + macros.in | 28 ++ + scripts/find-debuginfo.sh | 60 ---- + tests/Makefile.am | 1 + + tests/data/SPECS/hello2cp.spec | 64 ++++ + tests/data/SPECS/hello2ln.spec | 63 ++++ + tests/rpmbuildid.at | 761 +++++++++++++++++++++++++++++++++++++++++ + tests/rpmtests.at | 1 + + 10 files changed, 1312 insertions(+), 60 deletions(-) + create mode 100644 tests/data/SPECS/hello2cp.spec + create mode 100644 tests/data/SPECS/hello2ln.spec + create mode 100644 tests/rpmbuildid.at + +diff --git a/build/Makefile.am b/build/Makefile.am +index dbda716..71edbd1 100644 +--- a/build/Makefile.am ++++ b/build/Makefile.am +@@ -26,3 +26,7 @@ librpmbuild_la_LIBADD = \ + @LTLIBICONV@ \ + @WITH_POPT_LIB@ \ + @WITH_MAGIC_LIB@ ++ ++if LIBDW ++librpmbuild_la_LIBADD += @WITH_LIBELF_LIB@ @WITH_LIBDW_LIB@ ++endif +diff --git a/build/files.c b/build/files.c +index b76ce04..b010483 100644 +--- a/build/files.c ++++ b/build/files.c +@@ -14,6 +14,11 @@ + #include + #endif + ++#if HAVE_LIBDW ++#include ++#include ++#endif ++ + #include + #include + #include +@@ -1551,6 +1556,368 @@ exit: + return rc; + } + ++#if HAVE_LIBDW ++/* How build id links are generated. See macros.in for description. */ ++#define BUILD_IDS_NONE 0 ++#define BUILD_IDS_ALLDEBUG 1 ++#define BUILD_IDS_SEPARATE 2 ++#define BUILD_IDS_COMPAT 3 ++ ++static int addNewIDSymlink(FileList fl, ++ char *targetpath, char *idlinkpath, ++ int isDbg, int isCompat) ++{ ++ const char *linkerr = _("failed symlink"); ++ int rc = 0; ++ int nr = 0; ++ char *origpath, *linkpath; ++ ++ if (isDbg) ++ rasprintf(&linkpath, "%s.debug", idlinkpath); ++ else ++ linkpath = idlinkpath; ++ origpath = linkpath; ++ ++ while (faccessat(AT_FDCWD, linkpath, F_OK, AT_SYMLINK_NOFOLLOW) == 0) { ++ if (nr > 0) ++ free(linkpath); ++ nr++; ++ rasprintf(&linkpath, "%s.%d%s", idlinkpath, nr, ++ isDbg ? ".debug" : ""); ++ } ++ ++ char *symtarget = targetpath; ++ if (nr > 0 && isCompat) ++ rasprintf (&symtarget, "%s.%d", targetpath, nr); ++ ++ if (symlink(symtarget, linkpath) < 0) { ++ rc = 1; ++ rpmlog(RPMLOG_ERR, "%s: %s -> %s: %m\n", ++ linkerr, linkpath, symtarget); ++ } else { ++ fl->cur.isDir = 0; ++ rc = addFile(fl, linkpath, NULL); ++ } ++ ++ /* Don't warn (again) if this is a compat id-link, we retarget it. */ ++ if (nr > 0 && !isCompat) { ++ /* Lets see why there are multiple build-ids. If the original ++ targets are hard linked, then it is OK, otherwise warn ++ something fishy is going on. Would be nice to call ++ something like eu-elfcmp to see if they are really the same ++ ELF file or not. */ ++ struct stat st1, st2; ++ if (stat (origpath, &st1) != 0) { ++ rpmlog(RPMLOG_WARNING, _("Duplicate build-id, stat %s: %m\n"), ++ origpath); ++ } else if (stat (linkpath, &st2) != 0) { ++ rpmlog(RPMLOG_WARNING, _("Duplicate build-id, stat %s: %m\n"), ++ linkpath); ++ } else if (!(S_ISREG(st1.st_mode) && S_ISREG(st2.st_mode) ++ && st1.st_nlink > 1 && st2.st_nlink == st1.st_nlink ++ && st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev)) { ++ char *rpath1 = realpath(origpath, NULL); ++ char *rpath2 = realpath(linkpath, NULL); ++ rpmlog(RPMLOG_WARNING, _("Duplicate build-ids %s and %s\n"), ++ rpath1, rpath2); ++ free(rpath1); ++ free(rpath2); ++ } ++ } ++ ++ if (isDbg) ++ free(origpath); ++ if (nr > 0) ++ free(linkpath); ++ if (nr > 0 && isCompat) ++ free(symtarget); ++ ++ return rc; ++} ++ ++static int generateBuildIDs(FileList fl) ++{ ++ int rc = 0; ++ int i; ++ FileListRec flp; ++ char **ids = NULL; ++ char **paths = NULL; ++ size_t nr_ids, allocated; ++ nr_ids = allocated = 0; ++ ++ /* How are we supposed to create the build-id links? */ ++ char *build_id_links_macro = rpmExpand("%{?_build_id_links}", NULL); ++ int build_id_links; ++ if (build_id_links_macro == NULL) { ++ rpmlog(RPMLOG_WARNING, ++ _("_build_id_links macro not set, assuming 'compat'\n")); ++ build_id_links = BUILD_IDS_COMPAT; ++ } else if (strcmp(build_id_links_macro, "none") == 0) { ++ build_id_links = BUILD_IDS_NONE; ++ } else if (strcmp(build_id_links_macro, "alldebug") == 0) { ++ build_id_links = BUILD_IDS_ALLDEBUG; ++ } else if (strcmp(build_id_links_macro, "separate") == 0) { ++ build_id_links = BUILD_IDS_SEPARATE; ++ } else if (strcmp(build_id_links_macro, "compat") == 0) { ++ build_id_links = BUILD_IDS_COMPAT; ++ } else { ++ rc = 1; ++ rpmlog(RPMLOG_ERR, ++ _("_build_id_links macro set to unknown value '%s'\n"), ++ build_id_links_macro); ++ build_id_links = BUILD_IDS_NONE; ++ } ++ free(build_id_links_macro); ++ ++ if (build_id_links == BUILD_IDS_NONE || rc != 0) ++ return rc; ++ ++ int terminate = rpmExpandNumeric("%{?_missing_build_ids_terminate_build}"); ++ ++ /* Collect and check all build-ids for ELF files in this package. */ ++ int needMain = 0; ++ int needDbg = 0; ++ for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) { ++ int fd; ++ fd = open (flp->diskPath, O_RDONLY); ++ if (fd >= 0) { ++ struct stat sbuf; ++ if (fstat (fd, &sbuf) == 0 && S_ISREG (sbuf.st_mode)) { ++ Elf *elf = elf_begin (fd, ELF_C_READ, NULL); ++ if (elf != NULL && elf_kind(elf) == ELF_K_ELF) { ++ const void *build_id; ++ ssize_t len = dwelf_elf_gnu_build_id (elf, &build_id); ++ /* len == -1 means error. Zero means no ++ build-id. We want at least a length of 2 so we ++ have at least a xx/yy (hex) dir/file. But ++ reasonable build-ids are between 16 bytes (md5 ++ is 128 bits) and 64 bytes (largest sha3 is 512 ++ bits), common is 20 bytes (sha1 is 160 bits). */ ++ if (len >= 16 && len <= 64) { ++ /* We determine whether this is a main or ++ debug ELF based on path. */ ++ #define DEBUGPATH "/usr/lib/debug/" ++ int addid = 0; ++ if (strncmp (flp->cpioPath, ++ DEBUGPATH, strlen (DEBUGPATH)) == 0) { ++ needDbg = 1; ++ addid = 1; ++ } ++ else if (build_id_links != BUILD_IDS_ALLDEBUG) { ++ needMain = 1; ++ addid = 1; ++ } ++ if (addid) { ++ const unsigned char *p = build_id; ++ const unsigned char *end = p + len; ++ char *id_str; ++ if (allocated <= nr_ids) { ++ allocated += 16; ++ paths = xrealloc (paths, ++ allocated * sizeof(char *)); ++ ids = xrealloc (ids, ++ allocated * sizeof(char *)); ++ } ++ ++ paths[nr_ids] = xstrdup(flp->cpioPath); ++ id_str = ids[nr_ids] = xmalloc(2 * len + 1); ++ while (p < end) ++ id_str += sprintf(id_str, "%02x", ++ (unsigned)*p++); ++ *id_str = '\0'; ++ nr_ids++; ++ } ++ } else { ++ if (len < 0) { ++ rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING, ++ _("error reading build-id in %s: %s\n"), ++ flp->diskPath, elf_errmsg (-1)); ++ } else if (len == 0) { ++ rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING, ++ _("Missing build-id in %s\n"), ++ flp->diskPath); ++ } else { ++ rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING, ++ (len < 16 ++ ? _("build-id found in %s too small\n") ++ : _("build-id found in %s too large\n")), ++ flp->diskPath); ++ } ++ if (terminate) ++ rc = 1; ++ } ++ elf_end (elf); ++ } ++ } ++ close (fd); ++ } ++ } ++ ++ /* Process and clean up all build-ids. */ ++ if (nr_ids > 0) { ++ const char *errdir = _("failed to create directory"); ++ char *mainiddir = NULL; ++ char *debugiddir = NULL; ++ if (rc == 0) { ++ /* Add .build-id directories to hold the subdirs/symlinks. */ ++ #define BUILD_ID_DIR "/usr/lib/.build-id" ++ #define DEBUG_ID_DIR "/usr/lib/debug/.build-id" ++ ++ mainiddir = rpmGetPath(fl->buildRoot, BUILD_ID_DIR, NULL); ++ debugiddir = rpmGetPath(fl->buildRoot, DEBUG_ID_DIR, NULL); ++ ++ /* Supported, but questionable. */ ++ if (needMain && needDbg) ++ rpmlog(RPMLOG_WARNING, ++ _("Mixing main ELF and debug files in package")); ++ ++ if (needMain) { ++ if ((rc = rpmioMkpath(mainiddir, 0755, -1, -1)) != 0) { ++ rpmlog(RPMLOG_ERR, "%s %s: %m\n", errdir, mainiddir); ++ } else { ++ fl->cur.isDir = 1; ++ rc = addFile(fl, mainiddir, NULL); ++ } ++ } ++ ++ if (rc == 0 && needDbg) { ++ if ((rc = rpmioMkpath(debugiddir, 0755, -1, -1)) != 0) { ++ rpmlog(RPMLOG_ERR, "%s %s: %m\n", errdir, debugiddir); ++ } else { ++ fl->cur.isDir = 1; ++ rc = addFile(fl, debugiddir, NULL); ++ } ++ } ++ } ++ ++ /* Now add a subdir and symlink for each buildid found. */ ++ for (i = 0; i < nr_ids; i++) { ++ /* Don't add anything more when an error occured. But do ++ cleanup. */ ++ if (rc == 0) { ++ int isDbg = strncmp (paths[i], DEBUGPATH, ++ strlen (DEBUGPATH)) == 0; ++ ++ char *buildidsubdir; ++ char subdir[4]; ++ subdir[0] = '/'; ++ subdir[1] = ids[i][0]; ++ subdir[2] = ids[i][1]; ++ subdir[3] = '\0'; ++ if (isDbg) ++ buildidsubdir = rpmGetPath(debugiddir, subdir, NULL); ++ else ++ buildidsubdir = rpmGetPath(mainiddir, subdir, NULL); ++ /* We only need to create and add the subdir once. */ ++ int addsubdir = access (buildidsubdir, F_OK) == -1; ++ if (addsubdir ++ && (rc = rpmioMkpath(buildidsubdir, 0755, -1, -1)) != 0) { ++ rpmlog(RPMLOG_ERR, "%s %s: %m\n", errdir, buildidsubdir); ++ } else { ++ fl->cur.isDir = 1; ++ if (!addsubdir ++ || (rc = addFile(fl, buildidsubdir, NULL)) == 0) { ++ char *linkpattern, *targetpattern; ++ char *linkpath, *targetpath; ++ if (isDbg) { ++ linkpattern = "%s/%s"; ++ targetpattern = "../../../../..%s"; ++ } else { ++ linkpattern = "%s/%s"; ++ targetpattern = "../../../..%s"; ++ } ++ rasprintf(&linkpath, linkpattern, ++ buildidsubdir, &ids[i][2]); ++ rasprintf(&targetpath, targetpattern, paths[i]); ++ rc = addNewIDSymlink(fl, targetpath, linkpath, ++ isDbg, 0); ++ ++ /* We might want to have a link from the debug ++ build_ids dir to the main one. We create it ++ when we are creating compat links or doing ++ an old style alldebug build-ids package. In ++ the first case things are simple since we ++ just link to the main build-id symlink. The ++ second case is a bit tricky, since we ++ cannot be 100% sure the file names in the ++ main and debug package match. Currently ++ they do, but when creating parallel ++ installable debuginfo packages they might ++ not (in that case we might have to also ++ strip the nvr from the debug name). ++ ++ In general either method is discouraged ++ since it might create dangling symlinks if ++ the package versions get out of sync. */ ++ if (rc == 0 && isDbg ++ && build_id_links == BUILD_IDS_COMPAT) { ++ /* buildidsubdir already points to the ++ debug buildid. We just need to setup ++ the symlink to the main one. */ ++ free(linkpath); ++ free(targetpath); ++ rasprintf(&linkpath, "%s/%s", ++ buildidsubdir, &ids[i][2]); ++ rasprintf(&targetpath, ++ "../../../.build-id%s/%s", ++ subdir, &ids[i][2]); ++ rc = addNewIDSymlink(fl, targetpath, linkpath, ++ 0, 1); ++ } ++ ++ if (rc == 0 && isDbg ++ && build_id_links == BUILD_IDS_ALLDEBUG) { ++ /* buildidsubdir already points to the ++ debug buildid. We do have to figure out ++ the main ELF file though (which is most ++ likely not in this package). Guess we ++ can find it by stripping the ++ /usr/lib/debug path and .debug ++ prefix. Which might not really be ++ correct if there was a more involved ++ transformation (for example for ++ parallel installable debuginfo ++ packages), but then we shouldn't be ++ using ALLDEBUG in the first place. ++ Also ignore things like .dwz multifiles ++ which don't end in ".debug". */ ++ int pathlen = strlen(paths[i]); ++ int debuglen = strlen(".debug"); ++ int prefixlen = strlen("/usr/lib/debug"); ++ if (pathlen > prefixlen ++ && strcmp (paths[i] + pathlen - debuglen, ++ ".debug") == 0) { ++ free(linkpath); ++ free(targetpath); ++ char *targetstr = xstrdup (paths[i] ++ + prefixlen); ++ int targetlen = pathlen - prefixlen; ++ targetstr[targetlen - debuglen] = '\0'; ++ rasprintf(&linkpath, "%s/%s", ++ buildidsubdir, &ids[i][2]); ++ rasprintf(&targetpath, "../../../../..%s", ++ targetstr); ++ rc = addNewIDSymlink(fl, targetpath, ++ linkpath, 0, 0); ++ free(targetstr); ++ } ++ } ++ free(linkpath); ++ free(targetpath); ++ } ++ } ++ free(buildidsubdir); ++ } ++ free(paths[i]); ++ free(ids[i]); ++ } ++ free(paths); ++ free(ids); ++ } ++ return rc; ++} ++#endif ++ + /** + * Add a file to a binary package. + * @param pkg +@@ -1963,6 +2330,11 @@ static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags, + if (fl.processingFailed) + goto exit; + ++#if HAVE_LIBDW ++ if (generateBuildIDs (&fl) != 0) ++ goto exit; ++#endif ++ + /* Verify that file attributes scope over hardlinks correctly. */ + if (checkHardLinks(&fl.files)) + (void) rpmlibNeedsFeature(pkg, "PartialHardlinkSets", "4.0.4-1"); +@@ -2161,6 +2533,9 @@ rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags, + Package pkg; + rpmRC rc = RPMRC_OK; + ++#if HAVE_LIBDW ++ elf_version (EV_CURRENT); ++#endif + check_fileList = newStringBuf(); + genSourceRpmName(spec); + +diff --git a/configure.ac b/configure.ac +index b2ec501..08eceeb 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -367,6 +367,21 @@ AC_SUBST(WITH_ARCHIVE_LIB) + AM_CONDITIONAL(WITH_ARCHIVE,[test "$with_archive" = yes]) + + #================= ++# Check for elfutils libdw library with dwelf_elf_gnu_build_id. ++AS_IF([test "$WITH_LIBELF" = yes],[ ++ AC_CHECK_HEADERS([elfutils/libdwelf.h],[ ++ AC_CHECK_LIB(dw, dwelf_elf_gnu_build_id, [ ++ AC_DEFINE(HAVE_LIBDW, 1, ++ [Define to 1 if you have elfutils libdw library]) ++ WITH_LIBDW_LIB="-ldw" ++ WITH_LIBDW=yes ++ ]) ++ ]) ++ AC_SUBST(WITH_LIBDW_LIB) ++ AM_CONDITIONAL(LIBDW,[test "$WITH_LIBDW" = yes]) ++]) ++ ++#================= + # Process --with/without-external-db + AC_ARG_WITH(external_db, [AS_HELP_STRING([--with-external-db],[build against an external Berkeley db])], + [case "$with_external_db" in +diff --git a/macros.in b/macros.in +index fd57f2e..e43d62b0 100644 +--- a/macros.in ++++ b/macros.in +@@ -449,6 +449,34 @@ package or when debugging this package.\ + #%_include_minidebuginfo 1 + + # ++# Defines how and if build_id links are generated for ELF files. ++# The following settings are supported: ++# ++# - none ++# No build_id links are generated. ++# ++# - alldebug ++# build_id links are generated only when the __debug_package global is ++# defined. This will generate build_id links in the -debuginfo package ++# for both the main file as /usr/lib/debug/.build-id/xx/yyy and for ++# the .debug file as /usr/lib/debug/.build-id/xx/yyy.debug. ++# This is the old style build_id links as generated by the original ++# find-debuginfo.sh script. ++# ++# - separate ++# build_id links are generate for all binary packages. If this is a ++# main package (the __debug_package global isn't set) then the ++# build_id link is generated as /usr/lib/.build-id/xx/yyy. If this is ++# a -debuginfo package (the __debug_package global is set) then the ++# build_id link is generated as /usr/lib/debug/.build-id/xx/yyy. ++# ++# - compat ++# Same as for "separate" but if the __debug_package global is set then ++# the -debuginfo package will have a compatibility link for the main ++# ELF /usr/lib/debug/.build-id/xx/yyy -> /usr/lib/.build-id/xx/yyy ++%_build_id_links compat ++ ++# + # 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 4293261..c9e2293 100644 +--- a/scripts/find-debuginfo.sh ++++ b/scripts/find-debuginfo.sh +@@ -207,57 +207,6 @@ debug_link() + link_relative "$t" "$l" "$RPM_BUILD_ROOT" + } + +-# Provide .2, .3, ... symlinks to all filename instances of this build-id. +-make_id_dup_link() +-{ +- local id="$1" file="$2" idfile +- +- local n=1 +- while true; do +- idfile=".build-id/${id:0:2}/${id:2}.$n" +- [ $# -eq 3 ] && idfile="${idfile}$3" +- if [ ! -L "$RPM_BUILD_ROOT/usr/lib/debug/$idfile" ]; then +- break +- fi +- n=$[$n+1] +- done +- debug_link "$file" "/$idfile" +-} +- +-# Make a build-id symlink for id $1 with suffix $3 to file $2. +-make_id_link() +-{ +- local id="$1" file="$2" +- local idfile=".build-id/${id:0:2}/${id:2}" +- [ $# -eq 3 ] && idfile="${idfile}$3" +- local root_idfile="$RPM_BUILD_ROOT/usr/lib/debug/$idfile" +- +- if [ ! -L "$root_idfile" ]; then +- debug_link "$file" "/$idfile" +- return +- fi +- +- make_id_dup_link "$@" +- +- [ $# -eq 3 ] && return 0 +- +- local other=$(readlink -m "$root_idfile") +- other=${other#$RPM_BUILD_ROOT} +- if cmp -s "$root_idfile" "$RPM_BUILD_ROOT$file" || +- eu-elfcmp -q "$root_idfile" "$RPM_BUILD_ROOT$file" 2> /dev/null; then +- # Two copies. Maybe one has to be setuid or something. +- echo >&2 "*** WARNING: identical binaries are copied, not linked:" +- echo >&2 " $file" +- echo >&2 " and $other" +- else +- # This is pathological, break the build. +- echo >&2 "*** ERROR: same build ID in nonidentical files!" +- echo >&2 " $file" +- echo >&2 " and $other" +- exit 2 +- fi +-} +- + get_debugfn() + { + dn=$(dirname "${1#$RPM_BUILD_ROOT}") +@@ -288,8 +237,6 @@ while read nlinks inum f; do + eval linked=\$linked_$inum + if [ -n "$linked" ]; then + eval id=\$linkedid_$inum +- make_id_dup_link "$id" "$dn/$(basename $f)" +- make_id_dup_link "$id" "/usr/lib/debug$dn/$bn" .debug + link=$debugfn + get_debugfn "$linked" + echo "hard linked $link to $debugfn" +@@ -318,7 +265,6 @@ while read nlinks inum f; do + # just has its file names collected and adjusted. + case "$dn" in + /usr/lib/debug/*) +- [ -z "$id" ] || make_id_link "$id" "$dn/$(basename $f)" + continue ;; + esac + +@@ -336,10 +282,6 @@ while read nlinks inum f; do + + echo "./${f#$RPM_BUILD_ROOT}" >> "$ELFBINSFILE" + +- if [ -n "$id" ]; then +- make_id_link "$id" "$dn/$(basename $f)" +- make_id_link "$id" "/usr/lib/debug$dn/$bn" .debug +- fi + done || exit + + # Invoke the DWARF Compressor utility. +@@ -367,8 +309,6 @@ if $run_dwz && type dwz >/dev/null 2>&1 \ + if [ -f "${RPM_BUILD_ROOT}/usr/lib/debug/.dwz/${dwz_multifile_name}" ]; then + id="`readelf -Wn "${RPM_BUILD_ROOT}/usr/lib/debug/.dwz/${dwz_multifile_name}" \ + 2>/dev/null | sed -n 's/^ Build ID: \([0-9a-f]\+\)/\1/p'`" +- [ -n "$id" ] \ +- && make_id_link "$id" "/usr/lib/debug/.dwz/${dwz_multifile_name}" .debug + fi + + # dwz invalidates .gnu_debuglink CRC32 in the main files. +diff --git a/tests/Makefile.am b/tests/Makefile.am +index 7a5cc65..10555ce 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -19,6 +19,7 @@ TESTSUITE_AT += rpmquery.at + TESTSUITE_AT += rpmverify.at + TESTSUITE_AT += rpmdb.at + TESTSUITE_AT += rpmbuild.at ++TESTSUITE_AT += rpmbuildid.at + TESTSUITE_AT += rpmi.at + TESTSUITE_AT += rpmvercmp.at + TESTSUITE_AT += rpmdeps.at +diff --git a/tests/data/SPECS/hello2cp.spec b/tests/data/SPECS/hello2cp.spec +new file mode 100644 +index 0000000..33d8dc2 +--- /dev/null ++++ b/tests/data/SPECS/hello2cp.spec +@@ -0,0 +1,64 @@ ++Summary: hello2 -- double hello, world rpm ++Name: hello2 ++Version: 1.0 ++Release: 1 ++Group: Utilities ++License: GPL ++Distribution: RPM test suite. ++Vendor: Red Hat Software ++Packager: Red Hat Software ++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 -n hello-1.0 ++%patch0 -p1 -b .modernize ++ ++%build ++make CFLAGS="-g -O1" ++cp hello hello2 ++ ++%install ++rm -rf $RPM_BUILD_ROOT ++mkdir -p $RPM_BUILD_ROOT/usr/local/bin ++make DESTDIR=$RPM_BUILD_ROOT install ++cp hello2 $RPM_BUILD_ROOT/usr/local/bin/ ++ ++%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 ++%attr(0751,root,root) /usr/local/bin/hello2 ++ ++%changelog ++* Mon Jun 6 2016 Mark Wielaard ++- Copy hello to hello2 for duplicate build-id testing. ++ ++* Wed May 18 2016 Mark Wielaard ++- Add hello2 for dwz testing support. ++ ++* Tue Oct 20 1998 Jeff Johnson ++- create. +diff --git a/tests/data/SPECS/hello2ln.spec b/tests/data/SPECS/hello2ln.spec +new file mode 100644 +index 0000000..2c40dcc +--- /dev/null ++++ b/tests/data/SPECS/hello2ln.spec +@@ -0,0 +1,63 @@ ++Summary: hello2 -- double hello, world rpm ++Name: hello2 ++Version: 1.0 ++Release: 1 ++Group: Utilities ++License: GPL ++Distribution: RPM test suite. ++Vendor: Red Hat Software ++Packager: Red Hat Software ++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 -n hello-1.0 ++%patch0 -p1 -b .modernize ++ ++%build ++make CFLAGS="-g -O1" ++ ++%install ++rm -rf $RPM_BUILD_ROOT ++mkdir -p $RPM_BUILD_ROOT/usr/local/bin ++make DESTDIR=$RPM_BUILD_ROOT install ++ln $RPM_BUILD_ROOT/usr/local/bin/hello $RPM_BUILD_ROOT/usr/local/bin/hello2 ++ ++%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 ++%attr(0751,root,root) /usr/local/bin/hello2 ++ ++%changelog ++* Mon Jun 6 2016 Mark Wielaard ++- Hard link hello to hello2 for duplicate build-id testing. ++ ++* Wed May 18 2016 Mark Wielaard ++- Add hello2 for dwz testing support. ++ ++* Tue Oct 20 1998 Jeff Johnson ++- create. +diff --git a/tests/rpmbuildid.at b/tests/rpmbuildid.at +new file mode 100644 +index 0000000..eddca96 +--- /dev/null ++++ b/tests/rpmbuildid.at +@@ -0,0 +1,761 @@ ++# rpmbuildid.at: test rpmbuild buildid symlink support ++# ++# This file is part of RPM, the RPM Package Manager. ++# Copyright (C) 2016 Mark J. Wielaard ++# ++# This file is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 2 of the License, or ++# (at your option) any later version. ++# ++# RPM is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++AT_BANNER([RPM buildid tests]) ++ ++# ------------------------------ ++# Check if rpmbuild "none" doesn't generates buildid symlinks for hello program ++AT_SETUP([rpmbuild buildid none]) ++AT_KEYWORDS([build] [debuginfo] [buildid]) ++AT_CHECK([ ++rm -rf ${TOPDIR} ++AS_MKDIR_P(${TOPDIR}/SOURCES) ++ ++# Setup sources ++cp "${abs_srcdir}"/data/SOURCES/hello-1.0.tar.gz "${abs_srcdir}"/data/SOURCES/hello-1.0-modernize.patch ${TOPDIR}/SOURCES ++ ++# Build, contains one ELF which should have a buildid. ++run rpmbuild \ ++ --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="_build_id_links none" \ ++ --quiet -ba "${abs_srcdir}"/data/SPECS/hello.spec ++ ++# There should be zero build-id files in both the main and debuginfo package ++echo -n "hello build-id files: " ++run rpm -ql -p "${TOPDIR}"/RPMS/*/hello-1.0-1.*.rpm \ ++ | grep /.build-id/ | wc --lines ++ ++echo -n "hello debuginfo build-id files: " ++run rpm -ql -p "${TOPDIR}"/RPMS/*/hello-debuginfo-1.0-1.*.rpm \ ++ | grep /.build-id/ | wc --lines ++ ++], ++[0], ++[hello build-id files: 0 ++hello debuginfo build-id files: 0 ++], ++[ignore]) ++AT_CLEANUP ++ ++# ------------------------------ ++# Check if rpmbuild "alldebug" generates debuginfo buildid symlinks ++AT_SETUP([rpmbuild buildid alldebug]) ++AT_KEYWORDS([build] [debuginfo] [buildid]) ++AT_CHECK([ ++rm -rf ${TOPDIR} ++AS_MKDIR_P(${TOPDIR}/SOURCES) ++ ++# Setup sources ++cp "${abs_srcdir}"/data/SOURCES/hello-1.0.tar.gz "${abs_srcdir}"/data/SOURCES/hello-1.0-modernize.patch ${TOPDIR}/SOURCES ++ ++# Build, contains one ELF which should have a buildid. ++run rpmbuild \ ++ --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="_build_id_links alldebug" \ ++ --quiet -ba "${abs_srcdir}"/data/SPECS/hello.spec ++ ++# There should be zero build-id files in the main package ++# Main and debug should be in the debuginfo package, ++# plus the .build-id/xx subdir, 3 in total. ++echo -n "hello build-id files: " ++run rpm -ql -p "${TOPDIR}"/RPMS/*/hello-1.0-1.*.rpm \ ++ | grep /.build-id/ | wc --lines ++ ++echo -n "hello debuginfo build-id files: " ++run rpm -ql -p "${TOPDIR}"/RPMS/*/hello-debuginfo-1.0-1.*.rpm \ ++ | grep /.build-id/ | wc --lines ++ ++# Extract the both packages to check the build-id files link to the ++# main and .debug files. ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-1.0-1.*.rpm \ ++ | cpio -diu ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu ++ ++# Check there is a build-id symlink for the main file. ++main_file=./usr/local/bin/hello ++test -f "${main_file}" || echo "No main file ${main_file}" ++ ++# Extract the build-id from the main file ++id_main=$(file $main_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++id_main_file="./usr/lib/debug/.build-id/${id_main:0:2}/${id_main:2}" ++test -L "$id_main_file" || echo "No build-id file $id_main_file" ++ ++canon_main_file=$(readlink -f ${main_file}) ++ ++test -f "$canon_main_file" \ ++ || echo "Cannot resolve main file ${main_file} -> ${canon_main_file}" ++ ++canon_main_id_file=$(readlink -f ${id_main_file}) ++ ++test -f "$canon_main_id_file" \ ++ || echo "Cannot resolve main build-id file ${id_main_file} -> ${canon_main_id_file}" ++ ++test "$canon_main_file" = "$canon_main_id_file" \ ++ || echo "main and build-id file not linked" ++ ++# And check the same for the debug file. ++debug_file=./usr/lib/debug/usr/local/bin/hello.debug ++test -f "${debug_file}" || echo "No debug file ${debug_file}" ++ ++# Extract the build-id from the .debug file ++id_debug=$(file $debug_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++test ${id_main} = ${id_debug} || echo "unequal main and debug id" ++ ++id_debug_file="./usr/lib/debug/.build-id/${id_debug:0:2}/${id_debug:2}.debug" ++test -L "$id_debug_file" || echo "No build-id file $id_debug_file" ++ ++canon_debug_file=$(readlink -f ${debug_file}) ++ ++test -f "$canon_debug_file" \ ++ || echo "Cannot resolve debug file ${debug_file} -> ${canon_debug_file}" ++ ++canon_debug_id_file=$(readlink -f ${id_debug_file}) ++ ++test -f "$canon_debug_id_file" \ ++ || echo "Cannot resolve debug build-id file ${id_debug_file} -> ${canon_debug_id_file}" ++ ++test "$canon_debug_file" = "$canon_debug_id_file" \ ++ || echo "debug and build-id not linked" ++], ++[0], ++[hello build-id files: 0 ++hello debuginfo build-id files: 3 ++], ++[ignore]) ++AT_CLEANUP ++ ++# ------------------------------ ++# Check if rpmbuild "separate" generates main and debuginfo buildid symlinks ++AT_SETUP([rpmbuild buildid separate]) ++AT_KEYWORDS([build] [debuginfo] [buildid]) ++AT_CHECK([ ++rm -rf ${TOPDIR} ++AS_MKDIR_P(${TOPDIR}/SOURCES) ++ ++# Setup sources ++cp "${abs_srcdir}"/data/SOURCES/hello-1.0.tar.gz "${abs_srcdir}"/data/SOURCES/hello-1.0-modernize.patch ${TOPDIR}/SOURCES ++ ++# Build, contains one ELF which should have a buildid. ++run rpmbuild \ ++ --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="_build_id_links separate" \ ++ --quiet -ba "${abs_srcdir}"/data/SPECS/hello.spec ++ ++# There should be one build-id files in the main and debuginfo package ++# plus the .build-id/xx subdir, 2 in total. ++echo -n "hello build-id files: " ++run rpm -ql -p "${TOPDIR}"/RPMS/*/hello-1.0-1.*.rpm \ ++ | grep /.build-id/ | wc --lines ++ ++echo -n "hello debuginfo build-id files: " ++run rpm -ql -p "${TOPDIR}"/RPMS/*/hello-debuginfo-1.0-1.*.rpm \ ++ | grep /.build-id/ | wc --lines ++ ++# Extract the both packages to check the build-id files link to the ++# main and .debug files. ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-1.0-1.*.rpm \ ++ | cpio -diu ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu ++ ++# Check there is a build-id symlink for the main file. ++main_file=./usr/local/bin/hello ++test -f "${main_file}" || echo "No main file ${main_file}" ++ ++# Extract the build-id from the main file ++id_main=$(file $main_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++id_main_file="./usr/lib/.build-id/${id_main:0:2}/${id_main:2}" ++test -L "$id_main_file" || echo "No build-id file $id_main_file" ++ ++canon_main_file=$(readlink -f ${main_file}) ++ ++test -f "$canon_main_file" \ ++ || echo "Cannot resolve main file ${main_file} -> ${canon_main_file}" ++ ++canon_main_id_file=$(readlink -f ${id_main_file}) ++ ++test -f "$canon_main_id_file" \ ++ || echo "Cannot resolve main build-id file ${id_main_file} -> ${canon_main_id_file}" ++ ++test "$canon_main_file" = "$canon_main_id_file" \ ++ || echo "main and build-id file not linked" ++ ++# And check the same for the debug file. ++debug_file=./usr/lib/debug/usr/local/bin/hello.debug ++test -f "${debug_file}" || echo "No debug file ${debug_file}" ++ ++# Extract the build-id from the .debug file ++id_debug=$(file $debug_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++test ${id_main} = ${id_debug} || echo "unequal main and debug id" ++ ++id_debug_file="./usr/lib/debug/.build-id/${id_debug:0:2}/${id_debug:2}.debug" ++test -L "$id_debug_file" || echo "No build-id file $id_debug_file" ++ ++canon_debug_file=$(readlink -f ${debug_file}) ++ ++test -f "$canon_debug_file" \ ++ || echo "Cannot resolve debug file ${debug_file} -> ${canon_debug_file}" ++ ++canon_debug_id_file=$(readlink -f ${id_debug_file}) ++ ++test -f "$canon_debug_id_file" \ ++ || echo "Cannot resolve debug build-id file ${id_debug_file} -> ${canon_debug_id_file}" ++ ++test "$canon_debug_file" = "$canon_debug_id_file" \ ++ || echo "debug and build-id not linked" ++], ++[0], ++[hello build-id files: 2 ++hello debuginfo build-id files: 2 ++], ++[ignore]) ++AT_CLEANUP ++ ++# ------------------------------ ++# Check if rpmbuild "compat" generates main and debuginfo buildid symlinks ++AT_SETUP([rpmbuild buildid compat]) ++AT_KEYWORDS([build] [debuginfo] [buildid]) ++AT_CHECK([ ++rm -rf ${TOPDIR} ++AS_MKDIR_P(${TOPDIR}/SOURCES) ++ ++# Setup sources ++cp "${abs_srcdir}"/data/SOURCES/hello-1.0.tar.gz "${abs_srcdir}"/data/SOURCES/hello-1.0-modernize.patch ${TOPDIR}/SOURCES ++ ++# Build, contains one ELF which should have a buildid. ++run rpmbuild \ ++ --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="_build_id_links compat" \ ++ --quiet -ba "${abs_srcdir}"/data/SPECS/hello.spec ++ ++# There should be one build-id files in the main and debuginfo package. ++# the debuginfo package has one extra main build-id compat symlink ++# plus the .build-id/xx subdir, 2 in total in main, 3 in total in debug ++echo -n "hello build-id files: " ++run rpm -ql -p "${TOPDIR}"/RPMS/*/hello-1.0-1.*.rpm \ ++ | grep /.build-id/ | wc --lines ++ ++echo -n "hello debuginfo build-id files: " ++run rpm -ql -p "${TOPDIR}"/RPMS/*/hello-debuginfo-1.0-1.*.rpm \ ++ | grep /.build-id/ | wc --lines ++ ++# Extract the both packages to check the build-id files link to the ++# main and .debug files. ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-1.0-1.*.rpm \ ++ | cpio -diu ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu ++ ++# Check there is a build-id symlink for the main file. ++main_file=./usr/local/bin/hello ++test -f "${main_file}" || echo "No main file ${main_file}" ++ ++# Extract the build-id from the main file ++id_main=$(file $main_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++id_main_file="./usr/lib/.build-id/${id_main:0:2}/${id_main:2}" ++test -L "$id_main_file" || echo "No build-id file $id_main_file" ++ ++canon_main_file=$(readlink -f ${main_file}) ++ ++test -f "$canon_main_file" \ ++ || echo "Cannot resolve main file ${main_file} -> ${canon_main_file}" ++ ++canon_main_id_file=$(readlink -f ${id_main_file}) ++ ++test -f "$canon_main_id_file" \ ++ || echo "Cannot resolve main build-id file ${id_main_file} -> ${canon_main_id_file}" ++ ++test "$canon_main_file" = "$canon_main_id_file" \ ++ || echo "main and build-id file not linked" ++ ++# And check the same for the debug file. ++debug_file=./usr/lib/debug/usr/local/bin/hello.debug ++test -f "${debug_file}" || echo "No debug file ${debug_file}" ++ ++# Extract the build-id from the .debug file ++id_debug=$(file $debug_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++test ${id_main} = ${id_debug} || echo "unequal main and debug id" ++ ++id_debug_file="./usr/lib/debug/.build-id/${id_debug:0:2}/${id_debug:2}.debug" ++test -L "$id_debug_file" || echo "No build-id file $id_debug_file" ++ ++canon_debug_file=$(readlink -f ${debug_file}) ++ ++test -f "$canon_debug_file" \ ++ || echo "Cannot resolve debug file ${debug_file} -> ${canon_debug_file}" ++ ++canon_debug_id_file=$(readlink -f ${id_debug_file}) ++ ++test -f "$canon_debug_id_file" \ ++ || echo "Cannot resolve debug build-id file ${id_debug_file} -> ${canon_debug_id_file}" ++ ++test "$canon_debug_file" = "$canon_debug_id_file" \ ++ || echo "debug and build-id not linked" ++ ++# The compat link should also point to the same (indirectly). ++id_compat_file="./usr/lib/debug/.build-id/${id_main:0:2}/${id_main:2}" ++test -L "$id_compat_file" || echo "No build-id compat file $id_compat_file" ++ ++canon_compat_file=$(readlink -f ${id_compat_file}) ++ ++test -f "$canon_compat_file" \ ++ || echo "Cannot resolve compat file ${id_compat_file} -> ${canon_compat_file}" ++ ++test "$canon_compat_file" = "$canon_main_file" \ ++ || echo "compat and build-id not linked" ++], ++[0], ++[hello build-id files: 2 ++hello debuginfo build-id files: 3 ++], ++[ignore]) ++AT_CLEANUP ++ ++# ------------------------------ ++# Check that (copied) files with duplicate build-ids are handled correctly. ++# This should create "numbered" build-id files. ++# This is simply the hello example with one binary copied. ++AT_SETUP([rpmbuild buildid duplicate alldebug]) ++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 ++ ++# Should create two warnings ++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="_build_id_links alldebug" \ ++ -ba "${abs_srcdir}"/data/SPECS/hello2cp.spec 2>&1 | grep "^warning: " \ ++ | cut -f1-3 -d' ' ++ ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-1.0-1.*.rpm \ ++ | cpio -diu --quiet ++ ++hello_file=./usr/local/bin/hello ++ ++# Extract the build-id from the main file ++id=$(file $hello_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++# alldebug not here... ++id_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}" ++test -L "$id_file" && echo "main id in main package" ++id_dup_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}.1" ++test -L "$id_dup_file" && echo "main dup id in main package" ++ ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu --quiet ++ ++# alldebug, so they are all here ++test -L "$id_file" && echo "main id in debug package" ++test -L "$id_dup_file" && echo "main dup id in debug package" ++ ++debug_id_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}.debug" ++test -L "$debug_id_file" && echo "debug id in debug package" ++debug_dup_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}.1.debug" ++test -L "$debug_dup_file" && echo "debug dup id in debug package" ++ ++# We don't know which points to which, but we do know they point ++# to different files. ++canon_id_file=$(readlink -f ${id_file}) ++canon_dup_file=$(readlink -f ${id_dup_file}) ++test "$canon_id_file" != "$canon_dup_file" \ ++ || echo "id and dup same" ++ ++canon_debug_id_file=$(readlink -f ${debug_id_file}) ++canon_debug_dup_file=$(readlink -f ${debug_dup_file}) ++test "$canon_debug_id_file" != "$canon_debug_dup_file" \ ++ || echo "debug id and dup same" ++], ++[0], ++[warning: Duplicate build-ids ++warning: Duplicate build-ids ++main id in debug package ++main dup id in debug package ++debug id in debug package ++debug dup id in debug package ++], ++[]) ++AT_CLEANUP ++ ++# ------------------------------ ++# Check that hard linked files are handled correctly. ++# Since the hard linked files have duplicate build-ids, ++# it should create "numbered" build-id files. ++# This is simply the hello example with one binary hard linked. ++AT_SETUP([rpmbuild buildid hardlink alldebug]) ++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="_build_id_links alldebug" \ ++ -ba "${abs_srcdir}"/data/SPECS/hello2ln.spec 2>&1 | grep "^warning: " \ ++ | cut -f1-3 -d' ' ++ ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-1.0-1.*.rpm \ ++ | cpio -diu --quiet ++ ++hello_file=./usr/local/bin/hello ++ ++# Extract the build-id from the main file ++id=$(file $hello_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++# alldebug not here... ++id_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}" ++test -L "$id_file" && echo "main id in main package" ++id_dup_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}.1" ++test -L "$id_dup_file" && echo "main dup id in main package" ++ ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu --quiet ++ ++# alldebug, so they are all here ++test -L "$id_file" && echo "main id in debug package" ++test -L "$id_dup_file" && echo "main dup id in debug package" ++ ++debug_id_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}.debug" ++test -L "$debug_id_file" && echo "debug id in debug package" ++debug_dup_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}.1.debug" ++test -L "$debug_dup_file" && echo "debug dup id in debug package" ++ ++# We don't know which points to which, but we do know they point ++# to different files. ++canon_id_file=$(readlink -f ${id_file}) ++canon_dup_file=$(readlink -f ${id_dup_file}) ++test "$canon_id_file" != "$canon_dup_file" \ ++ || echo "id and dup same" ++ ++canon_debug_id_file=$(readlink -f ${debug_id_file}) ++canon_debug_dup_file=$(readlink -f ${debug_dup_file}) ++test "$canon_debug_id_file" != "$canon_debug_dup_file" \ ++ || echo "debug id and dup same" ++], ++[0], ++[main id in debug package ++main dup id in debug package ++debug id in debug package ++debug dup id in debug package ++], ++[]) ++AT_CLEANUP ++ ++# ------------------------------ ++# Check that (copied) files with duplicate build-ids are handled correctly. ++# This should create "numbered" build-id files. ++# This is simply the hello example with one binary copied. ++AT_SETUP([rpmbuild buildid duplicate separate]) ++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 ++ ++# Should create two warnings ++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="_build_id_links separate" \ ++ -ba "${abs_srcdir}"/data/SPECS/hello2cp.spec 2>&1 | grep "^warning: " \ ++ | cut -f1-3 -d' ' ++ ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-1.0-1.*.rpm \ ++ | cpio -diu --quiet ++ ++hello_file=./usr/local/bin/hello ++ ++# Extract the build-id from the main file ++id=$(file $hello_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++# separate build-ids split... ++id_file="./usr/lib/.build-id/${id:0:2}/${id:2}" ++test -L "$id_file" && echo "main id in main package" ++id_dup_file="./usr/lib/.build-id/${id:0:2}/${id:2}.1" ++test -L "$id_dup_file" && echo "main dup id in main package" ++ ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu --quiet ++ ++# seperate, so debug ids are here ++debug_id_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}.debug" ++test -L "$debug_id_file" && echo "debug id in debug package" ++debug_dup_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}.1.debug" ++test -L "$debug_dup_file" && echo "debug dup id in debug package" ++ ++# We don't know which points to which, but we do know they point ++# to different files. ++canon_id_file=$(readlink -f ${id_file}) ++canon_dup_file=$(readlink -f ${id_dup_file}) ++test "$canon_id_file" != "$canon_dup_file" \ ++ || echo "id and dup same" ++ ++canon_debug_id_file=$(readlink -f ${debug_id_file}) ++canon_debug_dup_file=$(readlink -f ${debug_dup_file}) ++test "$canon_debug_id_file" != "$canon_debug_dup_file" \ ++ || echo "debug id and dup same" ++], ++[0], ++[warning: Duplicate build-ids ++warning: Duplicate build-ids ++main id in main package ++main dup id in main package ++debug id in debug package ++debug dup id in debug package ++], ++[]) ++AT_CLEANUP ++ ++# ------------------------------ ++# Check that hard linked files are handled correctly. ++# Since the hard linked files have duplicate build-ids, ++# it should create "numbered" build-id files. ++# This is simply the hello example with one binary hard linked. ++AT_SETUP([rpmbuild buildid hardlink separate]) ++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="_build_id_links separate" \ ++ -ba "${abs_srcdir}"/data/SPECS/hello2ln.spec 2>&1 | grep "^warning: " \ ++ | cut -f1-3 -d' ' ++ ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-1.0-1.*.rpm \ ++ | cpio -diu --quiet ++ ++hello_file=./usr/local/bin/hello ++ ++# Extract the build-id from the main file ++id=$(file $hello_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++# separate build-ids split... ++id_file="./usr/lib/.build-id/${id:0:2}/${id:2}" ++test -L "$id_file" && echo "main id in main package" ++id_dup_file="./usr/lib/.build-id/${id:0:2}/${id:2}.1" ++test -L "$id_dup_file" && echo "main dup id in main package" ++ ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu --quiet ++ ++# separate, so debug ids are here ++debug_id_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}.debug" ++test -L "$debug_id_file" && echo "debug id in debug package" ++debug_dup_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}.1.debug" ++test -L "$debug_dup_file" && echo "debug dup id in debug package" ++ ++# We don't know which points to which, but we do know they point ++# to different files. ++canon_id_file=$(readlink -f ${id_file}) ++canon_dup_file=$(readlink -f ${id_dup_file}) ++test "$canon_id_file" != "$canon_dup_file" \ ++ || echo "id and dup same" ++ ++canon_debug_id_file=$(readlink -f ${debug_id_file}) ++canon_debug_dup_file=$(readlink -f ${debug_dup_file}) ++test "$canon_debug_id_file" != "$canon_debug_dup_file" \ ++ || echo "debug id and dup same" ++], ++[0], ++[main id in main package ++main dup id in main package ++debug id in debug package ++debug dup id in debug package ++], ++[]) ++AT_CLEANUP ++ ++# ------------------------------ ++# Check that (copied) files with duplicate build-ids are handled correctly. ++# This should create "numbered" build-id files. ++# This is simply the hello example with one binary copied. ++AT_SETUP([rpmbuild buildid duplicate compat]) ++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 ++ ++# Should create two warnings ++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="_build_id_links compat" \ ++ -ba "${abs_srcdir}"/data/SPECS/hello2cp.spec 2>&1 | grep "^warning: " \ ++ | cut -f1-3 -d' ' ++ ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-1.0-1.*.rpm \ ++ | cpio -diu --quiet ++ ++hello_file=./usr/local/bin/hello ++ ++# Extract the build-id from the main file ++id=$(file $hello_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++# compat build-ids split... ++id_file="./usr/lib/.build-id/${id:0:2}/${id:2}" ++test -L "$id_file" && echo "main id in main package" ++id_dup_file="./usr/lib/.build-id/${id:0:2}/${id:2}.1" ++test -L "$id_dup_file" && echo "main dup id in main package" ++ ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu --quiet ++ ++# compat, so main (and debug) ids are (also) here ++compat_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}" ++test -L "$compat_file" && echo "compat id in debug package" ++compat_dup_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}.1" ++test -L "$compat_dup_file" && echo "compat dup id in debug package" ++ ++debug_id_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}.debug" ++test -L "$debug_id_file" && echo "debug id in debug package" ++debug_dup_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}.1.debug" ++test -L "$debug_dup_file" && echo "debug dup id in debug package" ++ ++# We don't know which points to which, but we do know they point ++# to different files. ++canon_id_file=$(readlink -f ${id_file}) ++canon_dup_file=$(readlink -f ${id_dup_file}) ++test "$canon_id_file" != "$canon_dup_file" \ ++ || echo "id and dup same" ++ ++canon_debug_id_file=$(readlink -f ${debug_id_file}) ++canon_debug_dup_file=$(readlink -f ${debug_dup_file}) ++test "$canon_debug_id_file" != "$canon_debug_dup_file" \ ++ || echo "debug id and dup same" ++ ++canon_compat_file=$(readlink -f ${compat_file}) ++canon_compat_dup_file=$(readlink -f ${compat_dup_file}) ++test "$canon_compat_file" != "$canon_compat_dup_file" \ ++ || echo "compat id and dup same" ++], ++[0], ++[warning: Duplicate build-ids ++warning: Duplicate build-ids ++main id in main package ++main dup id in main package ++compat id in debug package ++compat dup id in debug package ++debug id in debug package ++debug dup id in debug package ++], ++[]) ++AT_CLEANUP ++ ++# ------------------------------ ++# Check that hard linked files are handled correctly. ++# Since the hard linked files have duplicate build-ids, ++# it should create "numbered" build-id files. ++# This is simply the hello example with one binary hard linked. ++AT_SETUP([rpmbuild buildid hardlink compat]) ++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="_build_id_links compat" \ ++ -ba "${abs_srcdir}"/data/SPECS/hello2ln.spec 2>&1 | grep "^warning: " \ ++ | cut -f1-3 -d' ' ++ ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-1.0-1.*.rpm \ ++ | cpio -diu --quiet ++ ++hello_file=./usr/local/bin/hello ++ ++# Extract the build-id from the main file ++id=$(file $hello_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++# compat build-ids split... ++id_file="./usr/lib/.build-id/${id:0:2}/${id:2}" ++test -L "$id_file" && echo "main id in main package" ++id_dup_file="./usr/lib/.build-id/${id:0:2}/${id:2}.1" ++test -L "$id_dup_file" && echo "main dup id in main package" ++ ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu --quiet ++ ++# compat, so main (and debug) ids are (also) here ++compat_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}" ++test -L "$compat_file" && echo "compat id in debug package" ++compat_dup_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}.1" ++test -L "$compat_dup_file" && echo "compat dup id in debug package" ++ ++debug_id_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}.debug" ++test -L "$debug_id_file" && echo "debug id in debug package" ++debug_dup_file="./usr/lib/debug/.build-id/${id:0:2}/${id:2}.1.debug" ++test -L "$debug_dup_file" && echo "debug dup id in debug package" ++ ++# We don't know which points to which, but we do know they point ++# to different files. ++canon_id_file=$(readlink -f ${id_file}) ++canon_dup_file=$(readlink -f ${id_dup_file}) ++test "$canon_id_file" != "$canon_dup_file" \ ++ || echo "id and dup same" ++ ++canon_debug_id_file=$(readlink -f ${debug_id_file}) ++canon_debug_dup_file=$(readlink -f ${debug_dup_file}) ++test "$canon_debug_id_file" != "$canon_debug_dup_file" \ ++ || echo "debug id and dup same" ++ ++canon_compat_file=$(readlink -f ${compat_file}) ++canon_compat_dup_file=$(readlink -f ${compat_dup_file}) ++test "$canon_compat_file" != "$canon_compat_dup_file" \ ++ || echo "compat id and dup same" ++], ++[0], ++[main id in main package ++main dup id in main package ++compat id in debug package ++compat dup id in debug package ++debug id in debug package ++debug dup id in debug package ++], ++[]) ++AT_CLEANUP +\ No newline at end of file +diff --git a/tests/rpmtests.at b/tests/rpmtests.at +index b51266a..5495cce 100644 +--- a/tests/rpmtests.at ++++ b/tests/rpmtests.at +@@ -4,6 +4,7 @@ m4_include([rpmverify.at]) + m4_include([rpmdb.at]) + m4_include([rpmi.at]) + m4_include([rpmbuild.at]) ++m4_include([rpmbuildid.at]) + m4_include([rpmscript.at]) + m4_include([rpmvercmp.at]) + m4_include([rpmdeps.at]) +-- +2.9.3 + diff --git a/0002-Make-it-possible-to-have-unique-build-ids-across-bui.patch b/0002-Make-it-possible-to-have-unique-build-ids-across-bui.patch new file mode 100644 index 0000000..dc7ca3e --- /dev/null +++ b/0002-Make-it-possible-to-have-unique-build-ids-across-bui.patch @@ -0,0 +1,351 @@ +From 2a97fb48279af17049f96c661db040173185a650 Mon Sep 17 00:00:00 2001 +Message-Id: <2a97fb48279af17049f96c661db040173185a650.1488964568.git.pmatilai@redhat.com> +In-Reply-To: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +References: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +From: Mark Wielaard +Date: Tue, 14 Jun 2016 17:07:13 +0200 +Subject: [PATCH 02/11] 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 +--- + 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 e43d62b0..dcd0961 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 c9e2293..2cb9570 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 0000000..ca5091d +--- /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 ++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 ++- Update release for unique build-id generation tests. ++ ++* Tue Oct 20 1998 Jeff Johnson ++- create. +diff --git a/tests/rpmbuildid.at b/tests/rpmbuildid.at +index eddca96..1da6302 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 cf89312..c0147f0 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 , 2002 + Based on code by Jakub Jelinek , 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); +-- +2.9.3 + diff --git a/0003-Make-adding-GDB-index-sections-configurable.patch b/0003-Make-adding-GDB-index-sections-configurable.patch new file mode 100644 index 0000000..e8722bc --- /dev/null +++ b/0003-Make-adding-GDB-index-sections-configurable.patch @@ -0,0 +1,214 @@ +From 270b8627b03bc39fb008a7da1a4fd6b259ff92b8 Mon Sep 17 00:00:00 2001 +Message-Id: <270b8627b03bc39fb008a7da1a4fd6b259ff92b8.1488964568.git.pmatilai@redhat.com> +In-Reply-To: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +References: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +From: Mark Wielaard +Date: Tue, 14 Jun 2016 17:07:14 +0200 +Subject: [PATCH 03/11] Make adding GDB index sections configurable. + +Introduces _include_gdb_index macro and -i flag to find-debuginfo.sh to +enable or disable adding a .gdb_index section to debug files. Adds tests +to make sure the .gdb_index is really added (or not) when requested. +Checks that gdb-add-index is actually installed instead of silently +failing if not. Similar for dwz. + +Signed-off-by: Mark Wielaard +--- + macros.debug | 1 + + macros.in | 8 ++++++- + scripts/find-debuginfo.sh | 29 ++++++++++++++++++---- + tests/rpmbuild.at | 61 +++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 94 insertions(+), 5 deletions(-) + +diff --git a/macros.debug b/macros.debug +index 6a8432e..d273c08 100644 +--- a/macros.debug ++++ b/macros.debug +@@ -2,6 +2,7 @@ + + %_enable_debug_packages 1 + %_include_minidebuginfo 1 ++%_include_gdb_index 1 + + # Expanded at end of %install scriptlet + +diff --git a/macros.in b/macros.in +index dcd0961..c845f58 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} %{?_unique_build_ids:--ver-rel "%{version}-%{release}"} %{?_find_debuginfo_dwz_opts} %{?_find_debuginfo_opts} "%{_builddir}/%{?buildsubdir}"\ ++ %{_rpmconfigdir}/find-debuginfo.sh %{?_missing_build_ids_terminate_build:--strict-build-id} %{?_include_minidebuginfo:-m} %{?_include_gdb_index:-i} %{?_unique_build_ids:--ver-rel "%{version}-%{release}"} %{?_find_debuginfo_dwz_opts} %{?_find_debuginfo_opts} "%{_builddir}/%{?buildsubdir}"\ + %{nil} + + # Template for debug information sub-package. +@@ -449,6 +449,12 @@ package or when debugging this package.\ + #%_include_minidebuginfo 1 + + # ++# Include a .gdb_index section in the .debug files. ++# Requires _enable_debug_packages and gdb-add-index installed. ++# ++#%_include_gdb_index 1 ++ ++# + # Defines how and if build_id links are generated for ELF files. + # The following settings are supported: + # +diff --git a/scripts/find-debuginfo.sh b/scripts/find-debuginfo.sh +index 2cb9570..8e60813 100644 +--- a/scripts/find-debuginfo.sh ++++ b/scripts/find-debuginfo.sh +@@ -2,7 +2,7 @@ + #find-debuginfo.sh - automagically generate debug info and file list + #for inclusion in an rpm spec file. + # +-# Usage: find-debuginfo.sh [--strict-build-id] [-g] [-r] [-m] ++# Usage: find-debuginfo.sh [--strict-build-id] [-g] [-r] [-m] [-i] + # [-o debugfiles.list] + # [--run-dwz] [--dwz-low-mem-die-limit N] + # [--dwz-max-die-limit N] +@@ -14,6 +14,8 @@ + # The --strict-build-id flag says to exit with failure status if + # any ELF binary processed fails to contain a build-id note. + # The -r flag says to use eu-strip --reloc-debug-sections. ++# The -m flag says to include a .gnu_debugdata section in the main binary. ++# The -i flag says to include a .gdb_index section in the .debug file. + # + # A single -o switch before any -l or -p switches simply renames + # the primary output file from debugfiles.list to something else. +@@ -48,6 +50,9 @@ strip_r=false + # with -m arg, add minimal debuginfo to binary. + include_minidebug=false + ++# with -i arg, add GDB index to .debug file. ++include_gdb_index=false ++ + # Barf on missing build IDs. + strict=false + +@@ -88,6 +93,9 @@ while [ $# -gt 0 ]; do + -m) + include_minidebug=true + ;; ++ -i) ++ include_gdb_index=true ++ ;; + -o) + if [ -z "${lists[$nout]}" -a -z "${ptns[$nout]}" ]; then + out=$2 +@@ -277,7 +285,15 @@ while read nlinks inum f; do + $strict && exit 2 + fi + +- type gdb-add-index >/dev/null 2>&1 && gdb-add-index "$f" > /dev/null 2>&1 ++ # Add .gdb_index if requested. ++ if $include_gdb_index; then ++ if type gdb-add-index >/dev/null 2>&1; then ++ gdb-add-index "$f" ++ else ++ echo >&2 "*** ERROR: GDB index requested, but no gdb-add-index installed" ++ exit 2 ++ fi ++ fi + + # A binary already copied into /usr/lib/debug doesn't get stripped, + # just has its file names collected and adjusted. +@@ -303,7 +319,7 @@ while read nlinks inum f; do + done || exit + + # Invoke the DWARF Compressor utility. +-if $run_dwz && type dwz >/dev/null 2>&1 \ ++if $run_dwz \ + && [ -d "${RPM_BUILD_ROOT}/usr/lib/debug" ]; then + dwz_files="`cd "${RPM_BUILD_ROOT}/usr/lib/debug"; find -type f -name \*.debug`" + if [ -n "${dwz_files}" ]; then +@@ -321,7 +337,12 @@ if $run_dwz && type dwz >/dev/null 2>&1 \ + && dwz_opts="${dwz_opts} -l ${dwz_low_mem_die_limit}" + [ -n "${dwz_max_die_limit}" ] \ + && dwz_opts="${dwz_opts} -L ${dwz_max_die_limit}" +- ( cd "${RPM_BUILD_ROOT}/usr/lib/debug" && dwz $dwz_opts $dwz_files ) ++ if type dwz >/dev/null 2>&1; then ++ ( cd "${RPM_BUILD_ROOT}/usr/lib/debug" && dwz $dwz_opts $dwz_files ) ++ else ++ echo >&2 "*** ERROR: DWARF compression requested, but no dwz installed" ++ exit 2 ++ fi + # Remove .dwz directory if empty + rmdir "${RPM_BUILD_ROOT}/usr/lib/debug/.dwz" 2>/dev/null + if [ -f "${RPM_BUILD_ROOT}/usr/lib/debug/.dwz/${dwz_multifile_name}" ]; then +diff --git a/tests/rpmbuild.at b/tests/rpmbuild.at +index 1531700..a312324 100644 +--- a/tests/rpmbuild.at ++++ b/tests/rpmbuild.at +@@ -532,3 +532,64 @@ usr/local/bin/hello2 + ], + [ignore]) + AT_CLEANUP ++ ++# ------------------------------ ++# Check that a GDB index is included when requested. ++AT_SETUP([rpmbuild debuginfo gdb index included]) ++AT_KEYWORDS([build] [debuginfo] [gdb]) ++AT_CHECK([ ++rm -rf ${TOPDIR} ++AS_MKDIR_P(${TOPDIR}/SOURCES) ++ ++# Build a package that has some debuginfo ++cp "${abs_srcdir}"/data/SOURCES/hello-1.0.tar.gz "${abs_srcdir}"/data/SOURCES/hello-1.0-modernize.patch ${TOPDIR}/SOURCES ++ ++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 "_include_gdb_index 1" \ ++ -ba "${abs_srcdir}"/data/SPECS/hello2.spec ++ ++# Unpack the debuginfo rpms so we can check the .debug files. ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu --quiet ++ ++# Check that gdb-add-index has ran and a .gdb_index section has been added ++readelf -S ./usr/lib/debug/usr/local/bin/hello2*.debug \ ++ | grep gdb_index | cut -c8-17 ++], ++[0], ++[.gdb_index ++], ++[ignore]) ++AT_CLEANUP ++ ++# ------------------------------ ++# Check that a GDB index is NOT included when not requested. ++AT_SETUP([rpmbuild debuginfo no gdb index included]) ++AT_KEYWORDS([build] [debuginfo] [gdb]) ++AT_CHECK([ ++rm -rf ${TOPDIR} ++AS_MKDIR_P(${TOPDIR}/SOURCES) ++ ++# Build a package that has some debuginfo ++cp "${abs_srcdir}"/data/SOURCES/hello-1.0.tar.gz "${abs_srcdir}"/data/SOURCES/hello-1.0-modernize.patch ${TOPDIR}/SOURCES ++ ++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 "_include_gdb_index" \ ++ -ba "${abs_srcdir}"/data/SPECS/hello2.spec ++ ++# Unpack the debuginfo rpms so we can check the .debug files. ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu --quiet ++ ++# Check that gdb-add-index has not ran and no .gdb_index section has been added ++readelf -S ./usr/lib/debug/usr/local/bin/hello2*.debug \ ++ | grep gdb_index | cut -c8-17 ++], ++[0], ++[], ++[ignore]) ++AT_CLEANUP +-- +2.9.3 + diff --git a/0004-Add-option-to-have-unique-debug-file-names-across-ve.patch b/0004-Add-option-to-have-unique-debug-file-names-across-ve.patch new file mode 100644 index 0000000..ae3bcc3 --- /dev/null +++ b/0004-Add-option-to-have-unique-debug-file-names-across-ve.patch @@ -0,0 +1,808 @@ +From 76e637c715e13fe7f746feb29af4a6fd0de3cbc7 Mon Sep 17 00:00:00 2001 +Message-Id: <76e637c715e13fe7f746feb29af4a6fd0de3cbc7.1488964568.git.pmatilai@redhat.com> +In-Reply-To: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +References: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +From: Mark Wielaard +Date: Thu, 16 Jun 2016 14:24:22 +0200 +Subject: [PATCH 04/11] Add option to have unique debug file names across + version/release/arch. + +Introduce a new macro _unique_debug_names that when set will pass +--unique-debug-arch "%{_arch}" to find-debuginfo.sh to create debuginfo +files which end in "--..debug" instead of simply ".debug". + +Adds testcases for dwz and buildid with and without unique debug file names. + +Signed-off-by: Mark Wielaard +--- + build/files.c | 19 ++- + macros.debug | 3 + + macros.in | 9 +- + scripts/find-debuginfo.sh | 18 ++- + tests/rpmbuild.at | 168 +++++++++++++++++++----- + tests/rpmbuildid.at | 316 +++++++++++++++++++++++++++++++++++++++++++++- + 6 files changed, 494 insertions(+), 39 deletions(-) + +diff --git a/build/files.c b/build/files.c +index b010483..eb39856 100644 +--- a/build/files.c ++++ b/build/files.c +@@ -1790,6 +1790,16 @@ static int generateBuildIDs(FileList fl) + } + } + ++ /* In case we need ALLDEBUG links we might need the vra as ++ tagged onto the .debug file name. */ ++ char *vra = NULL; ++ if (rc == 0 && needDbg && build_id_links == BUILD_IDS_ALLDEBUG) { ++ int unique_debug_names = ++ rpmExpandNumeric("%{?_unique_debug_names}"); ++ if (unique_debug_names == 1) ++ vra = rpmExpand("-%{version}-%{release}.%{_arch}", NULL); ++ } ++ + /* Now add a subdir and symlink for each buildid found. */ + for (i = 0; i < nr_ids; i++) { + /* Don't add anything more when an error occured. But do +@@ -1884,15 +1894,17 @@ static int generateBuildIDs(FileList fl) + int pathlen = strlen(paths[i]); + int debuglen = strlen(".debug"); + int prefixlen = strlen("/usr/lib/debug"); +- if (pathlen > prefixlen +- && strcmp (paths[i] + pathlen - debuglen, ++ int vralen = vra == NULL ? 0 : strlen(vra); ++ if (pathlen > prefixlen + debuglen + vralen ++ && strcmp ((paths[i] + pathlen - debuglen), + ".debug") == 0) { + free(linkpath); + free(targetpath); + char *targetstr = xstrdup (paths[i] + + prefixlen); + int targetlen = pathlen - prefixlen; +- targetstr[targetlen - debuglen] = '\0'; ++ int targetend = targetlen - debuglen - vralen; ++ targetstr[targetend] = '\0'; + rasprintf(&linkpath, "%s/%s", + buildidsubdir, &ids[i][2]); + rasprintf(&targetpath, "../../../../..%s", +@@ -1911,6 +1923,7 @@ static int generateBuildIDs(FileList fl) + free(paths[i]); + free(ids[i]); + } ++ free(vra); + free(paths); + free(ids); + } +diff --git a/macros.debug b/macros.debug +index d273c08..ee0cc9e 100644 +--- a/macros.debug ++++ b/macros.debug +@@ -1,4 +1,7 @@ + # macros to include to generate debuginfo ++# Note don't define/enable a feature here if it is already the default in ++# macros.in. Otherwise it cannot simply be --undefined on the command line ++# in the tests (it needs to be undefined multiple times then). + + %_enable_debug_packages 1 + %_include_minidebuginfo 1 +diff --git a/macros.in b/macros.in +index c845f58..68bf391 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} %{?_include_gdb_index:-i} %{?_unique_build_ids:--ver-rel "%{version}-%{release}"} %{?_find_debuginfo_dwz_opts} %{?_find_debuginfo_opts} "%{_builddir}/%{?buildsubdir}"\ ++ %{_rpmconfigdir}/find-debuginfo.sh %{?_missing_build_ids_terminate_build:--strict-build-id} %{?_include_minidebuginfo:-m} %{?_include_gdb_index:-i} %{?_unique_build_ids:--ver-rel "%{version}-%{release}"} %{?_unique_debug_names:--unique-debug-arch "%{_arch}"} %{?_find_debuginfo_dwz_opts} %{?_find_debuginfo_opts} "%{_builddir}/%{?buildsubdir}"\ + %{nil} + + # Template for debug information sub-package. +@@ -488,6 +488,13 @@ package or when debugging this package.\ + # onto debugedit --build-id-seed to be used to prime the build-id note hash. + %_unique_build_ids 1 + ++# Whether .debug files should be made unique between package version, ++# release and architecture. If set to 1 this will pass ++# --unique-debug-arch "%{_arch}" to find-debuginfo.sh to create ++# debuginfo files which end in --..debug ++# Requires _unique_build_ids. ++%_unique_debug_names 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 8e60813..3653c48 100644 +--- a/scripts/find-debuginfo.sh ++++ b/scripts/find-debuginfo.sh +@@ -64,6 +64,9 @@ dwz_max_die_limit= + # Version and release of the spec. Given by --ver-rel + ver_rel= + ++# Arch given by --unique-debug-arch ++unique_debug_arch= ++ + BUILDDIR=. + out=debugfiles.list + nout=0 +@@ -87,6 +90,10 @@ while [ $# -gt 0 ]; do + ver_rel=$2 + shift + ;; ++ --unique-debug-arch) ++ unique_debug_arch=$2 ++ shift ++ ;; + -g) + strip_g=true + ;; +@@ -125,6 +132,11 @@ while [ $# -gt 0 ]; do + shift + done + ++if test -z "$ver_rel" -a -n "$unique_debug_arch"; then ++ echo >&2 "*** ERROR: --unique-debug-arch (${unique_debug_arch}) needs --ver-rel (${ver_rel})" ++ exit 2 ++fi ++ + i=0 + while ((i < nout)); do + outs[$i]="$BUILDDIR/${outs[$i]}" +@@ -232,7 +244,11 @@ debug_link() + get_debugfn() + { + dn=$(dirname "${1#$RPM_BUILD_ROOT}") +- bn=$(basename "$1" .debug).debug ++ if test -n "${unique_debug_arch}"; then ++ bn=$(basename "$1" .debug)-${ver_rel}.${unique_debug_arch}.debug ++ else ++ bn=$(basename "$1" .debug).debug ++ fi + + debugdn=${debugdir}${dn} + debugfn=${debugdn}/${bn} +diff --git a/tests/rpmbuild.at b/tests/rpmbuild.at +index a312324..0a2c01e 100644 +--- a/tests/rpmbuild.at ++++ b/tests/rpmbuild.at +@@ -386,8 +386,9 @@ AT_CLEANUP + # Check if rpmbuild runs dwz and generates a multi file that with shared + # debuginfo. This is simply the hello example with one binary build twice + # so dwz has enough slightly similar debug data. ++# Test the case without unique debug file names. + AT_SETUP([rpmbuild debuginfo dwz]) +-AT_KEYWORDS([build] [debuginfo]) ++AT_KEYWORDS([build] [debuginfo] [dwz]) + AT_CHECK([ + rm -rf ${TOPDIR} + AS_MKDIR_P(${TOPDIR}/SOURCES) +@@ -397,63 +398,170 @@ cp "${abs_srcdir}"/data/SOURCES/hello-1.0.tar.gz "${abs_srcdir}"/data/SOURCES/he + 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_debug_names" \ + -ba "${abs_srcdir}"/data/SPECS/hello2.spec + + # The debuginfo package should contain a .debug file for each binary + # and a dwz multi file that contains the shared debuginfo between them. + rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-debuginfo-1.0-1.*.rpm \ + | cpio -diu +-test -f ./usr/lib/debug/usr/local/bin/hello.debug || exit 1 +-test -f ./usr/lib/debug/usr/local/bin/hello2.debug || exit 1 +-test -f ./usr/lib/debug/.dwz/hello2-1.0-1.* || exit 1 ++ ++hello_file_debug=./usr/lib/debug/usr/local/bin/hello.debug ++hello2_file_debug=./usr/lib/debug/usr/local/bin/hello2.debug ++hello_multi_file=./usr/lib/debug/.dwz/hello2-1.0-1.* ++test -f $hello_file_debug || echo "no hello debug file: $hello_file_debug" ++test -f $hello2_file_debug || echo "no hello2 debug file: $hello2_file_debug" ++test -f $hello_multi_file || echo "no dwz multi file: $hello_multi_file" + + # Make sure the main package binaries contain the correct build-ids + # linking them to the debug packages. + rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-1.0-1.*.rpm \ + | cpio -diu +-id1=$(file ./usr/local/bin/hello | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') +-id2=$(file ./usr/local/bin/hello2 | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') +-id1debug=$(file ./usr/lib/debug/usr/local/bin/hello.debug \ +- | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') +-id2debug=$(file ./usr/lib/debug/usr/local/bin/hello2.debug \ +- | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') +-idmulti=$(file ./usr/lib/debug/.dwz/hello2-1.0-1.* \ +- | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') +- +-test "$id1" = "$id1debug" || exit 1 +-test "$id2" = "$id2debug" || exit 1 ++hello_file=./usr/local/bin/hello ++hello2_file=./usr/local/bin/hello2 ++test -f $hello_file || echo "no hello file: $hello_file" ++test -f $hello2_file || echo "no hello2 file: $hello2_file" ++ ++id1=$(file $hello_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++id2=$(file $hello2_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++id1debug=$(file $hello_file_debug | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++id2debug=$(file $hello2_file_debug | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++idmulti=$(file $hello_multi_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++test "$id1" = "$id1debug" || echo "id1: $id1 != id1debug: $id1debug" ++test "$id2" = "$id2debug" || echo "id2: $id2 != id2debug: $id2debug" ++ ++# The build-id files should link to the .debug files. ++id1file="./usr/lib/debug/.build-id/${id1:0:2}/${id1:2}" ++canonid1file=$(readlink -f ${id1file}) ++canonfile1=$(readlink -f $hello_file) ++canonid1debug=$(readlink -f ${id1file}.debug) ++canondebug1=$(readlink -f $hello_file_debug) ++ ++test "$canonid1file" = "$canonfile1" \ ++ || echo "canonid1file: $canonid1file != $canonfile1" ++test "$canonid1debug" = "$canondebug1" \ ++ || echo "canonid1debug: $canonid1debug != $canondebug1" ++ ++id2file="./usr/lib/debug/.build-id/${id2:0:2}/${id2:2}" ++canonid2file=$(readlink -f ${id2file}) ++canonfile2=$(readlink -f $hello2_file) ++canonid2debug=$(readlink -f ${id2file}.debug) ++canondebug2=$(readlink -f $hello2_file_debug) ++ ++test "$canonid2file" = "$canonfile2" \ ++ || echo "canonid2: $canonid2file != $canonfile2" ++test "$canonid2debug" = "$canondebug2" \ ++ || echo "canonid2debug: $canonid2debug" != "$canondebug2" ++ ++# Both .debug files should point to the dwz multi file. ++# It would be nice to also test that they contain the correct dwz build-id ++# but that is a bit hard to grep out of the section data. ++multiref1=$(readelf --string-dump=.gnu_debugaltlink $hello_file_debug \ ++ | grep '[ 0]' | cut -c13-) ++multiref2=$(readelf --string-dump=.gnu_debugaltlink $hello2_file_debug \ ++ | grep '[ 0]' | cut -c13-) ++ ++test "$multiref1" = "$multiref2" || echo "multiref: $multiref1 != $multiref2" ++ ++canonmultiref=$(readlink -f $(dirname $canondebug1)/$multiref1) ++canonmultifile=$(readlink -f $hello_multi_file) ++ ++test "$canonmultiref" = "$canonmultifile" \ ++ || echo "canonmultiref: $canonmultiref" != "$canonmultifile" ++], ++[0], ++[], ++[ignore]) ++AT_CLEANUP ++ ++# ------------------------------ ++# Check if rpmbuild runs dwz and generates a multi file that with shared ++# debuginfo. This is simply the hello example with one binary build twice ++# so dwz has enough slightly similar debug data. ++# Test with unique debug file names. ++AT_SETUP([rpmbuild debuginfo dwz unique debug names]) ++AT_KEYWORDS([build] [debuginfo] [dwz]) ++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 ++ ++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_debug_names 1" \ ++ -ba "${abs_srcdir}"/data/SPECS/hello2.spec ++ ++# The debuginfo package should contain a .debug file for each binary ++# and a dwz multi file that contains the shared debuginfo between them. ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu ++ ++hello_file_debug=./usr/lib/debug/usr/local/bin/hello-*.debug ++hello2_file_debug=./usr/lib/debug/usr/local/bin/hello2-*.debug ++hello_multi_file=./usr/lib/debug/.dwz/hello2-1.0-1.* ++test -f $hello_file_debug || echo "no hello debug file: $hello_file_debug" ++test -f $hello2_file_debug || echo "no hello2 debug file: $hello2_file_debug" ++test -f $hello_multi_file || echo "no dwz multi file: $hello_multi_file" ++ ++# Make sure the main package binaries contain the correct build-ids ++# linking them to the debug packages. ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-1.0-1.*.rpm \ ++ | cpio -diu ++hello_file=./usr/local/bin/hello ++hello2_file=./usr/local/bin/hello2 ++test -f $hello_file || echo "no hello file: $hello_file" ++test -f $hello2_file || echo "no hello2 file: $hello2_file" ++ ++id1=$(file $hello_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++id2=$(file $hello2_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++id1debug=$(file $hello_file_debug | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++id2debug=$(file $hello2_file_debug | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++idmulti=$(file $hello_multi_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++test "$id1" = "$id1debug" || echo "id1: $id1 != id1debug: $id1debug" ++test "$id2" = "$id2debug" || echo "id2: $id2 != id2debug: $id2debug" + + # The build-id files should link to the .debug files. + id1file="./usr/lib/debug/.build-id/${id1:0:2}/${id1:2}" + canonid1file=$(readlink -f ${id1file}) +-canonfile1=$(readlink -f ./usr/local/bin/hello) ++canonfile1=$(readlink -f $hello_file) + canonid1debug=$(readlink -f ${id1file}.debug) +-canondebug1=$(readlink -f ./usr/lib/debug/usr/local/bin/hello.debug) ++canondebug1=$(readlink -f $hello_file_debug) + +-test "$canonid1file" = "$canonfile1" || exit 1 +-test "$canonid1debug" = "$canondebug1" || exit 1 ++test "$canonid1file" = "$canonfile1" \ ++ || echo "canonid1file: $canonid1file != $canonfile1" ++test "$canonid1debug" = "$canondebug1" \ ++ || echo "canonid1debug: $canonid1debug != $canondebug1" + + id2file="./usr/lib/debug/.build-id/${id2:0:2}/${id2:2}" +-canonid2file=$(readlink -f ${id1file}) +-canonfile2=$(readlink -f ./usr/local/bin/hello) +-canonid2debug=$(readlink -f ${id1file}.debug) +-canondebug2=$(readlink -f ./usr/lib/debug/usr/local/bin/hello.debug) ++canonid2file=$(readlink -f ${id2file}) ++canonfile2=$(readlink -f $hello2_file) ++canonid2debug=$(readlink -f ${id2file}.debug) ++canondebug2=$(readlink -f $hello2_file_debug) + +-test "$canonid2file" = "$canonfile2" || exit 1 +-test "$canonid2debug" = "$canondebug2" || exit 1 ++test "$canonid2file" = "$canonfile2" \ ++ || echo "canonid2: $canonid2file != $canonfile2" ++test "$canonid2debug" = "$canondebug2" \ ++ || echo "canonid2debug: $canonid2debug" != "$canondebug2" + + # Both .debug files should point to the dwz multi file. + # It would be nice to also test that they contain the correct dwz build-id + # but that is a bit hard to grep out of the section data. +-multiref1=$(readelf --string-dump=.gnu_debugaltlink ./usr/lib/debug/usr/local/bin/hello.debug | grep '[ 0]' | cut -c13-) +-multiref2=$(readelf --string-dump=.gnu_debugaltlink ./usr/lib/debug/usr/local/bin/hello2.debug | grep '[ 0]' | cut -c13-) ++multiref1=$(readelf --string-dump=.gnu_debugaltlink $hello_file_debug \ ++ | grep '[ 0]' | cut -c13-) ++multiref2=$(readelf --string-dump=.gnu_debugaltlink $hello2_file_debug \ ++ | grep '[ 0]' | cut -c13-) + +-test "$multiref1" = "$multiref2" || exit 1 ++test "$multiref1" = "$multiref2" || echo "multiref: $multiref1 != $multiref2" + + canonmultiref=$(readlink -f $(dirname $canondebug1)/$multiref1) +-canonmultifile=$(readlink -f ./usr/lib/debug/.dwz/hello2-1.0-1.*) ++canonmultifile=$(readlink -f $hello_multi_file) + +-test "$canonmultiref" = "$canonmultifile" || exit 1 ++test "$canonmultiref" = "$canonmultifile" \ ++ || echo "canonmultiref: $canonmultiref" != "$canonmultifile" + ], + [0], + [], +diff --git a/tests/rpmbuildid.at b/tests/rpmbuildid.at +index 1da6302..ede1181 100644 +--- a/tests/rpmbuildid.at ++++ b/tests/rpmbuildid.at +@@ -54,7 +54,8 @@ hello debuginfo build-id files: 0 + AT_CLEANUP + + # ------------------------------ +-# Check if rpmbuild "alldebug" generates debuginfo buildid symlinks ++# Check if rpmbuild "alldebug" generates debuginfo buildid symlinks. ++# Without unique debug file names. + AT_SETUP([rpmbuild buildid alldebug]) + AT_KEYWORDS([build] [debuginfo] [buildid]) + AT_CHECK([ +@@ -69,6 +70,7 @@ run rpmbuild \ + --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="_build_id_links alldebug" \ ++ --undefine "_unique_debug_names" \ + --quiet -ba "${abs_srcdir}"/data/SPECS/hello.spec + + # There should be zero build-id files in the main package +@@ -114,7 +116,100 @@ test "$canon_main_file" = "$canon_main_id_file" \ + + # And check the same for the debug file. + debug_file=./usr/lib/debug/usr/local/bin/hello.debug +-test -f "${debug_file}" || echo "No debug file ${debug_file}" ++test -f ${debug_file} || echo "No debug file ${debug_file}" ++ ++# Extract the build-id from the .debug file ++id_debug=$(file $debug_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++test ${id_main} = ${id_debug} || echo "unequal main and debug id" ++ ++id_debug_file="./usr/lib/debug/.build-id/${id_debug:0:2}/${id_debug:2}.debug" ++test -L "$id_debug_file" || echo "No build-id file $id_debug_file" ++ ++canon_debug_file=$(readlink -f ${debug_file}) ++ ++test -f "$canon_debug_file" \ ++ || echo "Cannot resolve debug file ${debug_file} -> ${canon_debug_file}" ++ ++canon_debug_id_file=$(readlink -f ${id_debug_file}) ++ ++test -f "$canon_debug_id_file" \ ++ || echo "Cannot resolve debug build-id file ${id_debug_file} -> ${canon_debug_id_file}" ++ ++test "$canon_debug_file" = "$canon_debug_id_file" \ ++ || echo "debug and build-id not linked" ++], ++[0], ++[hello build-id files: 0 ++hello debuginfo build-id files: 3 ++], ++[ignore]) ++AT_CLEANUP ++ ++# ------------------------------ ++# Check if rpmbuild "alldebug" generates debuginfo buildid symlinks. ++# With unique debug file names. ++AT_SETUP([rpmbuild buildid alldebug unique debug names]) ++AT_KEYWORDS([build] [debuginfo] [buildid]) ++AT_CHECK([ ++rm -rf ${TOPDIR} ++AS_MKDIR_P(${TOPDIR}/SOURCES) ++ ++# Setup sources ++cp "${abs_srcdir}"/data/SOURCES/hello-1.0.tar.gz "${abs_srcdir}"/data/SOURCES/hello-1.0-modernize.patch ${TOPDIR}/SOURCES ++ ++# Build, contains one ELF which should have a buildid. ++run rpmbuild \ ++ --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="_build_id_links alldebug" \ ++ --define="_unique_debug_names 1" \ ++ --quiet -ba "${abs_srcdir}"/data/SPECS/hello.spec ++ ++# There should be zero build-id files in the main package ++# Main and debug should be in the debuginfo package, ++# plus the .build-id/xx subdir, 3 in total. ++echo -n "hello build-id files: " ++run rpm -ql -p "${TOPDIR}"/RPMS/*/hello-1.0-1.*.rpm \ ++ | grep /.build-id/ | wc --lines ++ ++echo -n "hello debuginfo build-id files: " ++run rpm -ql -p "${TOPDIR}"/RPMS/*/hello-debuginfo-1.0-1.*.rpm \ ++ | grep /.build-id/ | wc --lines ++ ++# Extract the both packages to check the build-id files link to the ++# main and .debug files. ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-1.0-1.*.rpm \ ++ | cpio -diu ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu ++ ++# Check there is a build-id symlink for the main file. ++main_file=./usr/local/bin/hello ++test -f "${main_file}" || echo "No main file ${main_file}" ++ ++# Extract the build-id from the main file ++id_main=$(file $main_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++id_main_file="./usr/lib/debug/.build-id/${id_main:0:2}/${id_main:2}" ++test -L "$id_main_file" || echo "No build-id file $id_main_file" ++ ++canon_main_file=$(readlink -f ${main_file}) ++ ++test -f "$canon_main_file" \ ++ || echo "Cannot resolve main file ${main_file} -> ${canon_main_file}" ++ ++canon_main_id_file=$(readlink -f ${id_main_file}) ++ ++test -f "$canon_main_id_file" \ ++ || echo "Cannot resolve main build-id file ${id_main_file} -> ${canon_main_id_file}" ++ ++test "$canon_main_file" = "$canon_main_id_file" \ ++ || echo "main and build-id file not linked" ++ ++# And check the same for the debug file. ++debug_file=./usr/lib/debug/usr/local/bin/hello-*.debug ++test -f ${debug_file} || echo "No debug file ${debug_file}" + + # Extract the build-id from the .debug file + id_debug=$(file $debug_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') +@@ -146,6 +241,7 @@ AT_CLEANUP + + # ------------------------------ + # Check if rpmbuild "separate" generates main and debuginfo buildid symlinks ++# Without unique debug file names + AT_SETUP([rpmbuild buildid separate]) + AT_KEYWORDS([build] [debuginfo] [buildid]) + AT_CHECK([ +@@ -160,6 +256,7 @@ run rpmbuild \ + --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="_build_id_links separate" \ ++ --undefine "_unique_debug_names" \ + --quiet -ba "${abs_srcdir}"/data/SPECS/hello.spec + + # There should be one build-id files in the main and debuginfo package +@@ -204,7 +301,99 @@ test "$canon_main_file" = "$canon_main_id_file" \ + + # And check the same for the debug file. + debug_file=./usr/lib/debug/usr/local/bin/hello.debug +-test -f "${debug_file}" || echo "No debug file ${debug_file}" ++test -f ${debug_file} || echo "No debug file ${debug_file}" ++ ++# Extract the build-id from the .debug file ++id_debug=$(file $debug_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++test ${id_main} = ${id_debug} || echo "unequal main and debug id" ++ ++id_debug_file="./usr/lib/debug/.build-id/${id_debug:0:2}/${id_debug:2}.debug" ++test -L "$id_debug_file" || echo "No build-id file $id_debug_file" ++ ++canon_debug_file=$(readlink -f ${debug_file}) ++ ++test -f "$canon_debug_file" \ ++ || echo "Cannot resolve debug file ${debug_file} -> ${canon_debug_file}" ++ ++canon_debug_id_file=$(readlink -f ${id_debug_file}) ++ ++test -f "$canon_debug_id_file" \ ++ || echo "Cannot resolve debug build-id file ${id_debug_file} -> ${canon_debug_id_file}" ++ ++test "$canon_debug_file" = "$canon_debug_id_file" \ ++ || echo "debug and build-id not linked" ++], ++[0], ++[hello build-id files: 2 ++hello debuginfo build-id files: 2 ++], ++[ignore]) ++AT_CLEANUP ++ ++# ------------------------------ ++# Check if rpmbuild "separate" generates main and debuginfo buildid symlinks ++# With unique debug file names ++AT_SETUP([rpmbuild buildid separate unique debug names]) ++AT_KEYWORDS([build] [debuginfo] [buildid]) ++AT_CHECK([ ++rm -rf ${TOPDIR} ++AS_MKDIR_P(${TOPDIR}/SOURCES) ++ ++# Setup sources ++cp "${abs_srcdir}"/data/SOURCES/hello-1.0.tar.gz "${abs_srcdir}"/data/SOURCES/hello-1.0-modernize.patch ${TOPDIR}/SOURCES ++ ++# Build, contains one ELF which should have a buildid. ++run rpmbuild \ ++ --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="_build_id_links separate" \ ++ --define="_unique_debug_names 1" \ ++ --quiet -ba "${abs_srcdir}"/data/SPECS/hello.spec ++ ++# There should be one build-id files in the main and debuginfo package ++# plus the .build-id/xx subdir, 2 in total. ++echo -n "hello build-id files: " ++run rpm -ql -p "${TOPDIR}"/RPMS/*/hello-1.0-1.*.rpm \ ++ | grep /.build-id/ | wc --lines ++ ++echo -n "hello debuginfo build-id files: " ++run rpm -ql -p "${TOPDIR}"/RPMS/*/hello-debuginfo-1.0-1.*.rpm \ ++ | grep /.build-id/ | wc --lines ++ ++# Extract the both packages to check the build-id files link to the ++# main and .debug files. ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-1.0-1.*.rpm \ ++ | cpio -diu ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu ++ ++# Check there is a build-id symlink for the main file. ++main_file=./usr/local/bin/hello ++test -f "${main_file}" || echo "No main file ${main_file}" ++ ++# Extract the build-id from the main file ++id_main=$(file $main_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++id_main_file="./usr/lib/.build-id/${id_main:0:2}/${id_main:2}" ++test -L "$id_main_file" || echo "No build-id file $id_main_file" ++ ++canon_main_file=$(readlink -f ${main_file}) ++ ++test -f "$canon_main_file" \ ++ || echo "Cannot resolve main file ${main_file} -> ${canon_main_file}" ++ ++canon_main_id_file=$(readlink -f ${id_main_file}) ++ ++test -f "$canon_main_id_file" \ ++ || echo "Cannot resolve main build-id file ${id_main_file} -> ${canon_main_id_file}" ++ ++test "$canon_main_file" = "$canon_main_id_file" \ ++ || echo "main and build-id file not linked" ++ ++# And check the same for the debug file. ++debug_file=./usr/lib/debug/usr/local/bin/hello-*.debug ++test -f ${debug_file} || echo "No debug file ${debug_file}" + + # Extract the build-id from the .debug file + id_debug=$(file $debug_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') +@@ -236,6 +425,7 @@ AT_CLEANUP + + # ------------------------------ + # Check if rpmbuild "compat" generates main and debuginfo buildid symlinks ++# Without unique debug file names + AT_SETUP([rpmbuild buildid compat]) + AT_KEYWORDS([build] [debuginfo] [buildid]) + AT_CHECK([ +@@ -250,6 +440,7 @@ run rpmbuild \ + --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="_build_id_links compat" \ ++ --undefine "_unique_debug_names" \ + --quiet -ba "${abs_srcdir}"/data/SPECS/hello.spec + + # There should be one build-id files in the main and debuginfo package. +@@ -295,7 +486,112 @@ test "$canon_main_file" = "$canon_main_id_file" \ + + # And check the same for the debug file. + debug_file=./usr/lib/debug/usr/local/bin/hello.debug +-test -f "${debug_file}" || echo "No debug file ${debug_file}" ++test -f ${debug_file} || echo "No debug file ${debug_file}" ++ ++# Extract the build-id from the .debug file ++id_debug=$(file $debug_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++test ${id_main} = ${id_debug} || echo "unequal main and debug id" ++ ++id_debug_file="./usr/lib/debug/.build-id/${id_debug:0:2}/${id_debug:2}.debug" ++test -L "$id_debug_file" || echo "No build-id file $id_debug_file" ++ ++canon_debug_file=$(readlink -f ${debug_file}) ++ ++test -f "$canon_debug_file" \ ++ || echo "Cannot resolve debug file ${debug_file} -> ${canon_debug_file}" ++ ++canon_debug_id_file=$(readlink -f ${id_debug_file}) ++ ++test -f "$canon_debug_id_file" \ ++ || echo "Cannot resolve debug build-id file ${id_debug_file} -> ${canon_debug_id_file}" ++ ++test "$canon_debug_file" = "$canon_debug_id_file" \ ++ || echo "debug and build-id not linked" ++ ++# The compat link should also point to the same (indirectly). ++id_compat_file="./usr/lib/debug/.build-id/${id_main:0:2}/${id_main:2}" ++test -L "$id_compat_file" || echo "No build-id compat file $id_compat_file" ++ ++canon_compat_file=$(readlink -f ${id_compat_file}) ++ ++test -f "$canon_compat_file" \ ++ || echo "Cannot resolve compat file ${id_compat_file} -> ${canon_compat_file}" ++ ++test "$canon_compat_file" = "$canon_main_file" \ ++ || echo "compat and build-id not linked" ++], ++[0], ++[hello build-id files: 2 ++hello debuginfo build-id files: 3 ++], ++[ignore]) ++AT_CLEANUP ++ ++# ------------------------------ ++# Check if rpmbuild "compat" generates main and debuginfo buildid symlinks ++# With unique debug file names ++AT_SETUP([rpmbuild buildid compat unique debug names]) ++AT_KEYWORDS([build] [debuginfo] [buildid]) ++AT_CHECK([ ++rm -rf ${TOPDIR} ++AS_MKDIR_P(${TOPDIR}/SOURCES) ++ ++# Setup sources ++cp "${abs_srcdir}"/data/SOURCES/hello-1.0.tar.gz "${abs_srcdir}"/data/SOURCES/hello-1.0-modernize.patch ${TOPDIR}/SOURCES ++ ++# Build, contains one ELF which should have a buildid. ++run rpmbuild \ ++ --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="_build_id_links compat" \ ++ --define="_unique_debug_names 1" \ ++ --quiet -ba "${abs_srcdir}"/data/SPECS/hello.spec ++ ++# There should be one build-id files in the main and debuginfo package. ++# the debuginfo package has one extra main build-id compat symlink ++# plus the .build-id/xx subdir, 2 in total in main, 3 in total in debug ++echo -n "hello build-id files: " ++run rpm -ql -p "${TOPDIR}"/RPMS/*/hello-1.0-1.*.rpm \ ++ | grep /.build-id/ | wc --lines ++ ++echo -n "hello debuginfo build-id files: " ++run rpm -ql -p "${TOPDIR}"/RPMS/*/hello-debuginfo-1.0-1.*.rpm \ ++ | grep /.build-id/ | wc --lines ++ ++# Extract the both packages to check the build-id files link to the ++# main and .debug files. ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-1.0-1.*.rpm \ ++ | cpio -diu ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu ++ ++# Check there is a build-id symlink for the main file. ++main_file=./usr/local/bin/hello ++test -f "${main_file}" || echo "No main file ${main_file}" ++ ++# Extract the build-id from the main file ++id_main=$(file $main_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') ++ ++id_main_file="./usr/lib/.build-id/${id_main:0:2}/${id_main:2}" ++test -L "$id_main_file" || echo "No build-id file $id_main_file" ++ ++canon_main_file=$(readlink -f ${main_file}) ++ ++test -f "$canon_main_file" \ ++ || echo "Cannot resolve main file ${main_file} -> ${canon_main_file}" ++ ++canon_main_id_file=$(readlink -f ${id_main_file}) ++ ++test -f "$canon_main_id_file" \ ++ || echo "Cannot resolve main build-id file ${id_main_file} -> ${canon_main_id_file}" ++ ++test "$canon_main_file" = "$canon_main_id_file" \ ++ || echo "main and build-id file not linked" ++ ++# And check the same for the debug file. ++debug_file=./usr/lib/debug/usr/local/bin/hello-*debug ++test -f ${debug_file} || echo "No debug file ${debug_file}" + + # Extract the build-id from the .debug file + id_debug=$(file $debug_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') +@@ -784,8 +1080,12 @@ rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-1.0-1.*.rpm \ + hello_file=./usr/local/bin/hello + + # Extract the build-id from the main file ++test -f $hello_file || echo "No $hello_file" + id1=$(file $hello_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') + ++# Make sure we generate a new one ++rm $hello_file ++ + # 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 \ +@@ -797,6 +1097,7 @@ rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-1.0-2.*.rpm \ + | cpio -diu --quiet + + # Extract the build-id from the main file ++test -f $hello_file || echo "No $hello_file" + id2=$(file $hello_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') + + if test "$id1" == "$id2"; then echo "equal $id1"; else echo "unequal"; fi +@@ -823,6 +1124,7 @@ 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" \ ++ --undefine="_unique_debug_names" \ + -ba "${abs_srcdir}"/data/SPECS/hello.spec + + rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-1.0-1.*.rpm \ +@@ -831,19 +1133,25 @@ rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-1.0-1.*.rpm \ + hello_file=./usr/local/bin/hello + + # Extract the build-id from the main file ++test -f $hello_file || echo "No $hello_file" + id1=$(file $hello_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') + ++# Make sure we generate a new one ++rm $hello_file ++ + # 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" \ ++ --undefine="_unique_debug_names" \ + -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 ++test -f $hello_file || echo "No $hello_file" + id2=$(file $hello_file | sed 's/.*, BuildID[.*]=\(.*\),.*/\1/') + + if test "$id1" == "$id2"; then echo "equal"; else echo "unequal $id1 $id2"; fi +-- +2.9.3 + diff --git a/0005-Fix-behavior-when-_build_id_links-is-undefined.patch b/0005-Fix-behavior-when-_build_id_links-is-undefined.patch new file mode 100644 index 0000000..2eeee49 --- /dev/null +++ b/0005-Fix-behavior-when-_build_id_links-is-undefined.patch @@ -0,0 +1,32 @@ +From 1a8a0364fd049f3b4432633160fba12aa137f88d Mon Sep 17 00:00:00 2001 +Message-Id: <1a8a0364fd049f3b4432633160fba12aa137f88d.1488964568.git.pmatilai@redhat.com> +In-Reply-To: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +References: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +From: Panu Matilainen +Date: Mon, 3 Oct 2016 12:36:46 +0300 +Subject: [PATCH 05/11] Fix behavior when %_build_id_links is undefined + +Commit bbfe1f86b2e4b5c0bd499d9f3dd9de9c9c20fff2 tries to behave sanely +and use compat setting when %_build_id_links is undefined, but +rpmExpand() never returns NULL so the original check is incorrect. +Check for empty string instead. +--- + build/files.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/build/files.c b/build/files.c +index eb39856..6215bf8 100644 +--- a/build/files.c ++++ b/build/files.c +@@ -1648,7 +1648,7 @@ static int generateBuildIDs(FileList fl) + /* How are we supposed to create the build-id links? */ + char *build_id_links_macro = rpmExpand("%{?_build_id_links}", NULL); + int build_id_links; +- if (build_id_links_macro == NULL) { ++ if (*build_id_links_macro == '\0') { + rpmlog(RPMLOG_WARNING, + _("_build_id_links macro not set, assuming 'compat'\n")); + build_id_links = BUILD_IDS_COMPAT; +-- +2.9.3 + diff --git a/0006-Fix-debuginfo-etc-when-subpackages-have-different-ve.patch b/0006-Fix-debuginfo-etc-when-subpackages-have-different-ve.patch new file mode 100644 index 0000000..c14ede5 --- /dev/null +++ b/0006-Fix-debuginfo-etc-when-subpackages-have-different-ve.patch @@ -0,0 +1,139 @@ +From 53c9e8d00983b0d99caefc0ef94a18184c0ba85c Mon Sep 17 00:00:00 2001 +Message-Id: <53c9e8d00983b0d99caefc0ef94a18184c0ba85c.1488964568.git.pmatilai@redhat.com> +In-Reply-To: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +References: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +From: Panu Matilainen +Date: Wed, 9 Nov 2016 09:16:48 +0200 +Subject: [PATCH 06/11] Fix debuginfo etc when subpackages have different + versions (RhBug:1051407) + +Rpm has always been a bit dazed and confused when it comes to specs +with sub-packages having different version etc from the main package. +Many things work fine in that case .. except .. when they dont. Debuginfo +picking up wrong versions (RhBug:1051407) is just one example, there +are countless more in bugzilla wrt buildroot paths and whatnot. +The simple and sane solution would be not piling on them macros +from sub-packages, but that would surely break somebodys precious +spec tricks. + +The ugly but brutally simple and compatible solution to this all is to +create separate set of macros when on the main package, this lets users +in and out of rpm pick which one (latest or main) they want. To hopefully +avoid stomping on anybodys toes, use uppercasing for the macro name (other +variants like %pkg_release are awfully commonly used). Pile 'em on, yay! +--- + build/files.c | 2 +- + build/parsePreamble.c | 11 ++++++++++- + macros.in | 18 +++++++++--------- + 3 files changed, 20 insertions(+), 11 deletions(-) + +diff --git a/build/files.c b/build/files.c +index 6215bf8..2ede463 100644 +--- a/build/files.c ++++ b/build/files.c +@@ -1797,7 +1797,7 @@ static int generateBuildIDs(FileList fl) + int unique_debug_names = + rpmExpandNumeric("%{?_unique_debug_names}"); + if (unique_debug_names == 1) +- vra = rpmExpand("-%{version}-%{release}.%{_arch}", NULL); ++ vra = rpmExpand("-%{VERSION}-%{RELEASE}.%{_arch}", NULL); + } + + /* Now add a subdir and symlink for each buildid found. */ +diff --git a/build/parsePreamble.c b/build/parsePreamble.c +index 933f734..6be4403 100644 +--- a/build/parsePreamble.c ++++ b/build/parsePreamble.c +@@ -909,8 +909,17 @@ static rpmRC handlePreambleTag(rpmSpec spec, Package pkg, rpmTagVal tag, + goto exit; + } + +- if (macro) ++ if (macro) { + addMacro(spec->macros, macro, NULL, field, RMIL_SPEC); ++ /* Add a separate uppercase macro for tags from the main package */ ++ if (pkg == spec->packages) { ++ char *m = xstrdup(macro); ++ for (char *p = m; *p; ++p) ++ *p = rtoupper(*p); ++ addMacro(spec->macros, m, NULL, field, RMIL_SPEC); ++ free(m); ++ } ++ } + rc = RPMRC_OK; + exit: + return rc; +diff --git a/macros.in b/macros.in +index 68bf391..4d90282 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} %{?_include_gdb_index:-i} %{?_unique_build_ids:--ver-rel "%{version}-%{release}"} %{?_unique_debug_names:--unique-debug-arch "%{_arch}"} %{?_find_debuginfo_dwz_opts} %{?_find_debuginfo_opts} "%{_builddir}/%{?buildsubdir}"\ ++ %{_rpmconfigdir}/find-debuginfo.sh %{?_missing_build_ids_terminate_build:--strict-build-id} %{?_include_minidebuginfo:-m} %{?_include_gdb_index:-i} %{?_unique_build_ids:--ver-rel "%{VERSION}-%{RELEASE}"} %{?_unique_debug_names:--unique-debug-arch "%{_arch}"} %{?_find_debuginfo_dwz_opts} %{?_find_debuginfo_opts} "%{_builddir}/%{?buildsubdir}"\ + %{nil} + + # Template for debug information sub-package. +@@ -232,7 +232,7 @@ package or when debugging this package.\ + %_buildrootdir %{_topdir}/BUILDROOT + + # Build root path, where %install installs the package during build. +-%buildroot %{_buildrootdir}/%{name}-%{version}-%{release}.%{_arch} ++%buildroot %{_buildrootdir}/%{NAME}-%{VERSION}-%{RELEASE}.%{_arch} + + # Directory where temporaray files can be created. + %_tmppath %{_var}/tmp +@@ -484,7 +484,7 @@ package or when debugging this package.\ + + # 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 ++# --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 + +@@ -705,9 +705,9 @@ package or when debugging this package.\ + export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_ARCH RPM_OS\ + RPM_DOC_DIR=\"%{_docdir}\"\ + export RPM_DOC_DIR\ +- RPM_PACKAGE_NAME=\"%{name}\"\ +- RPM_PACKAGE_VERSION=\"%{version}\"\ +- RPM_PACKAGE_RELEASE=\"%{release}\"\ ++ RPM_PACKAGE_NAME=\"%{NAME}\"\ ++ RPM_PACKAGE_VERSION=\"%{VERSION}\"\ ++ RPM_PACKAGE_RELEASE=\"%{RELEASE}\"\ + export RPM_PACKAGE_NAME RPM_PACKAGE_VERSION RPM_PACKAGE_RELEASE\ + LANG=C\ + export LANG\ +@@ -1146,7 +1146,7 @@ done \ + %__scm_setup_hg(q)\ + %{__hg} init %{-q} .\ + %{__hg} add %{-q} .\ +-%{__hg} commit %{-q} --user "%{__scm_author}" -m "%{name}-%{version} base" ++%{__hg} commit %{-q} --user "%{__scm_author}" -m "%{NAME}-%{VERSION} base" + + %__scm_apply_hg(qp:m:)\ + %{__hg} import - %{-p:-p%{-p*}} %{-q} -m %{-m*} --user "%{__scm_author}" +@@ -1158,7 +1158,7 @@ done \ + %{__git} config user.email "%{__scm_usermail}"\ + %{__git} add .\ + %{__git} commit %{-q} -a\\\ +- --author "%{__scm_author}" -m "%{name}-%{version} base" ++ --author "%{__scm_author}" -m "%{NAME}-%{VERSION} base" + + %__scm_apply_git(qp:m:)\ + %{__git} apply --index %{-p:-p%{-p*}} -\ +@@ -1181,7 +1181,7 @@ done \ + %{__bzr} init %{-q}\ + %{__bzr} whoami --branch "%{__scm_author}"\ + %{__bzr} add .\ +-%{__bzr} commit %{-q} -m "%{name}-%{version} base" ++%{__bzr} commit %{-q} -m "%{NAME}-%{VERSION} base" + + # bzr doesn't seem to have its own command to apply patches? + %__scm_apply_bzr(qp:m:)\ +-- +2.9.3 + diff --git a/0007-Only-process-regular-files-when-generating-build-ids.patch b/0007-Only-process-regular-files-when-generating-build-ids.patch new file mode 100644 index 0000000..7d5b624 --- /dev/null +++ b/0007-Only-process-regular-files-when-generating-build-ids.patch @@ -0,0 +1,53 @@ +From eb21562bcec67746e756679a60995f68d7d45577 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +References: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +From: Panu Matilainen +Date: Thu, 5 Jan 2017 12:13:54 +0200 +Subject: [PATCH 07/11] Only process regular files when generating build-ids + +Versioned shared libraries typically have several symlinks pointing +to them directly and indirectly. When %_build_id_links is set to compat or +separate this causes bogus warnings about duplicate build-ids. + +It looks commit bbfe1f86b2e4b5c0bd499d9f3dd9de9c9c20fff2 intends to skip +symlinks since it filters on S_ISREG(), but since uses fstat() +on already open()'ed file it ends up stat()'ing the symlink target. +Flip stat() + open() around and use lstat() instead to fix it. +--- + build/files.c | 11 +++++------ + 1 file changed, 5 insertions(+), 6 deletions(-) + +diff --git a/build/files.c b/build/files.c +index 2ede463..ca04176 100644 +--- a/build/files.c ++++ b/build/files.c +@@ -1678,11 +1678,10 @@ static int generateBuildIDs(FileList fl) + int needMain = 0; + int needDbg = 0; + for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) { +- int fd; +- fd = open (flp->diskPath, O_RDONLY); +- if (fd >= 0) { +- struct stat sbuf; +- if (fstat (fd, &sbuf) == 0 && S_ISREG (sbuf.st_mode)) { ++ struct stat sbuf; ++ if (lstat(flp->diskPath, &sbuf) == 0 && S_ISREG (sbuf.st_mode)) { ++ int fd = open (flp->diskPath, O_RDONLY); ++ if (fd >= 0) { + Elf *elf = elf_begin (fd, ELF_C_READ, NULL); + if (elf != NULL && elf_kind(elf) == ELF_K_ELF) { + const void *build_id; +@@ -1748,8 +1747,8 @@ static int generateBuildIDs(FileList fl) + } + elf_end (elf); + } ++ close (fd); + } +- close (fd); + } + } + +-- +2.9.3 + diff --git a/0008-configure.ac-use-LIBDW-always-conditionally.patch b/0008-configure.ac-use-LIBDW-always-conditionally.patch new file mode 100644 index 0000000..1c80ca9 --- /dev/null +++ b/0008-configure.ac-use-LIBDW-always-conditionally.patch @@ -0,0 +1,44 @@ +From 1e38abe3e6697efcf55663060533e286e1e77ae4 Mon Sep 17 00:00:00 2001 +Message-Id: <1e38abe3e6697efcf55663060533e286e1e77ae4.1488964568.git.pmatilai@redhat.com> +In-Reply-To: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +References: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +From: Igor Gnatenko +Date: Wed, 10 Aug 2016 13:58:30 +0200 +Subject: [PATCH 08/11] configure.ac: use LIBDW always conditionally + +References: https://bugzilla.redhat.com/show_bug.cgi?id=1365278 +Reported-and-tested-by: Neal Gompa +Signed-off-by: Igor Gnatenko +--- + configure.ac | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 08eceeb..5dd2bb6 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -368,7 +368,8 @@ AM_CONDITIONAL(WITH_ARCHIVE,[test "$with_archive" = yes]) + + #================= + # Check for elfutils libdw library with dwelf_elf_gnu_build_id. +-AS_IF([test "$WITH_LIBELF" = yes],[ ++WITH_LIBDW_LIB= ++AS_IF([test "$WITH_LIBELF" != yes],[ + AC_CHECK_HEADERS([elfutils/libdwelf.h],[ + AC_CHECK_LIB(dw, dwelf_elf_gnu_build_id, [ + AC_DEFINE(HAVE_LIBDW, 1, +@@ -377,9 +378,9 @@ AS_IF([test "$WITH_LIBELF" = yes],[ + WITH_LIBDW=yes + ]) + ]) +- AC_SUBST(WITH_LIBDW_LIB) +- AM_CONDITIONAL(LIBDW,[test "$WITH_LIBDW" = yes]) + ]) ++AC_SUBST(WITH_LIBDW_LIB) ++AM_CONDITIONAL(LIBDW,[test "$WITH_LIBDW" = yes]) + + #================= + # Process --with/without-external-db +-- +2.9.3 + diff --git a/0009-Fix-libdw-configure-check.patch b/0009-Fix-libdw-configure-check.patch new file mode 100644 index 0000000..8228921 --- /dev/null +++ b/0009-Fix-libdw-configure-check.patch @@ -0,0 +1,33 @@ +From 15617bfa3d12ebe3927b130a88df6dd12ee8d0f9 Mon Sep 17 00:00:00 2001 +Message-Id: <15617bfa3d12ebe3927b130a88df6dd12ee8d0f9.1488964568.git.pmatilai@redhat.com> +In-Reply-To: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +References: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +From: Mark Wielaard +Date: Wed, 24 Aug 2016 17:06:34 +0200 +Subject: [PATCH 09/11] Fix libdw configure check. + +commit a82119 "configure.ac: use LIBDW always conditionally" contained +a typo that caused WITH_LIBDW_LIB never to be set when you were using +libelf. Fixed by reverting the "!=" to "=" again. + +Signed-off-by: Mark Wielaard +--- + configure.ac | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/configure.ac b/configure.ac +index 5dd2bb6..4baa3f1 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -369,7 +369,7 @@ AM_CONDITIONAL(WITH_ARCHIVE,[test "$with_archive" = yes]) + #================= + # Check for elfutils libdw library with dwelf_elf_gnu_build_id. + WITH_LIBDW_LIB= +-AS_IF([test "$WITH_LIBELF" != yes],[ ++AS_IF([test "$WITH_LIBELF" = yes],[ + AC_CHECK_HEADERS([elfutils/libdwelf.h],[ + AC_CHECK_LIB(dw, dwelf_elf_gnu_build_id, [ + AC_DEFINE(HAVE_LIBDW, 1, +-- +2.9.3 + diff --git a/0010-debugedit-Support-String-Line-table-rewriting-for-la.patch b/0010-debugedit-Support-String-Line-table-rewriting-for-la.patch new file mode 100644 index 0000000..566af7a --- /dev/null +++ b/0010-debugedit-Support-String-Line-table-rewriting-for-la.patch @@ -0,0 +1,2105 @@ +From 481a17c207809d10efcc87c1f831c5eef83c069f Mon Sep 17 00:00:00 2001 +Message-Id: <481a17c207809d10efcc87c1f831c5eef83c069f.1488964568.git.pmatilai@redhat.com> +In-Reply-To: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +References: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +From: Mark Wielaard +Date: Fri, 17 Feb 2017 14:13:57 +0100 +Subject: [PATCH 10/11] debugedit: Support String/Line table rewriting for + larger/smaller paths. + +debugedit --base to --dest rewriting of debug source file paths only +supported dest paths that were smaller or equal than the base path +(and the size should differ more than 1 character for correct debug lines). +All paths were changed "in place". Which could in theory mess up debug str +sharing. + +This rewrite supports base and dest strings of any size (some limitations, +see below). This is done by reconstructing the debug_str and debug_line +tables and updating the references in the debug_info attributes pointing +to these tables. Plus, if necessary (only for ET_REL kernel modules), +updating any relocations for the debug_info and debug_line sections. + +This has the nice benefit of merging any duplicate strings in the +debug_str table which might resulting on slightly smaller files. +kernel modules are ET_REL files that often contain a lot of duplicate +strings. + +The rewrite uses elfutils (either libebl or libdw) to reconstruct the +debug_str table. Since we are changing some section sizes now we cannot +just use mmap and rawdata to poke the values, but need to read in and +write out the changed sections. This does take a bit more memory because +we now also need to keep track of all string/line references. + +There are still some limitations (already in the original debugedit) +not fixed by this rewrite: +- DW_AT_comp_dir in .debug_info using DW_FORM_string can not be made + larger. We only warn about that now instead of failing. The only + producer of DW_FORM_string comp_dirs is binutils gas. It seems simpler + to fix gas than to try to support resizing the debug_info section. +- A DW_AT_name on a DW_TAG_compile_unit is only rewritten for DW_FORM_strp + not for DW_FORM_string. Probably no problem in practice since this + wasn't supported originally either. +- The debug_line program isn't scanned for DW_LNE_define_file which + could in theory define an absolute path that might need rewriting. + Again probably not a problem because this wasn't supported before + and there are no know producers for this construct. + +To support the upcoming DWARFv5 in gcc 7 (not on by default), we will +need to add support for the new debug_line format and scan the new +debug_macro section that can have references to the debug_str table. + +Signed-off-by: Mark Wielaard +--- + Makefile.am | 8 +- + configure.ac | 6 + + tools/debugedit.c | 1569 ++++++++++++++++++++++++++++++++++++++++++++--------- + 3 files changed, 1330 insertions(+), 253 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index 6b37b58..1b77730 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -156,13 +156,18 @@ rpm2archive_LDADD += @WITH_NSS_LIB@ @WITH_POPT_LIB@ @WITH_ZLIB_LIB@ @WITH_ARCHIV + + if LIBELF + if LIBDWARF ++if LIBDW + rpmconfig_SCRIPTS += scripts/find-debuginfo.sh + + rpmlibexec_PROGRAMS += debugedit + debugedit_SOURCES = tools/debugedit.c tools/hashtab.c tools/hashtab.h + debugedit_LDADD = rpmio/librpmio.la + debugedit_LDADD += @WITH_LIBELF_LIB@ @WITH_POPT_LIB@ +- ++if HAVE_LIBDW_STRTAB ++debugedit_LDADD += @WITH_LIBDW_LIB@ ++else ++debugedit_LDADD += @WITH_LIBDW_LIB@ -lebl ++endif + rpmlibexec_PROGRAMS += elfdeps + elfdeps_SOURCES = tools/elfdeps.c + elfdeps_LDADD = rpmio/librpmio.la +@@ -173,6 +178,7 @@ sepdebugcrcfix_SOURCES = tools/sepdebugcrcfix.c + sepdebugcrcfix_LDADD = @WITH_LIBELF_LIB@ + endif + endif ++endif + + rpmlibexec_PROGRAMS += rpmdeps + rpmdeps_SOURCES = tools/rpmdeps.c +diff --git a/configure.ac b/configure.ac +index 4baa3f1..743fe08 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -369,18 +369,24 @@ AM_CONDITIONAL(WITH_ARCHIVE,[test "$with_archive" = yes]) + #================= + # Check for elfutils libdw library with dwelf_elf_gnu_build_id. + WITH_LIBDW_LIB= ++HAVE_LIBDW_STRTAB= + AS_IF([test "$WITH_LIBELF" = yes],[ + AC_CHECK_HEADERS([elfutils/libdwelf.h],[ ++ # dwelf_elf_gnu_build_id was introduced in elfutils 0.159 + AC_CHECK_LIB(dw, dwelf_elf_gnu_build_id, [ + AC_DEFINE(HAVE_LIBDW, 1, + [Define to 1 if you have elfutils libdw library]) + WITH_LIBDW_LIB="-ldw" + WITH_LIBDW=yes ++ # If possible we also want the strtab functions from elfutils 0.167. ++ # But we can fall back on the (unsupported) ebl alternatives if not. ++ AC_CHECK_LIB(dw, dwelf_strtab_init, [HAVE_LIBDW_STRTAB=yes]) + ]) + ]) + ]) + AC_SUBST(WITH_LIBDW_LIB) + AM_CONDITIONAL(LIBDW,[test "$WITH_LIBDW" = yes]) ++AM_CONDITIONAL(HAVE_LIBDW_STRTAB,[test "$HAVE_LIBDW_STRTAB" = yes]) + + #================= + # Process --with/without-external-db +diff --git a/tools/debugedit.c b/tools/debugedit.c +index c0147f0..4798c63 100644 +--- a/tools/debugedit.c ++++ b/tools/debugedit.c +@@ -1,6 +1,7 @@ +-/* Copyright (C) 2001-2003, 2005, 2007, 2009-2011, 2016 Red Hat, Inc. ++/* Copyright (C) 2001-2003, 2005, 2007, 2009-2011, 2016, 2017 Red Hat, Inc. + Written by Alexander Larsson , 2002 + Based on code by Jakub Jelinek , 2001. ++ String/Line table rewriting by Mark Wielaard , 2017. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by +@@ -30,6 +31,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -39,6 +41,35 @@ + #include + #include + ++/* Unfortunately strtab manipulation functions were only officially added ++ to elfutils libdw in 0.167. Before that there were internal unsupported ++ ebl variants. While libebl.h isn't supported we'll try to use it anyway ++ if the elfutils we build against is too old. */ ++#include ++#if _ELFUTILS_PREREQ (0, 167) ++#include ++typedef Dwelf_Strent Strent; ++typedef Dwelf_Strtab Strtab; ++#define strtab_init dwelf_strtab_init ++#define strtab_add(X,Y) dwelf_strtab_add(X,Y) ++#define strtab_add_len(X,Y,Z) dwelf_strtab_add_len(X,Y,Z) ++#define strtab_free dwelf_strtab_free ++#define strtab_finalize dwelf_strtab_finalize ++#define strent_offset dwelf_strent_off ++#else ++#include ++typedef struct Ebl_Strent Strent; ++typedef struct Ebl_Strtab Strtab; ++#define strtab_init ebl_strtabinit ++#define strtab_add(X,Y) ebl_strtabadd(X,Y,0) ++#define strtab_add_len(X,Y,Z) ebl_strtabadd(X,Y,Z) ++#define strtab_free ebl_strtabfree ++#define strtab_finalize ebl_strtabfinalize ++#define strent_offset ebl_strtaboffset ++#endif ++ ++#include ++ + #include + #include + #include "tools/hashtab.h" +@@ -56,6 +87,99 @@ int list_file_fd = -1; + int do_build_id = 0; + char *build_id_seed = NULL; + ++/* We go over the debug sections in two phases. In phase zero we keep ++ track of any needed changes and collect strings, indexes and ++ sizes. In phase one we do the actual replacements updating the ++ strings, indexes and writing out new debug sections. The following ++ keep track of various changes that might be needed. */ ++ ++/* Whether we need to do any literal string (DW_FORM_string) replacements ++ in debug_info. */ ++static bool need_string_replacement = false; ++/* Whether we need to do any updates of the string indexes (DW_FORM_strp) ++ in debug_info for string indexes. */ ++static bool need_strp_update = false; ++/* If the debug_line changes size we will need to update the ++ DW_AT_stmt_list attributes indexes in the debug_info. */ ++static bool need_stmt_update = false; ++ ++/* Storage for dynamically allocated strings to put into string ++ table. Keep together in memory blocks of 16K. */ ++#define STRMEMSIZE (16 * 1024) ++struct strmemblock ++{ ++ struct strmemblock *next; ++ char memory[0]; ++}; ++ ++/* We keep track of each index in the original string table and the ++ associated entry in the new table so we don't insert identical ++ strings into the new string table. If constructed correctly the ++ original strtab shouldn't contain duplicate strings anyway. Any ++ actual identical strings could be deduplicated, but searching for ++ and comparing the indexes is much faster than comparing strings ++ (and we don't have to construct replacement strings). */ ++struct stridxentry ++{ ++ uint32_t idx; /* Original index in the string table. */ ++ Strent *entry; /* Entry in the new table. */ ++}; ++ ++/* Storage for new string table entries. Keep together in memory to ++ quickly search through them with tsearch. */ ++#define STRIDXENTRIES ((16 * 1024) / sizeof (struct stridxentry)) ++struct strentblock ++{ ++ struct strentblock *next; ++ struct stridxentry entry[0]; ++}; ++ ++/* All data to keep track of the existing and new string table. */ ++struct strings ++{ ++ Strtab *str_tab; /* The new string table. */ ++ char *str_buf; /* New Elf_Data d_buf. */ ++ struct strmemblock *blocks; /* The first strmemblock. */ ++ struct strmemblock *last_block; /* The currently used strmemblock. */ ++ size_t stridx; /* Next free byte in last block. */ ++ struct strentblock *entries; /* The first string index block. */ ++ struct strentblock *last_entries; /* The currently used strentblock. */ ++ size_t entryidx; /* Next free entry in the last block. */ ++ void *strent_root; /* strent binary search tree root. */ ++}; ++ ++struct line_table ++{ ++ size_t old_idx; /* Original offset. */ ++ size_t new_idx; /* Offset in new debug_line section. */ ++ ssize_t size_diff; /* Difference in (header) size. */ ++ bool replace_dirs; /* Whether to replace any dir paths. */ ++ bool replace_files; /* Whether to replace any file paths. */ ++ ++ /* Header fields. */ ++ uint32_t unit_length; ++ uint16_t version; ++ uint32_t header_length; ++ uint8_t min_instr_len; ++ uint8_t max_op_per_instr; /* Only if version >= 4 */ ++ uint8_t default_is_stmt; ++ int8_t line_base; ++ uint8_t line_range; ++ uint8_t opcode_base; ++}; ++ ++struct debug_lines ++{ ++ struct line_table *table; /* Malloc/Realloced. */ ++ size_t size; /* Total number of line_tables. ++ Updated by get_line_table. */ ++ size_t used; /* Used number of line_tables. ++ Updated by get_line_table. */ ++ size_t debug_lines_len; /* Total size of new debug_line section. ++ updated by edit_dwarf2_line. */ ++ char *line_buf; /* New Elf_Data d_buf. */ ++}; ++ + typedef struct + { + Elf *elf; +@@ -63,15 +187,42 @@ typedef struct + Elf_Scn **scn; + const char *filename; + int lastscn; ++ size_t phnum; ++ struct strings strings; ++ struct debug_lines lines; + GElf_Shdr shdr[0]; + } DSO; + ++static void ++setup_lines (struct debug_lines *lines) ++{ ++ lines->table = NULL; ++ lines->size = 0; ++ lines->used = 0; ++ lines->debug_lines_len = 0; ++ lines->line_buf = NULL; ++} ++ ++static void ++destroy_lines (struct debug_lines *lines) ++{ ++ free (lines->table); ++ free (lines->line_buf); ++} ++ + typedef struct + { + unsigned char *ptr; + uint32_t addend; ++ int ndx; + } REL; + ++typedef struct ++{ ++ Elf64_Addr r_offset; ++ int ndx; ++} LINE_REL; ++ + #define read_uleb128(ptr) ({ \ + unsigned int ret = 0; \ + unsigned int c; \ +@@ -88,9 +239,23 @@ typedef struct + ret; \ + }) + ++#define write_uleb128(ptr,val) ({ \ ++ uint32_t valv = (val); \ ++ do \ ++ { \ ++ unsigned char c = valv & 0x7f; \ ++ valv >>= 7; \ ++ if (valv) \ ++ c |= 0x80; \ ++ *ptr++ = c; \ ++ } \ ++ while (valv); \ ++}) ++ + static uint16_t (*do_read_16) (unsigned char *ptr); + static uint32_t (*do_read_32) (unsigned char *ptr); +-static void (*write_32) (unsigned char *ptr, GElf_Addr val); ++static void (*do_write_16) (unsigned char *ptr, uint16_t val); ++static void (*do_write_32) (unsigned char *ptr, uint32_t val); + + static int ptr_size; + static int cu_version; +@@ -129,7 +294,7 @@ strptr (DSO *dso, int sec, off_t offset) + if (offset >= 0 && (GElf_Addr) offset < dso->shdr[sec].sh_size) + { + data = NULL; +- while ((data = elf_rawdata (scn, data)) != NULL) ++ while ((data = elf_getdata (scn, data)) != NULL) + { + if (data->d_buf + && offset >= data->d_off +@@ -142,7 +307,7 @@ strptr (DSO *dso, int sec, off_t offset) + } + + +-#define read_1(ptr) *ptr++ ++#define read_8(ptr) *ptr++ + + #define read_16(ptr) ({ \ + uint16_t ret = do_read_16 (ptr); \ +@@ -183,28 +348,73 @@ int reltype; + }) + + static void +-dwarf2_write_le32 (unsigned char *p, GElf_Addr val) ++dwarf2_write_le16 (unsigned char *p, uint16_t v) + { +- uint32_t v = (uint32_t) val; ++ p[0] = v; ++ p[1] = v >> 8; ++} + ++static void ++dwarf2_write_le32 (unsigned char *p, uint32_t v) ++{ + p[0] = v; + p[1] = v >> 8; + p[2] = v >> 16; + p[3] = v >> 24; + } + +- + static void +-dwarf2_write_be32 (unsigned char *p, GElf_Addr val) ++dwarf2_write_be16 (unsigned char *p, uint16_t v) + { +- uint32_t v = (uint32_t) val; ++ p[1] = v; ++ p[0] = v >> 8; ++} + ++static void ++dwarf2_write_be32 (unsigned char *p, uint32_t v) ++{ + p[3] = v; + p[2] = v >> 8; + p[1] = v >> 16; + p[0] = v >> 24; + } + ++#define write_8(ptr,val) ({ \ ++ *ptr++ = (val); \ ++}) ++ ++#define write_16(ptr,val) ({ \ ++ do_write_16 (ptr,val); \ ++ ptr += 2; \ ++}) ++ ++#define write_32(ptr,val) ({ \ ++ do_write_32 (ptr,val); \ ++ ptr += 4; \ ++}) ++ ++/* relocated writes can only be called immediately after ++ do_read_32_relocated. ptr must be equal to relptr->ptr (or ++ relend). Might just update the addend. So relocations need to be ++ updated at the end. */ ++ ++#define do_write_32_relocated(ptr,val) ({ \ ++ if (relptr && relptr < relend && relptr->ptr == ptr) \ ++ { \ ++ if (reltype == SHT_REL) \ ++ do_write_32 (ptr, val - relptr->addend); \ ++ else \ ++ relptr->addend = val; \ ++ } \ ++ else \ ++ do_write_32 (ptr,val); \ ++}) ++ ++#define write_32_relocated(ptr,val) ({ \ ++ do_write_32_relocated (ptr,val); \ ++ ptr += 4; \ ++}) ++ + static struct + { + const char *name; +@@ -448,90 +658,638 @@ canonicalize_path (const char *s, char *d) + return rv; + } + ++/* Returns the rest of PATH if it starts with DIR_PREFIX, skipping any ++ / path separators, or NULL if PATH doesn't start with ++ DIR_PREFIX. Might return the empty string if PATH equals DIR_PREFIX ++ (modulo trailing slashes). Never returns path starting with '/'. */ ++static const char * ++skip_dir_prefix (const char *path, const char *dir_prefix) ++{ ++ size_t prefix_len = strlen (dir_prefix); ++ if (strncmp (path, dir_prefix, prefix_len) == 0) ++ { ++ path += prefix_len; ++ while (IS_DIR_SEPARATOR (path[0])) ++ path++; ++ return path; ++ } ++ ++ return 0; ++} ++ ++/* Most strings will be in the existing debug string table. But to ++ replace the base/dest directory prefix we need some new storage. ++ Keep new strings somewhat close together for faster comparison and ++ copying. SIZE should be at least one (and includes space for the ++ zero terminator). The returned pointer points to uninitialized ++ data. */ ++static char * ++new_string_storage (struct strings *strings, size_t size) ++{ ++ assert (size > 0); ++ ++ /* If the string is extra long just create a whole block for ++ it. Normally strings are much smaller than STRMEMSIZE. */ ++ if (strings->last_block == NULL ++ || size > STRMEMSIZE ++ || strings->stridx > STRMEMSIZE ++ || (STRMEMSIZE - strings->stridx) < size) ++ { ++ struct strmemblock *newblock = malloc (sizeof (struct strmemblock) ++ + MAX (STRMEMSIZE, size)); ++ if (newblock == NULL) ++ return NULL; ++ ++ newblock->next = NULL; ++ ++ if (strings->blocks == NULL) ++ strings->blocks = newblock; ++ ++ if (strings->last_block != NULL) ++ strings->last_block->next = newblock; ++ ++ strings->last_block = newblock; ++ strings->stridx = 0; ++ } ++ ++ size_t stridx = strings->stridx; ++ strings->stridx += size + 1; ++ return &strings->last_block->memory[stridx]; ++} ++ ++/* Comparison function used for tsearch. */ + static int +-has_prefix (const char *str, +- const char *prefix) ++strent_compare (const void *a, const void *b) + { +- size_t str_len; +- size_t prefix_len; ++ struct stridxentry *entry_a = (struct stridxentry *)a; ++ struct stridxentry *entry_b = (struct stridxentry *)b; ++ size_t idx_a = entry_a->idx; ++ size_t idx_b = entry_b->idx; + +- str_len = strlen (str); +- prefix_len = strlen (prefix); ++ if (idx_a < idx_b) ++ return -1; + +- if (str_len < prefix_len) +- return 0; ++ if (idx_a > idx_b) ++ return 1; + +- return strncmp (str, prefix, prefix_len) == 0; ++ return 0; + } + +-static int dirty_elf; ++/* Allocates and inserts a new entry for the old index if not yet ++ seen. Returns a stridxentry if the given index has not yet been ++ seen and needs to be filled in with the associated string (either ++ the original string or the replacement string). Returns NULL if the ++ idx is already known. Use in phase 0 to add all strings seen. In ++ phase 1 use string_find_entry instead to get existing entries. */ ++static struct stridxentry * ++string_find_new_entry (struct strings *strings, size_t old_idx) ++{ ++ /* Use next entry in the pool for lookup so we can use it directly ++ if this is a new index. */ ++ struct stridxentry *entry; ++ ++ /* Keep entries close together to make key comparison fast. */ ++ if (strings->last_entries == NULL || strings->entryidx >= STRIDXENTRIES) ++ { ++ size_t entriessz = (sizeof (struct strentblock) ++ + (STRIDXENTRIES * sizeof (struct stridxentry))); ++ struct strentblock *newentries = malloc (entriessz); ++ if (newentries == NULL) ++ error (1, errno, "Couldn't allocate new string entries block"); ++ else ++ { ++ if (strings->entries == NULL) ++ strings->entries = newentries; ++ ++ if (strings->last_entries != NULL) ++ strings->last_entries->next = newentries; ++ ++ strings->last_entries = newentries; ++ strings->last_entries->next = NULL; ++ strings->entryidx = 0; ++ } ++ } ++ ++ entry = &strings->last_entries->entry[strings->entryidx]; ++ entry->idx = old_idx; ++ struct stridxentry **tres = tsearch (entry, &strings->strent_root, ++ strent_compare); ++ if (tres == NULL) ++ error (1, ENOMEM, "Couldn't insert new strtab idx"); ++ else if (*tres == entry) ++ { ++ /* idx not yet seen, must add actual str. */ ++ strings->entryidx++; ++ return entry; ++ } ++ ++ return NULL; /* We already know about this idx, entry already complete. */ ++} ++ ++static struct stridxentry * ++string_find_entry (struct strings *strings, size_t old_idx) ++{ ++ struct stridxentry **ret; ++ struct stridxentry key; ++ key.idx = old_idx; ++ ret = tfind (&key, &strings->strent_root, strent_compare); ++ assert (ret != NULL); /* Can only happen for a bad/non-existing old_idx. */ ++ return *ret; ++} ++ ++/* Adds a string_idx_entry given an index into the old/existing string ++ table. Should be used in phase 0. Does nothing if the index was ++ already registered. Otherwise it checks the string associated with ++ the index. If the old string doesn't start with base_dir an entry ++ will be recorded for the index with the same string. Otherwise a ++ string will be recorded where the base_dir prefix will be replaced ++ by dest_dir. Returns true if this is a not yet seen index and there ++ a replacement file string has been recorded for it, otherwise ++ returns false. */ ++static bool ++record_file_string_entry_idx (struct strings *strings, size_t old_idx) ++{ ++ bool ret = false; ++ struct stridxentry *entry = string_find_new_entry (strings, old_idx); ++ if (entry != NULL) ++ { ++ Strent *strent; ++ const char *old_str = (char *)debug_sections[DEBUG_STR].data + old_idx; ++ const char *file = skip_dir_prefix (old_str, base_dir); ++ if (file == NULL) ++ { ++ /* Just record the existing string. */ ++ strent = strtab_add_len (strings->str_tab, old_str, ++ strlen (old_str) + 1); ++ } ++ else ++ { ++ /* Create and record the altered file path. */ ++ size_t dest_len = strlen (dest_dir); ++ size_t file_len = strlen (file); ++ size_t nsize = dest_len + 1; /* + '\0' */ ++ if (file_len > 0) ++ nsize += 1 + file_len; /* + '/' */ ++ char *nname = new_string_storage (strings, nsize); ++ if (nname == NULL) ++ error (1, ENOMEM, "Couldn't allocate new string storage"); ++ memcpy (nname, dest_dir, dest_len); ++ if (file_len > 0) ++ { ++ nname[dest_len] = '/'; ++ memcpy (nname + dest_len + 1, file, file_len + 1); ++ } ++ else ++ nname[dest_len] = '\0'; ++ ++ strent = strtab_add_len (strings->str_tab, nname, nsize); ++ ret = true; ++ } ++ if (strent == NULL) ++ error (1, ENOMEM, "Could not create new string table entry"); ++ else ++ entry->entry = strent; ++ } ++ ++ return ret; ++} ++ ++/* Same as record_new_string_file_string_entry_idx but doesn't replace ++ base_dir with dest_dir, just records the existing string associated ++ with the index. */ + static void +-dirty_section (unsigned int sec) ++record_existing_string_entry_idx (struct strings *strings, size_t old_idx) + { +- elf_flagdata (debug_sections[sec].elf_data, ELF_C_SET, ELF_F_DIRTY); +- dirty_elf = 1; ++ struct stridxentry *entry = string_find_new_entry (strings, old_idx); ++ if (entry != NULL) ++ { ++ const char *str = (char *)debug_sections[DEBUG_STR].data + old_idx; ++ Strent *strent = strtab_add_len (strings->str_tab, ++ str, strlen (str) + 1); ++ if (strent == NULL) ++ error (1, ENOMEM, "Could not create new string table entry"); ++ else ++ entry->entry = strent; ++ } + } + +-static int +-edit_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir, int phase) ++static void ++setup_strings (struct strings *strings) + { +- unsigned char *ptr = debug_sections[DEBUG_LINE].data, *dir; +- unsigned char **dirt; +- unsigned char *endsec = ptr + debug_sections[DEBUG_LINE].size; +- unsigned char *endcu, *endprol; +- unsigned char opcode_base; +- uint32_t value, dirt_cnt; +- size_t comp_dir_len = !comp_dir ? 0 : strlen (comp_dir); +- size_t abs_file_cnt = 0, abs_dir_cnt = 0; ++ strings->str_tab = strtab_init (false); ++ strings->str_buf = NULL; ++ strings->blocks = NULL; ++ strings->last_block = NULL; ++ strings->entries = NULL; ++ strings->last_entries = NULL; ++ strings->strent_root = NULL; ++} + +- if (phase != 0) +- return 0; ++/* Noop for tdestroy. */ ++static void free_node (void *p __attribute__((__unused__))) { } + +- /* XXX: RhBug:929365, should we error out instead of ignoring? */ ++static void ++destroy_strings (struct strings *strings) ++{ ++ struct strmemblock *smb = strings->blocks; ++ while (smb != NULL) ++ { ++ void *old = smb; ++ smb = smb->next; ++ free (old); ++ } ++ ++ struct strentblock *emb = strings->entries; ++ while (emb != NULL) ++ { ++ void *old = emb; ++ emb = emb->next; ++ free (old); ++ } ++ ++ strtab_free (strings->str_tab); ++ tdestroy (strings->strent_root, &free_node); ++ free (strings->str_buf); ++} ++ ++/* The minimum number of line tables we pre-allocate. */ ++#define MIN_LINE_TABLES 64 ++ ++/* Gets a line_table at offset. Returns true if not yet know and ++ successfully read, false otherwise. Sets *table to NULL and ++ outputs a warning if there was a problem reading the table at the ++ given offset. */ ++static bool ++get_line_table (DSO *dso, size_t off, struct line_table **table) ++{ ++ struct debug_lines *lines = &dso->lines; ++ /* Assume there aren't that many, just do a linear search. The ++ array is probably already sorted because the stmt_lists are ++ probably inserted in order. But we cannot rely on that (maybe we ++ should check that to make searching quicker if possible?). Once ++ we have all line tables for phase 1 (rewriting) we do explicitly ++ sort the array.*/ ++ for (int i = 0; i < lines->used; i++) ++ if (lines->table[i].old_idx == off) ++ { ++ *table = &lines->table[i]; ++ return false; ++ } ++ ++ if (lines->size == lines->used) ++ { ++ struct line_table *new_table = realloc (lines->table, ++ (sizeof (struct line_table) ++ * (lines->size ++ + MIN_LINE_TABLES))); ++ if (new_table == NULL) ++ { ++ error (0, ENOMEM, "Couldn't add more debug_line tables"); ++ *table = NULL; ++ return false; ++ } ++ lines->table = new_table; ++ lines->size += MIN_LINE_TABLES; ++ } ++ ++ struct line_table *t = &lines->table[lines->used]; ++ *table = NULL; ++ ++ t->old_idx = off; ++ t->size_diff = 0; ++ t->replace_dirs = false; ++ t->replace_files = false; ++ ++ unsigned char *ptr = debug_sections[DEBUG_LINE].data; ++ unsigned char *endsec = ptr + debug_sections[DEBUG_LINE].size; + if (ptr == NULL) +- return 0; ++ { ++ error (0, 0, "%s: No .line_table section", dso->filename); ++ return false; ++ } + ++ if (off > debug_sections[DEBUG_LINE].size) ++ { ++ error (0, 0, "%s: Invalid .line_table offset 0x%zx", ++ dso->filename, off); ++ return false; ++ } + ptr += off; + +- endcu = ptr + 4; +- endcu += read_32 (ptr); ++ /* unit_length */ ++ unsigned char *endcu = ptr + 4; ++ t->unit_length = read_32 (ptr); ++ endcu += t->unit_length; + if (endcu == ptr + 0xffffffff) + { + error (0, 0, "%s: 64-bit DWARF not supported", dso->filename); +- return 1; ++ return false; + } + + if (endcu > endsec) + { + error (0, 0, "%s: .debug_line CU does not fit into section", + dso->filename); +- return 1; ++ return false; + } + +- value = read_16 (ptr); +- if (value != 2 && value != 3 && value != 4) ++ /* version */ ++ t->version = read_16 (ptr); ++ if (t->version != 2 && t->version != 3 && t->version != 4) + { + error (0, 0, "%s: DWARF version %d unhandled", dso->filename, +- value); +- return 1; ++ t->version); ++ return false; + } + +- endprol = ptr + 4; +- endprol += read_32 (ptr); ++ /* header_length */ ++ unsigned char *endprol = ptr + 4; ++ t->header_length = read_32 (ptr); ++ endprol += t->header_length; + if (endprol > endcu) + { + error (0, 0, "%s: .debug_line CU prologue does not fit into CU", + dso->filename); +- return 1; ++ return false; ++ } ++ ++ /* min instr len */ ++ t->min_instr_len = *ptr++; ++ ++ /* max op per instr, if version >= 4 */ ++ if (t->version >= 4) ++ t->max_op_per_instr = *ptr++; ++ ++ /* default is stmt */ ++ t->default_is_stmt = *ptr++; ++ ++ /* line base */ ++ t->line_base = (*(int8_t *)ptr++); ++ ++ /* line range */ ++ t->line_range = *ptr++; ++ ++ /* opcode base */ ++ t->opcode_base = *ptr++; ++ ++ if (ptr + t->opcode_base - 1 >= endcu) ++ { ++ error (0, 0, "%s: .debug_line opcode table does not fit into CU", ++ dso->filename); ++ return false; + } ++ lines->used++; ++ *table = t; ++ return true; ++} + +- opcode_base = ptr[4 + (value >= 4)]; +- ptr = dir = ptr + 4 + (value >= 4) + opcode_base; ++static int dirty_elf; ++static void ++dirty_section (unsigned int sec) ++{ ++ elf_flagdata (debug_sections[sec].elf_data, ELF_C_SET, ELF_F_DIRTY); ++ dirty_elf = 1; ++} ++ ++static int ++line_table_cmp (const void *a, const void *b) ++{ ++ struct line_table *ta = (struct line_table *) a; ++ struct line_table *tb = (struct line_table *) b; ++ ++ if (ta->old_idx < tb->old_idx) ++ return -1; ++ ++ if (ta->old_idx > tb->old_idx) ++ return 1; ++ ++ return 0; ++} ++ ++ ++/* Called after phase zero (which records all adjustments needed for ++ the line tables referenced from debug_info) and before phase one ++ starts (phase one will adjust the .debug_line section stmt ++ references using the updated data structures). */ ++static void ++edit_dwarf2_line (DSO *dso) ++{ ++ Elf_Data *linedata = debug_sections[DEBUG_LINE].elf_data; ++ int linendx = debug_sections[DEBUG_LINE].sec; ++ Elf_Scn *linescn = dso->scn[linendx]; ++ unsigned char *old_buf = linedata->d_buf; ++ ++ /* Out with the old. */ ++ linedata->d_size = 0; ++ ++ /* In with the new. */ ++ linedata = elf_newdata (linescn); ++ ++ dso->lines.line_buf = malloc (dso->lines.debug_lines_len); ++ if (dso->lines.line_buf == NULL) ++ error (1, ENOMEM, "No memory for new .debug_line table (0x%zx bytes)", ++ dso->lines.debug_lines_len); ++ ++ linedata->d_size = dso->lines.debug_lines_len; ++ linedata->d_buf = dso->lines.line_buf; ++ debug_sections[DEBUG_LINE].size = linedata->d_size; ++ ++ /* Make sure the line tables are sorted on the old index. */ ++ qsort (dso->lines.table, dso->lines.used, sizeof (struct line_table), ++ line_table_cmp); ++ ++ unsigned char *ptr = linedata->d_buf; ++ for (int ldx = 0; ldx < dso->lines.used; ldx++) ++ { ++ struct line_table *t = &dso->lines.table[ldx]; ++ unsigned char *optr = old_buf + t->old_idx; ++ t->new_idx = ptr - (unsigned char *) linedata->d_buf; ++ ++ /* Just copy the whole table if nothing needs replacing. */ ++ if (! t->replace_dirs && ! t->replace_files) ++ { ++ assert (t->size_diff == 0); ++ memcpy (ptr, optr, t->unit_length + 4); ++ ptr += t->unit_length + 4; ++ continue; ++ } ++ ++ /* Header fields. */ ++ write_32 (ptr, t->unit_length + t->size_diff); ++ write_16 (ptr, t->version); ++ write_32 (ptr, t->header_length + t->size_diff); ++ write_8 (ptr, t->min_instr_len); ++ if (t->version >= 4) ++ write_8 (ptr, t->max_op_per_instr); ++ write_8 (ptr, t->default_is_stmt); ++ write_8 (ptr, t->line_base); ++ write_8 (ptr, t->line_range); ++ write_8 (ptr, t->opcode_base); ++ ++ optr += (4 /* unit len */ ++ + 2 /* version */ ++ + 4 /* header len */ ++ + 1 /* min instr len */ ++ + (t->version >= 4) /* max op per instr, if version >= 4 */ ++ + 1 /* default is stmt */ ++ + 1 /* line base */ ++ + 1 /* line range */ ++ + 1); /* opcode base */ ++ ++ /* opcode len table. */ ++ memcpy (ptr, optr, t->opcode_base - 1); ++ optr += t->opcode_base - 1; ++ ptr += t->opcode_base - 1; ++ ++ /* directory table. We need to find the end (start of file ++ table) anyway, so loop over all dirs, even if replace_dirs is ++ false. */ ++ while (*optr != 0) ++ { ++ const char *dir = (const char *) optr; ++ const char *file_path = NULL; ++ if (t->replace_dirs) ++ { ++ file_path = skip_dir_prefix (dir, base_dir); ++ if (file_path != NULL) ++ { ++ size_t dest_len = strlen (dest_dir); ++ size_t file_len = strlen (file_path); ++ memcpy (ptr, dest_dir, dest_len); ++ ptr += dest_len; ++ if (file_len > 0) ++ { ++ *ptr++ = '/'; ++ memcpy (ptr, file_path, file_len); ++ ptr += file_len; ++ } ++ *ptr++ = '\0'; ++ } ++ } ++ if (file_path == NULL) ++ { ++ size_t dir_len = strlen (dir); ++ memcpy (ptr, dir, dir_len + 1); ++ ptr += dir_len + 1; ++ } ++ ++ optr = (unsigned char *) strchr (dir, 0) + 1; ++ } ++ optr++; ++ *ptr++ = '\0'; ++ ++ /* file table */ ++ if (t->replace_files) ++ { ++ while (*optr != 0) ++ { ++ const char *file = (const char *) optr; ++ const char *file_path = NULL; ++ if (t->replace_dirs) ++ { ++ file_path = skip_dir_prefix (file, base_dir); ++ if (file_path != NULL) ++ { ++ size_t dest_len = strlen (dest_dir); ++ size_t file_len = strlen (file_path); ++ memcpy (ptr, dest_dir, dest_len); ++ ptr += dest_len; ++ if (file_len > 0) ++ { ++ *ptr++ = '/'; ++ memcpy (ptr, file_path, file_len); ++ ptr += file_len; ++ } ++ *ptr++ = '\0'; ++ } ++ } ++ if (file_path == NULL) ++ { ++ size_t file_len = strlen (file); ++ memcpy (ptr, file, file_len + 1); ++ ptr += file_len + 1; ++ } ++ ++ optr = (unsigned char *) strchr (file, 0) + 1; ++ ++ /* dir idx, time, len */ ++ uint32_t dir_idx = read_uleb128 (optr); ++ write_uleb128 (ptr, dir_idx); ++ uint32_t time = read_uleb128 (optr); ++ write_uleb128 (ptr, time); ++ uint32_t len = read_uleb128 (optr); ++ write_uleb128 (ptr, len); ++ } ++ optr++; ++ *ptr++ = '\0'; ++ } ++ ++ /* line number program (and file table if not copied above). */ ++ size_t remaining = (t->unit_length + 4 ++ - (optr - (old_buf + t->old_idx))); ++ memcpy (ptr, optr, remaining); ++ ptr += remaining; ++ } ++} ++ ++/* Called during phase zero for each debug_line table referenced from ++ .debug_info. Outputs all source files seen and records any ++ adjustments needed in the debug_list data structures. Returns true ++ if line_table needs to be rewrite either the dir or file paths. */ ++static bool ++read_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir) ++{ ++ unsigned char *ptr, *dir; ++ unsigned char **dirt; ++ uint32_t value, dirt_cnt; ++ size_t comp_dir_len = !comp_dir ? 0 : strlen (comp_dir); ++ struct line_table *table; ++ ++ if (get_line_table (dso, off, &table) == false ++ || table == NULL) ++ { ++ if (table != NULL) ++ error (0, 0, ".debug_line offset 0x%x referenced multiple times", ++ off); ++ return false; ++ } ++ ++ /* Skip to the directory table. The rest of the header has already ++ been read and checked by get_line_table. */ ++ ptr = debug_sections[DEBUG_LINE].data + off; ++ ptr += (4 /* unit len */ ++ + 2 /* version */ ++ + 4 /* header len */ ++ + 1 /* min instr len */ ++ + (table->version >= 4) /* max op per instr, if version >= 4 */ ++ + 1 /* default is stmt */ ++ + 1 /* line base */ ++ + 1 /* line range */ ++ + 1 /* opcode base */ ++ + table->opcode_base - 1); /* opcode len table */ ++ dir = ptr; + + /* dir table: */ + value = 1; + while (*ptr != 0) + { ++ if (base_dir && dest_dir) ++ { ++ /* Do we need to replace any of the dirs? Calculate new size. */ ++ const char *file_path = skip_dir_prefix ((const char *)ptr, ++ base_dir); ++ if (file_path != NULL) ++ { ++ size_t old_size = strlen ((const char *)ptr) + 1; ++ size_t file_len = strlen (file_path); ++ size_t new_size = strlen (dest_dir) + 1; ++ if (file_len > 0) ++ new_size += 1 + file_len; ++ table->size_diff += (new_size - old_size); ++ table->replace_dirs = true; ++ } ++ } ++ + ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1; + ++value; + } +@@ -561,21 +1319,34 @@ edit_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir, int phase) + { + error (0, 0, "%s: Wrong directory table index %u", + dso->filename, value); +- return 1; ++ return false; + } + file_len = strlen (file); ++ if (base_dir && dest_dir) ++ { ++ /* Do we need to replace any of the files? Calculate new size. */ ++ const char *file_path = skip_dir_prefix (file, base_dir); ++ if (file_path != NULL) ++ { ++ size_t old_size = file_len + 1; ++ size_t file_len = strlen (file_path); ++ size_t new_size = strlen (dest_dir) + 1; ++ if (file_len > 0) ++ new_size += 1 + file_len; ++ table->size_diff += (new_size - old_size); ++ table->replace_files = true; ++ } ++ } + dir_len = strlen ((char *)dirt[value]); + s = malloc (comp_dir_len + 1 + file_len + 1 + dir_len + 1); + if (s == NULL) + { + error (0, ENOMEM, "%s: Reading file table", dso->filename); +- return 1; ++ return false; + } + if (*file == '/') + { + memcpy (s, file, file_len + 1); +- if (dest_dir && has_prefix (file, base_dir)) +- ++abs_file_cnt; + } + else if (*dirt[value] == '/') + { +@@ -599,13 +1370,15 @@ edit_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir, int phase) + canonicalize_path (s, s); + if (list_file_fd != -1) + { +- char *p = NULL; ++ const char *p = NULL; + if (base_dir == NULL) + p = s; +- else if (has_prefix (s, base_dir)) +- p = s + strlen (base_dir); +- else if (has_prefix (s, dest_dir)) +- p = s + strlen (dest_dir); ++ else ++ { ++ p = skip_dir_prefix (s, base_dir); ++ if (p == NULL && dest_dir != NULL) ++ p = skip_dir_prefix (s, dest_dir); ++ } + + if (p) + { +@@ -626,112 +1399,28 @@ edit_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir, int phase) + read_uleb128 (ptr); + read_uleb128 (ptr); + } +- ++ptr; +- +- if (dest_dir) +- { +- unsigned char *srcptr, *buf = NULL; +- size_t base_len = strlen (base_dir); +- size_t dest_len = strlen (dest_dir); +- size_t shrank = 0; +- +- if (dest_len == base_len) +- abs_file_cnt = 0; +- if (abs_file_cnt) +- { +- srcptr = buf = malloc (ptr - dir); +- memcpy (srcptr, dir, ptr - dir); +- ptr = dir; +- } +- else +- ptr = srcptr = dir; +- while (*srcptr != 0) +- { +- size_t len = strlen ((char *)srcptr) + 1; +- const unsigned char *readptr = srcptr; +- +- char *orig = strdup ((const char *) srcptr); +- +- if (*srcptr == '/' && has_prefix ((char *)srcptr, base_dir)) +- { +- if (dest_len < base_len) +- ++abs_dir_cnt; +- memcpy (ptr, dest_dir, dest_len); +- ptr += dest_len; +- readptr += base_len; +- } +- srcptr += len; + +- shrank += srcptr - readptr; +- canonicalize_path ((char *)readptr, (char *)ptr); +- len = strlen ((char *)ptr) + 1; +- shrank -= len; +- ptr += len; +- +- if (memcmp (orig, ptr - len, len)) +- dirty_section (DEBUG_STR); +- free (orig); +- } +- +- if (shrank > 0) +- { +- if (--shrank == 0) +- error (EXIT_FAILURE, 0, +- "canonicalization unexpectedly shrank by one character"); +- else +- { +- memset (ptr, 'X', shrank); +- ptr += shrank; +- *ptr++ = '\0'; +- } +- } +- +- if (abs_dir_cnt + abs_file_cnt != 0) +- { +- size_t len = (abs_dir_cnt + abs_file_cnt) * (base_len - dest_len); +- +- if (len == 1) +- error (EXIT_FAILURE, 0, "-b arg has to be either the same length as -d arg, or more than 1 char longer"); +- memset (ptr, 'X', len - 1); +- ptr += len - 1; +- *ptr++ = '\0'; +- } +- *ptr++ = '\0'; +- ++srcptr; +- +- while (*srcptr != 0) +- { +- size_t len = strlen ((char *)srcptr) + 1; ++ dso->lines.debug_lines_len += 4 + table->unit_length + table->size_diff; ++ return table->replace_dirs || table->replace_files; ++} + +- if (*srcptr == '/' && has_prefix ((char *)srcptr, base_dir)) +- { +- memcpy (ptr, dest_dir, dest_len); +- if (dest_len < base_len) +- { +- memmove (ptr + dest_len, srcptr + base_len, +- len - base_len); +- ptr += dest_len - base_len; +- } +- dirty_section (DEBUG_STR); +- } +- else if (ptr != srcptr) +- memmove (ptr, srcptr, len); +- srcptr += len; +- ptr += len; +- dir = srcptr; +- read_uleb128 (srcptr); +- read_uleb128 (srcptr); +- read_uleb128 (srcptr); +- if (ptr != dir) +- memmove (ptr, dir, srcptr - dir); +- ptr += srcptr - dir; +- } +- *ptr = '\0'; +- free (buf); +- } +- return 0; ++/* Called during phase one, after the table has been sorted. */ ++static size_t ++find_new_list_offs (struct debug_lines *lines, size_t idx) ++{ ++ struct line_table key; ++ key.old_idx = idx; ++ struct line_table *table = bsearch (&key, lines->table, ++ lines->used, ++ sizeof (struct line_table), ++ line_table_cmp); ++ return table->new_idx; + } + ++/* This scans the attributes of one DIE described by the given abbrev_tag. ++ PTR points to the data in the debug_info. It will be advanced till all ++ abbrev data is consumed. In phase zero data is collected, in phase one ++ data might be replaced/updated. */ + static unsigned char * + edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase) + { +@@ -747,20 +1436,36 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase) + { + uint32_t form = t->attr[i].form; + size_t len = 0; +- size_t base_len, dest_len; +- + while (1) + { ++ /* Whether we already handled a string as file for this ++ attribute. If we did then we don't need to handle/record ++ it again when handling the DW_FORM_strp later. */ ++ bool handled_strp = false; ++ ++ /* A stmt_list points into the .debug_line section. In ++ phase zero record all offsets. Then in phase one replace ++ them with the new offsets if we rewrote the line ++ tables. */ + if (t->attr[i].attr == DW_AT_stmt_list) + { + if (form == DW_FORM_data4 + || form == DW_FORM_sec_offset) + { + list_offs = do_read_32_relocated (ptr); +- found_list_offs = 1; ++ if (phase == 0) ++ found_list_offs = 1; ++ else if (need_stmt_update) /* phase one */ ++ { ++ size_t idx, new_idx; ++ idx = do_read_32_relocated (ptr); ++ new_idx = find_new_list_offs (&dso->lines, idx); ++ do_write_32_relocated (ptr, new_idx); ++ } + } + } + ++ /* DW_AT_comp_dir is the current working directory. */ + if (t->attr[i].attr == DW_AT_comp_dir) + { + if (form == DW_FORM_string) +@@ -768,44 +1473,65 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase) + free (comp_dir); + comp_dir = strdup ((char *)ptr); + +- if (phase == 1 && dest_dir && has_prefix ((char *)ptr, base_dir)) ++ if (dest_dir) + { +- base_len = strlen (base_dir); +- dest_len = strlen (dest_dir); +- +- memcpy (ptr, dest_dir, dest_len); +- if (dest_len < base_len) ++ /* In phase zero we are just collecting dir/file ++ names and check whether any need to be ++ adjusted. If so, in phase one we replace ++ those dir/files. */ ++ const char *file = skip_dir_prefix (comp_dir, base_dir); ++ if (file != NULL && phase == 0) ++ need_string_replacement = true; ++ else if (file != NULL && phase == 1) + { +- memset(ptr + dest_len, '/', +- base_len - dest_len); +- ++ size_t orig_len = strlen (comp_dir); ++ size_t dest_len = strlen (dest_dir); ++ size_t file_len = strlen (file); ++ size_t new_len = dest_len; ++ if (file_len > 0) ++ new_len += 1 + file_len; /* + '/' */ ++ ++ /* We don't want to rewrite the whole ++ debug_info section, so we only replace ++ the comp_dir with something equal or ++ smaller, possibly adding some slashes ++ at the end of the new compdir. This ++ normally doesn't happen since most ++ producers will use DW_FORM_strp which is ++ more efficient. */ ++ if (orig_len < new_len) ++ fprintf (stderr, "Warning, not replacing comp_dir " ++ "'%s' prefix ('%s' -> '%s') encoded as " ++ "DW_FORM_string. " ++ "Replacement too large.\n", ++ comp_dir, base_dir, dest_dir); ++ else ++ { ++ /* Add one or more slashes in between to ++ fill up all space (replacement must be ++ of the same length). */ ++ memcpy (ptr, dest_dir, dest_len); ++ memset (ptr + dest_len, '/', ++ orig_len - new_len + 1); ++ } + } +- dirty_section (DEBUG_INFO); + } + } + else if (form == DW_FORM_strp && + debug_sections[DEBUG_STR].data) + { +- char *dir; +- +- dir = (char *) debug_sections[DEBUG_STR].data +- + do_read_32_relocated (ptr); ++ const char *dir; ++ size_t idx = do_read_32_relocated (ptr); ++ dir = (char *) debug_sections[DEBUG_STR].data + idx; + + free (comp_dir); + comp_dir = strdup (dir); + +- if (phase == 1 && dest_dir && has_prefix (dir, base_dir)) ++ if (dest_dir != NULL && phase == 0) + { +- base_len = strlen (base_dir); +- dest_len = strlen (dest_dir); +- +- memcpy (dir, dest_dir, dest_len); +- if (dest_len < base_len) +- { +- memmove (dir + dest_len, dir + base_len, +- strlen (dir + base_len) + 1); +- } +- dirty_section (DEBUG_STR); ++ if (record_file_string_entry_idx (&dso->strings, idx)) ++ need_strp_update = true; ++ handled_strp = true; + } + } + } +@@ -815,10 +1541,13 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase) + && form == DW_FORM_strp + && debug_sections[DEBUG_STR].data) + { ++ /* DW_AT_name is the primary file for this compile ++ unit. If starting with / it is a full path name. ++ Note that we don't handle DW_FORM_string in this ++ case. */ + char *name; +- +- name = (char *) debug_sections[DEBUG_STR].data +- + do_read_32_relocated (ptr); ++ size_t idx = do_read_32_relocated (ptr); ++ name = (char *) debug_sections[DEBUG_STR].data + idx; + if (*name == '/' && comp_dir == NULL) + { + char *enddir = strrchr (name, '/'); +@@ -833,18 +1562,14 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase) + comp_dir = strdup ("/"); + } + +- if (phase == 1 && dest_dir && has_prefix (name, base_dir)) ++ /* First pass (0) records the new name to be ++ added to the debug string pool, the second ++ pass (1) stores it (the new index). */ ++ if (dest_dir && phase == 0) + { +- base_len = strlen (base_dir); +- dest_len = strlen (dest_dir); +- +- memcpy (name, dest_dir, dest_len); +- if (dest_len < base_len) +- { +- memmove (name + dest_len, name + base_len, +- strlen (name + base_len) + 1); +- } +- dirty_section (DEBUG_STR); ++ if (record_file_string_entry_idx (&dso->strings, idx)) ++ need_strp_update = true; ++ handled_strp = true; + } + } + +@@ -886,6 +1611,29 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase) + read_uleb128 (ptr); + break; + case DW_FORM_strp: ++ /* In the first pass we collect all strings, in the ++ second we put the new references back (if there are ++ any changes). */ ++ if (phase == 0) ++ { ++ /* handled_strp is set for attributes refering to ++ files. If it is set the string is already ++ recorded. */ ++ if (! handled_strp) ++ { ++ size_t idx = do_read_32_relocated (ptr); ++ record_existing_string_entry_idx (&dso->strings, idx); ++ } ++ } ++ else if (need_strp_update) /* && phase == 1 */ ++ { ++ struct stridxentry *entry; ++ size_t idx, new_idx; ++ idx = do_read_32_relocated (ptr); ++ entry = string_find_entry (&dso->strings, idx); ++ new_idx = strent_offset (entry->entry); ++ do_write_32_relocated (ptr, new_idx); ++ } + ptr += 4; + break; + case DW_FORM_string: +@@ -930,14 +1678,17 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase) + CU current dir subdirectories. */ + if (comp_dir && list_file_fd != -1) + { +- char *p; ++ const char *p = NULL; + size_t size; + +- if (base_dir && has_prefix (comp_dir, base_dir)) +- p = comp_dir + strlen (base_dir); +- else if (dest_dir && has_prefix (comp_dir, dest_dir)) +- p = comp_dir + strlen (dest_dir); +- else ++ if (base_dir) ++ { ++ p = skip_dir_prefix (comp_dir, base_dir); ++ if (p == NULL && dest_dir != NULL) ++ p = skip_dir_prefix (comp_dir, dest_dir); ++ } ++ ++ if (p == NULL) + p = comp_dir; + + size = strlen (p) + 1; +@@ -951,8 +1702,13 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase) + } + } + +- if (found_list_offs) +- edit_dwarf2_line (dso, list_offs, comp_dir, phase); ++ /* In phase zero we collect all file names (we need the comp_dir for ++ that). Note that calculating the new size and offsets is done ++ separately (at the end of phase zero after all CUs have been ++ scanned in dwarf2_edit). */ ++ if (phase == 0 && found_list_offs ++ && read_dwarf2_line (dso, list_offs, comp_dir)) ++ need_stmt_update = true; + + free (comp_dir); + +@@ -974,6 +1730,20 @@ rel_cmp (const void *a, const void *b) + } + + static int ++line_rel_cmp (const void *a, const void *b) ++{ ++ LINE_REL *rela = (LINE_REL *) a, *relb = (LINE_REL *) b; ++ ++ if (rela->r_offset < relb->r_offset) ++ return -1; ++ ++ if (rela->r_offset > relb->r_offset) ++ return 1; ++ ++ return 0; ++} ++ ++static int + edit_dwarf2 (DSO *dso) + { + Elf_Data *data; +@@ -1009,9 +1779,9 @@ edit_dwarf2 (DSO *dso) + } + + scn = dso->scn[i]; +- data = elf_rawdata (scn, NULL); ++ data = elf_getdata (scn, NULL); + assert (data != NULL && data->d_buf != NULL); +- assert (elf_rawdata (scn, data) == NULL); ++ assert (elf_getdata (scn, data) == NULL); + assert (data->d_off == 0); + assert (data->d_size == dso->shdr[i].sh_size); + debug_sections[j].data = data->d_buf; +@@ -1050,13 +1820,15 @@ edit_dwarf2 (DSO *dso) + { + do_read_16 = buf_read_ule16; + do_read_32 = buf_read_ule32; +- write_32 = dwarf2_write_le32; ++ do_write_16 = dwarf2_write_le16; ++ do_write_32 = dwarf2_write_le32; + } + else if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) + { + do_read_16 = buf_read_ube16; + do_read_32 = buf_read_ube32; +- write_32 = dwarf2_write_be32; ++ do_write_16 = dwarf2_write_be16; ++ do_write_32 = dwarf2_write_be32; + } + else + { +@@ -1179,6 +1951,7 @@ edit_dwarf2 (DSO *dso) + relend->ptr = debug_sections[DEBUG_INFO].data + + (rela.r_offset - base); + relend->addend = rela.r_addend; ++ relend->ndx = ndx; + ++relend; + } + if (relbuf == relend) +@@ -1193,6 +1966,13 @@ edit_dwarf2 (DSO *dso) + + for (phase = 0; phase < 2; phase++) + { ++ /* If we don't need to update anyhing, skip phase 1. */ ++ if (phase == 1 ++ && !need_strp_update ++ && !need_string_replacement ++ && !need_stmt_update) ++ break; ++ + ptr = debug_sections[DEBUG_INFO].data; + relptr = relbuf; + endsec = ptr + debug_sections[DEBUG_INFO].size; +@@ -1240,7 +2020,7 @@ edit_dwarf2 (DSO *dso) + + if (ptr_size == 0) + { +- ptr_size = read_1 (ptr); ++ ptr_size = read_8 (ptr); + if (ptr_size != 4 && ptr_size != 8) + { + error (0, 0, "%s: Invalid DWARF pointer size %d", +@@ -1248,7 +2028,7 @@ edit_dwarf2 (DSO *dso) + return 1; + } + } +- else if (read_1 (ptr) != ptr_size) ++ else if (read_8 (ptr) != ptr_size) + { + error (0, 0, "%s: DWARF pointer size differs between CUs", + dso->filename); +@@ -1281,7 +2061,185 @@ edit_dwarf2 (DSO *dso) + + htab_delete (abbrev); + } ++ ++ /* We might have to recalculate/rewrite the debug_line ++ section. We need to do that before going into phase one ++ so we have all new offsets. We do this separately from ++ scanning the dirs/file names because the DW_AT_stmt_lists ++ might not be in order or skip some padding we might have ++ to (re)move. */ ++ if (phase == 0 && need_stmt_update) ++ { ++ edit_dwarf2_line (dso); ++ ++ /* The line table programs will be moved ++ forward/backwards a bit in the new data. Update the ++ debug_line relocations to the new offsets. */ ++ int rndx = debug_sections[DEBUG_LINE].relsec; ++ if (rndx != 0) ++ { ++ LINE_REL *rbuf; ++ size_t rels; ++ Elf_Data *rdata = elf_getdata (dso->scn[rndx], NULL); ++ int rtype = dso->shdr[rndx].sh_type; ++ rels = dso->shdr[rndx].sh_size / dso->shdr[rndx].sh_entsize; ++ rbuf = malloc (rels * sizeof (LINE_REL)); ++ if (rbuf == NULL) ++ error (1, errno, "%s: Could not allocate line relocations", ++ dso->filename); ++ ++ /* Sort them by offset into section. */ ++ for (size_t i = 0; i < rels; i++) ++ { ++ if (rtype == SHT_RELA) ++ { ++ GElf_Rela rela; ++ if (gelf_getrela (rdata, i, &rela) == NULL) ++ error (1, 0, "Couldn't get relocation: %s", ++ elf_errmsg (-1)); ++ rbuf[i].r_offset = rela.r_offset; ++ rbuf[i].ndx = i; ++ } ++ else ++ { ++ GElf_Rel rel; ++ if (gelf_getrel (rdata, i, &rel) == NULL) ++ error (1, 0, "Couldn't get relocation: %s", ++ elf_errmsg (-1)); ++ rbuf[i].r_offset = rel.r_offset; ++ rbuf[i].ndx = i; ++ } ++ } ++ qsort (rbuf, rels, sizeof (LINE_REL), line_rel_cmp); ++ ++ size_t lndx = 0; ++ for (size_t i = 0; i < rels; i++) ++ { ++ /* These relocations only happen in ET_REL files ++ and are section offsets. */ ++ GElf_Addr r_offset; ++ size_t ndx = rbuf[i].ndx; ++ ++ GElf_Rel rel; ++ GElf_Rela rela; ++ if (rtype == SHT_RELA) ++ { ++ if (gelf_getrela (rdata, ndx, &rela) == NULL) ++ error (1, 0, "Couldn't get relocation: %s", ++ elf_errmsg (-1)); ++ r_offset = rela.r_offset; ++ } ++ else ++ { ++ if (gelf_getrel (rdata, ndx, &rel) == NULL) ++ error (1, 0, "Couldn't get relocation: %s", ++ elf_errmsg (-1)); ++ r_offset = rel.r_offset; ++ } ++ ++ while (r_offset > (dso->lines.table[lndx].old_idx ++ + 4 ++ + dso->lines.table[lndx].unit_length) ++ && lndx < dso->lines.used) ++ lndx++; ++ ++ if (lndx >= dso->lines.used) ++ error (1, 0, ++ ".debug_line relocation offset out of range"); ++ ++ /* Offset (pointing into the line program) moves ++ from old to new index including the header ++ size diff. */ ++ r_offset += ((dso->lines.table[lndx].new_idx ++ - dso->lines.table[lndx].old_idx) ++ + dso->lines.table[lndx].size_diff); ++ ++ if (rtype == SHT_RELA) ++ { ++ rela.r_offset = r_offset; ++ if (gelf_update_rela (rdata, ndx, &rela) == 0) ++ error (1, 0, "Couldn't update relocation: %s", ++ elf_errmsg (-1)); ++ } ++ else ++ { ++ rel.r_offset = r_offset; ++ if (gelf_update_rel (rdata, ndx, &rel) == 0) ++ error (1, 0, "Couldn't update relocation: %s", ++ elf_errmsg (-1)); ++ } ++ } ++ ++ elf_flagdata (rdata, ELF_C_SET, ELF_F_DIRTY); ++ free (rbuf); ++ } ++ } ++ ++ /* Same for the debug_str section. Make sure everything is ++ in place for phase 1 updating of debug_info ++ references. */ ++ if (phase == 0 && need_strp_update) ++ { ++ Strtab *strtab = dso->strings.str_tab; ++ Elf_Data *strdata = debug_sections[DEBUG_STR].elf_data; ++ int strndx = debug_sections[DEBUG_STR].sec; ++ Elf_Scn *strscn = dso->scn[strndx]; ++ ++ /* Out with the old. */ ++ strdata->d_size = 0; ++ /* In with the new. */ ++ strdata = elf_newdata (strscn); ++ ++ /* We really should check whether we had enough memory, ++ but the old ebl version will just abort on out of ++ memory... */ ++ strtab_finalize (strtab, strdata); ++ debug_sections[DEBUG_STR].size = strdata->d_size; ++ dso->strings.str_buf = strdata->d_buf; ++ } ++ ++ } ++ ++ /* After phase 1 we might have rewritten the debug_info with ++ new strp, strings and/or linep offsets. */ ++ if (need_strp_update || need_string_replacement || need_stmt_update) ++ dirty_section (DEBUG_INFO); ++ ++ /* Update any debug_info relocations addends we might have touched. */ ++ if (relbuf != NULL && reltype == SHT_RELA) ++ { ++ Elf_Data *symdata; ++ int relsec_ndx = debug_sections[DEBUG_INFO].relsec; ++ data = elf_getdata (dso->scn[relsec_ndx], NULL); ++ symdata = elf_getdata (dso->scn[dso->shdr[relsec_ndx].sh_link], ++ NULL); ++ ++ relptr = relbuf; ++ while (relptr < relend) ++ { ++ GElf_Sym sym; ++ GElf_Rela rela; ++ int ndx = relptr->ndx; ++ ++ if (gelf_getrela (data, ndx, &rela) == NULL) ++ error (1, 0, "Couldn't get relocation: %s", ++ elf_errmsg (-1)); ++ ++ if (gelf_getsym (symdata, GELF_R_SYM (rela.r_info), ++ &sym) == NULL) ++ error (1, 0, "Couldn't get symbol: %s", elf_errmsg (-1)); ++ ++ rela.r_addend = relptr->addend - sym.st_value; ++ ++ if (gelf_update_rela (data, ndx, &rela) == 0) ++ error (1, 0, "Couldn't update relocations: %s", ++ elf_errmsg (-1)); ++ ++ ++relptr; ++ } ++ elf_flagdata (data, ELF_C_SET, ELF_F_DIRTY); + } ++ + free (relbuf); + } + +@@ -1310,8 +2268,9 @@ fdopen_dso (int fd, const char *name) + GElf_Ehdr ehdr; + int i; + DSO *dso = NULL; ++ size_t phnum; + +- elf = elf_begin (fd, ELF_C_RDWR_MMAP, NULL); ++ elf = elf_begin (fd, ELF_C_RDWR, NULL); + if (elf == NULL) + { + error (0, 0, "cannot open ELF file: %s", elf_errmsg (-1)); +@@ -1348,10 +2307,20 @@ fdopen_dso (int fd, const char *name) + goto error_out; + } + +- elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT); ++ if (elf_getphdrnum (elf, &phnum) != 0) ++ { ++ error (0, 0, "Couldn't get number of phdrs: %s", elf_errmsg (-1)); ++ goto error_out; ++ } ++ ++ /* If there are phdrs we want to maintain the layout of the ++ allocated sections in the file. */ ++ if (phnum != 0) ++ elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT); + + memset (dso, 0, sizeof(DSO)); + dso->elf = elf; ++ dso->phnum = phnum; + dso->ehdr = ehdr; + dso->scn = (Elf_Scn **) &dso->shdr[ehdr.e_shnum + 20]; + +@@ -1362,12 +2331,16 @@ fdopen_dso (int fd, const char *name) + } + + dso->filename = (const char *) strdup (name); ++ setup_strings (&dso->strings); ++ setup_lines (&dso->lines); + return dso; + + error_out: + if (dso) + { + free ((char *) dso->filename); ++ destroy_strings (&dso->strings); ++ destroy_lines (&dso->lines); + free (dso); + } + if (elf) +@@ -1406,13 +2379,6 @@ handle_build_id (DSO *dso, Elf_Data *build_id, + if (!dirty_elf && build_id_seed == NULL) + goto print; + +- if (elf_update (dso->elf, ELF_C_NULL) < 0) +- { +- fprintf (stderr, "Failed to update file: %s\n", +- elf_errmsg (elf_errno ())); +- exit (1); +- } +- + /* Clear the old bits so they do not affect the new hash. */ + memset ((char *) build_id->d_buf + build_id_offset, 0, build_id_size); + +@@ -1475,7 +2441,7 @@ handle_build_id (DSO *dso, Elf_Data *build_id, + + if (u.shdr.sh_type != SHT_NOBITS) + { +- Elf_Data *d = elf_rawdata (dso->scn[i], NULL); ++ Elf_Data *d = elf_getdata (dso->scn[i], NULL); + if (d == NULL) + goto bad; + rpmDigestUpdate(ctx, d->d_buf, d->d_size); +@@ -1509,7 +2475,6 @@ main (int argc, char *argv[]) + int nextopt; + const char **args; + struct stat stat_buf; +- char *p; + Elf_Data *build_id = NULL; + size_t build_id_offset = 0, build_id_size = 0; + +@@ -1541,11 +2506,6 @@ main (int argc, char *argv[]) + fprintf (stderr, "You must specify a base dir if you specify a dest dir\n"); + exit (1); + } +- if (strlen (dest_dir) > strlen (base_dir)) +- { +- fprintf (stderr, "Dest dir longer than base dir is not supported\n"); +- exit (1); +- } + } + + if (build_id_seed != NULL && do_build_id == 0) +@@ -1561,30 +2521,13 @@ main (int argc, char *argv[]) + exit (1); + } + +- /* Ensure clean paths, users can muck with these */ ++ /* Ensure clean paths, users can muck with these. Also removes any ++ trailing '/' from the paths. */ + if (base_dir) + canonicalize_path(base_dir, base_dir); + if (dest_dir) + canonicalize_path(dest_dir, dest_dir); + +- /* Make sure there are trailing slashes in dirs */ +- if (base_dir != NULL && base_dir[strlen (base_dir)-1] != '/') +- { +- p = malloc (strlen (base_dir) + 2); +- strcpy (p, base_dir); +- strcat (p, "/"); +- free (base_dir); +- base_dir = p; +- } +- if (dest_dir != NULL && dest_dir[strlen (dest_dir)-1] != '/') +- { +- p = malloc (strlen (dest_dir) + 2); +- strcpy (p, dest_dir); +- strcat (p, "/"); +- free (dest_dir); +- dest_dir = p; +- } +- + if (list_file != NULL) + { + list_file_fd = open (list_file, O_WRONLY|O_CREAT|O_APPEND, 0644); +@@ -1641,7 +2584,7 @@ main (int argc, char *argv[]) + && build_id == NULL && (dso->shdr[i].sh_flags & SHF_ALLOC)) + { + /* Look for a build-ID note here. */ +- Elf_Data *data = elf_rawdata (elf_getscn (dso->elf, i), NULL); ++ Elf_Data *data = elf_getdata (elf_getscn (dso->elf, i), NULL); + Elf32_Nhdr nh; + Elf_Data dst = + { +@@ -1679,6 +2622,123 @@ main (int argc, char *argv[]) + } + } + ++ /* We might have changed the size of some debug sections. If so make ++ sure the section headers are updated and the data offsets are ++ correct. We set ELF_F_LAYOUT above because we don't want libelf ++ to move any allocated sections around itself if there are any ++ phdrs. Which means we are reponsible for setting the section size ++ and offset fields. Plus the shdr offsets. We don't want to change ++ anything for the phdrs allocated sections. Keep the offset of ++ allocated sections so they are at the same place in the file. Add ++ unallocated ones after the allocated ones. */ ++ if (dso->phnum != 0 && (need_strp_update || need_stmt_update)) ++ { ++ Elf *elf = dso->elf; ++ GElf_Off last_offset; ++ /* We position everything after the phdrs (which normally would ++ be at the start of the ELF file after the ELF header. */ ++ last_offset = (dso->ehdr.e_phoff + gelf_fsize (elf, ELF_T_PHDR, ++ dso->phnum, EV_CURRENT)); ++ ++ /* First find the last allocated section. */ ++ Elf_Scn *scn = NULL; ++ while ((scn = elf_nextscn (elf, scn)) != NULL) ++ { ++ GElf_Shdr shdr_mem; ++ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); ++ if (shdr == NULL) ++ error (1, 0, "Couldn't get shdr: %s\n", elf_errmsg (-1)); ++ ++ /* Any sections we have changed aren't allocated sections, ++ so we don't need to lookup any changed section sizes. */ ++ if ((shdr->sh_flags & SHF_ALLOC) != 0) ++ { ++ GElf_Off off = shdr->sh_offset + (shdr->sh_type != SHT_NOBITS ++ ? shdr->sh_size : 0); ++ if (last_offset < off) ++ last_offset = off; ++ } ++ } ++ ++ /* Now adjust any sizes and offsets for the unallocated sections. */ ++ scn = NULL; ++ while ((scn = elf_nextscn (elf, scn)) != NULL) ++ { ++ GElf_Shdr shdr_mem; ++ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); ++ if (shdr == NULL) ++ error (1, 0, "Couldn't get shdr: %s\n", elf_errmsg (-1)); ++ ++ /* A bug in elfutils before 0.169 means we have to write out ++ all section data, even when nothing changed. ++ https://sourceware.org/bugzilla/show_bug.cgi?id=21199 */ ++#if !_ELFUTILS_PREREQ (0, 169) ++ if (shdr->sh_type != SHT_NOBITS) ++ { ++ Elf_Data *d = elf_getdata (scn, NULL); ++ elf_flagdata (d, ELF_C_SET, ELF_F_DIRTY); ++ } ++#endif ++ if ((shdr->sh_flags & SHF_ALLOC) == 0) ++ { ++ GElf_Off sec_offset = shdr->sh_offset; ++ GElf_Xword sec_size = shdr->sh_size; ++ ++ /* We might have changed the size (and content) of the ++ debug_str or debug_line section. */ ++ size_t secnum = elf_ndxscn (scn); ++ if (secnum == debug_sections[DEBUG_STR].sec) ++ sec_size = debug_sections[DEBUG_STR].size; ++ if (secnum == debug_sections[DEBUG_LINE].sec) ++ sec_size = debug_sections[DEBUG_LINE].size; ++ ++ /* Zero means one. No alignment constraints. */ ++ size_t addralign = shdr->sh_addralign ?: 1; ++ last_offset = (last_offset + addralign - 1) & ~(addralign - 1); ++ sec_offset = last_offset; ++ if (shdr->sh_type != SHT_NOBITS) ++ last_offset += sec_size; ++ ++ if (shdr->sh_size != sec_size ++ || shdr->sh_offset != sec_offset) ++ { ++ /* Make sure unchanged section data is written out ++ at the new location. */ ++ if (shdr->sh_offset != sec_offset ++ && shdr->sh_type != SHT_NOBITS) ++ { ++ Elf_Data *d = elf_getdata (scn, NULL); ++ elf_flagdata (d, ELF_C_SET, ELF_F_DIRTY); ++ } ++ ++ shdr->sh_size = sec_size; ++ shdr->sh_offset = sec_offset; ++ if (gelf_update_shdr (scn, shdr) == 0) ++ error (1, 0, "Couldn't update shdr: %s\n", ++ elf_errmsg (-1)); ++ } ++ } ++ } ++ ++ /* Position the shdrs after the last (unallocated) section. */ ++ const size_t offsize = gelf_fsize (elf, ELF_T_OFF, 1, EV_CURRENT); ++ GElf_Off new_offset = ((last_offset + offsize - 1) ++ & ~((GElf_Off) (offsize - 1))); ++ if (dso->ehdr.e_shoff != new_offset) ++ { ++ dso->ehdr.e_shoff = new_offset; ++ if (gelf_update_ehdr (elf, &dso->ehdr) == 0) ++ error (1, 0, "Couldn't update ehdr: %s\n", elf_errmsg (-1)); ++ } ++ } ++ ++ if (elf_update (dso->elf, ELF_C_NULL) < 0) ++ { ++ fprintf (stderr, "Failed to update file: %s\n", ++ elf_errmsg (elf_errno ())); ++ exit (1); ++ } ++ + if (do_build_id && build_id != NULL) + handle_build_id (dso, build_id, build_id_offset, build_id_size); + +@@ -1697,6 +2757,11 @@ main (int argc, char *argv[]) + /* Restore old access rights */ + chmod (file, stat_buf.st_mode); + ++ free ((char *) dso->filename); ++ destroy_strings (&dso->strings); ++ destroy_lines (&dso->lines); ++ free (dso); ++ + poptFreeContext (optCon); + + return 0; +-- +2.9.3 + diff --git a/0011-Add-option-to-have-unique-debug-source-dirs-across-v.patch b/0011-Add-option-to-have-unique-debug-source-dirs-across-v.patch new file mode 100644 index 0000000..df328a5 --- /dev/null +++ b/0011-Add-option-to-have-unique-debug-source-dirs-across-v.patch @@ -0,0 +1,290 @@ +From cf12c3f2c985fcaf94bb5e2b24178290f5ef09ed Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +References: <189b4f88c8e100155ec23a1e0b214bdc8473532a.1488964568.git.pmatilai@redhat.com> +From: Mark Wielaard +Date: Tue, 28 Feb 2017 20:45:24 +0100 +Subject: [PATCH 11/11] Add option to have unique debug source dirs across + version/release/arch. + +Introduce a new macro _unique_debug_srcs that when set will pass +--unique-debug-src-base "%{name}" to find-debuginfo.sh which will +move sources into a unique "--." directory +under /usr/src/debug/ and makes debugedit rewrite the source paths +in the debuginfo to use that unique directory name. + +Traditionally the debug src dir was named after the builddir which +was defined through the %setup macro which used the -n name argument +to define the builddir name and source archive to use. The builddir +might not be unique though between package versions. + +Now that debugedit doesn't have strict base and dest dir length +restrictions for rewriting the source dir paths this can now be made +more flexible. + +The added testcases show the difference between the old and new way. +The hello2.spec file defines the name of the package as hello2, but +uses the %setup marcro with -n hello-1.0 to use the hello-1.0.tar.gz +archive. This would traditionally result in a hello-1.0 builddir +which would be moved under /usr/src/debug. Possibly conflicting +with any other package (version) that used the same builddir name. +When defining _unique_debug_srcs to 1 that builddir will be moved +to --. instead (hello2-1.0-1.). + +The testcases check that both the actual package source filess under +/usr/debug/src/ and the source paths as found in the .debug files are +under the traditional or new unique directory names depending on whether +the new _unique_debug_srcs macro is defined. + +Signed-off-by: Mark Wielaard +--- + macros.in | 8 ++++- + scripts/find-debuginfo.sh | 35 ++++++++++++++++++--- + tests/rpmbuild.at | 80 +++++++++++++++++++++++++++++++++++++++++++++++ + tests/rpmbuildid.at | 5 +++ + 4 files changed, 123 insertions(+), 5 deletions(-) + +diff --git a/macros.in b/macros.in +index 4d90282..e48ef60 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} %{?_include_gdb_index:-i} %{?_unique_build_ids:--ver-rel "%{VERSION}-%{RELEASE}"} %{?_unique_debug_names:--unique-debug-arch "%{_arch}"} %{?_find_debuginfo_dwz_opts} %{?_find_debuginfo_opts} "%{_builddir}/%{?buildsubdir}"\ ++ %{_rpmconfigdir}/find-debuginfo.sh %{?_missing_build_ids_terminate_build:--strict-build-id} %{?_include_minidebuginfo:-m} %{?_include_gdb_index:-i} %{?_unique_build_ids:--ver-rel "%{VERSION}-%{RELEASE}"} %{?_unique_debug_names:--unique-debug-arch "%{_arch}"} %{?_unique_debug_srcs:--unique-debug-src-base "%{name}"} %{?_find_debuginfo_dwz_opts} %{?_find_debuginfo_opts} "%{_builddir}/%{?buildsubdir}"\ + %{nil} + + # Template for debug information sub-package. +@@ -495,6 +495,12 @@ package or when debugging this package.\ + # Requires _unique_build_ids. + %_unique_debug_names 1 + ++# Whether the /usr/debug/src/ directories should be unique between ++# package version, release and architecture. If set to 1 this will pass ++# --unique-debug-src-base "%{name}" to find-debuginfo.sh to name the ++# directory under /usr/debug/src as --. ++%_unique_debug_srcs 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 3653c48..1420ef6 100644 +--- a/scripts/find-debuginfo.sh ++++ b/scripts/find-debuginfo.sh +@@ -67,6 +67,9 @@ ver_rel= + # Arch given by --unique-debug-arch + unique_debug_arch= + ++# Base given by --unique-debug-src-base ++unique_debug_src_base= ++ + BUILDDIR=. + out=debugfiles.list + nout=0 +@@ -94,6 +97,10 @@ while [ $# -gt 0 ]; do + unique_debug_arch=$2 + shift + ;; ++ --unique-debug-src-base) ++ unique_debug_src_base=$2 ++ shift ++ ;; + -g) + strip_g=true + ;; +@@ -137,6 +144,11 @@ if test -z "$ver_rel" -a -n "$unique_debug_arch"; then + exit 2 + fi + ++if test -z "$unique_debug_arch" -a -n "$unique_debug_src_base"; then ++ echo >&2 "*** ERROR: --unique-debug-src-base (${unique_debug_src_base}) needs --unique-debug-arch (${unique_debug_arch})" ++ exit 2 ++fi ++ + i=0 + while ((i < nout)); do + outs[$i]="$BUILDDIR/${outs[$i]}" +@@ -291,7 +303,14 @@ while read nlinks inum f; do + 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 \ ++ # See also cpio SOURCEFILE copy. Directories must match up. ++ debug_base_name="$RPM_BUILD_DIR" ++ debug_dest_name="/usr/src/debug" ++ if [ ! -z "$unique_debug_src_base" ]; then ++ debug_base_name="$BUILDDIR" ++ debug_dest_name="/usr/src/debug/${unique_debug_src_base}-${ver_rel}.${unique_debug_arch}" ++ fi ++ id=$(${lib_rpm_dir}/debugedit -b $debug_base_name -d $debug_dest_name \ + -i $build_id_seed -l "$SOURCEFILE" "$f") || exit + if [ $nlinks -gt 1 ]; then + eval linkedid_$inum=\$id +@@ -388,11 +407,19 @@ do + done + + if [ -s "$SOURCEFILE" ]; then +- mkdir -p "${RPM_BUILD_ROOT}/usr/src/debug" ++ # See also debugedit invocation. Directories must match up. ++ debug_base_name="$RPM_BUILD_DIR" ++ debug_dest_name="/usr/src/debug" ++ if [ ! -z "$unique_debug_src_base" ]; then ++ debug_base_name="$BUILDDIR" ++ debug_dest_name="/usr/src/debug/${unique_debug_src_base}-${ver_rel}.${unique_debug_arch}" ++ fi ++ ++ mkdir -p "${RPM_BUILD_ROOT}${debug_dest_name}" + LC_ALL=C sort -z -u "$SOURCEFILE" | grep -E -v -z '(|)$' | +- (cd "$RPM_BUILD_DIR"; cpio -pd0mL "${RPM_BUILD_ROOT}/usr/src/debug") ++ (cd "${debug_base_name}"; cpio -pd0mL "${RPM_BUILD_ROOT}${debug_dest_name}") + # stupid cpio creates new directories in mode 0700, fixup +- find "${RPM_BUILD_ROOT}/usr/src/debug" -type d -print0 | ++ find "${RPM_BUILD_ROOT}${debug_dest_name}" -type d -print0 | + xargs --no-run-if-empty -0 chmod a+rx + fi + +diff --git a/tests/rpmbuild.at b/tests/rpmbuild.at +index 0a2c01e..a46822f 100644 +--- a/tests/rpmbuild.at ++++ b/tests/rpmbuild.at +@@ -399,6 +399,7 @@ 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_debug_names" \ ++ --undefine "_unique_debug_srcs" \ + -ba "${abs_srcdir}"/data/SPECS/hello2.spec + + # The debuginfo package should contain a .debug file for each binary +@@ -701,3 +702,82 @@ readelf -S ./usr/lib/debug/usr/local/bin/hello2*.debug \ + [], + [ignore]) + AT_CLEANUP ++ ++# ------------------------------ ++# Check that a debug source is in a "unique" directory when requested. ++AT_SETUP([rpmbuild debuginfo unique debug src dir]) ++AT_KEYWORDS([build] [debuginfo]) ++AT_CHECK([ ++rm -rf ${TOPDIR} ++AS_MKDIR_P(${TOPDIR}/SOURCES) ++ ++# Build a package that has some debuginfo ++cp "${abs_srcdir}"/data/SOURCES/hello-1.0.tar.gz "${abs_srcdir}"/data/SOURCES/hello-1.0-modernize.patch ${TOPDIR}/SOURCES ++ ++# Note that the spec defines hello2 as name, but the source is hello-1.0. ++# Disable dwz to make debuginfo path rewrite checking easier. ++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 "_find_debuginfo_dwz_opts" \ ++ --define "_unique_debug_srcs 1" \ ++ -ba "${abs_srcdir}"/data/SPECS/hello2.spec ++ ++# Unpack the debuginfo rpms so we can check the .debug files. ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu --quiet ++ ++# Check that the source path is "unique" ++# Drop the final arch prefix to make the test arch-independent. ++ls ./usr/src/debug/ | cut -f1,2 -d\. ++ ++# Check that the source path has been rewritten in the .debug file. ++# Drop the final arch prefix to make the test arch-independent. ++readelf --debug-dump=info ./usr/lib/debug/usr/local/bin/hello2*.debug \ ++ | grep comp_dir | cut -f5- -d/ | cut -f1,2 -d\. ++], ++[0], ++[hello2-1.0-1 ++hello2-1.0-1 ++], ++[ignore]) ++AT_CLEANUP ++ ++# ------------------------------ ++# Check that a debug source is NOT in a "unique" directory when not requested. ++# It will be in the "build directory" name under /usr/src/debug. ++AT_SETUP([rpmbuild debuginfo no unique debug src dir]) ++AT_KEYWORDS([build] [debuginfo]) ++AT_CHECK([ ++rm -rf ${TOPDIR} ++AS_MKDIR_P(${TOPDIR}/SOURCES) ++ ++# Build a package that has some debuginfo ++cp "${abs_srcdir}"/data/SOURCES/hello-1.0.tar.gz "${abs_srcdir}"/data/SOURCES/hello-1.0-modernize.patch ${TOPDIR}/SOURCES ++ ++# Note that the spec defines hello2 as name, but the source is hello-1.0. ++# Disable dwz to make debuginfo path rewrite checking easier. ++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 "_find_debuginfo_dwz_opts" \ ++ --undefine "_unique_debug_srcs" \ ++ -ba "${abs_srcdir}"/data/SPECS/hello2.spec ++ ++# Unpack the debuginfo rpms so we can check the .debug files. ++rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello2-debuginfo-1.0-1.*.rpm \ ++ | cpio -diu --quiet ++ ++# Check that the source path is "unique" ++ls ./usr/src/debug/ ++ ++# Check that the source path has been rewritten in the .debug file. ++readelf --debug-dump=info ./usr/lib/debug/usr/local/bin/hello2*.debug \ ++ | grep comp_dir | cut -f5- -d/ ++], ++[0], ++[hello-1.0 ++hello-1.0 ++], ++[ignore]) ++AT_CLEANUP +diff --git a/tests/rpmbuildid.at b/tests/rpmbuildid.at +index ede1181..15c0620 100644 +--- a/tests/rpmbuildid.at ++++ b/tests/rpmbuildid.at +@@ -71,6 +71,7 @@ run rpmbuild \ + --rcfile=${abs_top_builddir}/rpmrc \ + --define="_build_id_links alldebug" \ + --undefine "_unique_debug_names" \ ++ --undefine "_unique_debug_srcs" \ + --quiet -ba "${abs_srcdir}"/data/SPECS/hello.spec + + # There should be zero build-id files in the main package +@@ -257,6 +258,7 @@ run rpmbuild \ + --rcfile=${abs_top_builddir}/rpmrc \ + --define="_build_id_links separate" \ + --undefine "_unique_debug_names" \ ++ --undefine "_unique_debug_srcs" \ + --quiet -ba "${abs_srcdir}"/data/SPECS/hello.spec + + # There should be one build-id files in the main and debuginfo package +@@ -441,6 +443,7 @@ run rpmbuild \ + --rcfile=${abs_top_builddir}/rpmrc \ + --define="_build_id_links compat" \ + --undefine "_unique_debug_names" \ ++ --undefine "_unique_debug_srcs" \ + --quiet -ba "${abs_srcdir}"/data/SPECS/hello.spec + + # There should be one build-id files in the main and debuginfo package. +@@ -1125,6 +1128,7 @@ run rpmbuild --quiet \ + --rcfile=${abs_top_builddir}/rpmrc \ + --undefine="_unique_build_ids" \ + --undefine="_unique_debug_names" \ ++ --undefine="_unique_debug_srcs" \ + -ba "${abs_srcdir}"/data/SPECS/hello.spec + + rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-1.0-1.*.rpm \ +@@ -1145,6 +1149,7 @@ run rpmbuild --quiet \ + --rcfile=${abs_top_builddir}/rpmrc \ + --undefine="_unique_build_ids" \ + --undefine="_unique_debug_names" \ ++ --undefine="_unique_debug_srcs" \ + -ba "${abs_srcdir}"/data/SPECS/hello-r2.spec + + rpm2cpio ${abs_builddir}/testing/build/RPMS/*/hello-1.0-2.*.rpm \ +-- +2.9.3 + diff --git a/rpm-4.13.90-ldflags.patch b/rpm-4.13.90-ldflags.patch new file mode 100644 index 0000000..ad65430 --- /dev/null +++ b/rpm-4.13.90-ldflags.patch @@ -0,0 +1,15 @@ +diff -up rpm-4.9.1.1/macros.in.jx rpm-4.9.1.1/macros.in +--- rpm-4.9.1.1/macros.in.jx 2011-08-03 16:19:05.000000000 -0400 ++++ rpm-4.9.1.1/macros.in 2011-08-08 09:41:52.981064316 -0400 +@@ -674,9 +674,10 @@ print (t)\ + RPM_SOURCE_DIR=\"%{u2p:%{_sourcedir}}\"\ + RPM_BUILD_DIR=\"%{u2p:%{_builddir}}\"\ + RPM_OPT_FLAGS=\"%{optflags}\"\ ++ RPM_LD_FLAGS=\"%{?__global_ldflags}\"\ + RPM_ARCH=\"%{_arch}\"\ + RPM_OS=\"%{_os}\"\ +- export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_ARCH RPM_OS\ ++ export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_LD_FLAGS RPM_ARCH RPM_OS\ + RPM_DOC_DIR=\"%{_docdir}\"\ + export RPM_DOC_DIR\ + RPM_PACKAGE_NAME=\"%{NAME}\"\ diff --git a/rpm-4.9.1.1-ld-flags.patch b/rpm-4.9.1.1-ld-flags.patch deleted file mode 100644 index f8b2524..0000000 --- a/rpm-4.9.1.1-ld-flags.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff -up rpm-4.9.1.1/macros.in.jx rpm-4.9.1.1/macros.in ---- rpm-4.9.1.1/macros.in.jx 2011-08-03 16:19:05.000000000 -0400 -+++ rpm-4.9.1.1/macros.in 2011-08-08 09:41:52.981064316 -0400 -@@ -674,9 +674,10 @@ print (t)\ - RPM_SOURCE_DIR=\"%{u2p:%{_sourcedir}}\"\ - RPM_BUILD_DIR=\"%{u2p:%{_builddir}}\"\ - RPM_OPT_FLAGS=\"%{optflags}\"\ -+ RPM_LD_FLAGS=\"%{?__global_ldflags}\"\ - RPM_ARCH=\"%{_arch}\"\ - RPM_OS=\"%{_os}\"\ -- export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_ARCH RPM_OS\ -+ export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_LD_FLAGS RPM_ARCH RPM_OS\ - RPM_DOC_DIR=\"%{_docdir}\"\ - export RPM_DOC_DIR\ - RPM_PACKAGE_NAME=\"%{name}\"\ diff --git a/rpm.spec b/rpm.spec index 4b62853..741a239 100644 --- a/rpm.spec +++ b/rpm.spec @@ -29,7 +29,7 @@ Summary: The RPM package management system Name: rpm Version: %{rpmver} -Release: %{?snapver:0.%{snapver}.}3%{?dist} +Release: %{?snapver:0.%{snapver}.}4%{?dist} Group: System Environment/Base Url: http://www.rpm.org/ Source0: http://rpm.org/releases/%{srcdir}/%{name}-%{srcver}.tar.bz2 @@ -69,11 +69,23 @@ Patch142: rpm-4.13.x-fix-refcount-for-spec_type.patch # Fedora-specific (python3) patch (RHBZ #1405483) Patch200: rpm-4.13.x-pythondistdeps-python3.patch +# debuginfo backports (#1427970) +Patch250: 0001-Add-build-id-links-to-rpm-for-all-ELF-files.patch +Patch251: 0002-Make-it-possible-to-have-unique-build-ids-across-bui.patch +Patch252: 0003-Make-adding-GDB-index-sections-configurable.patch +Patch253: 0004-Add-option-to-have-unique-debug-file-names-across-ve.patch +Patch254: 0005-Fix-behavior-when-_build_id_links-is-undefined.patch +Patch255: 0006-Fix-debuginfo-etc-when-subpackages-have-different-ve.patch +Patch256: 0007-Only-process-regular-files-when-generating-build-ids.patch +Patch257: 0008-configure.ac-use-LIBDW-always-conditionally.patch +Patch258: 0009-Fix-libdw-configure-check.patch +Patch259: 0010-debugedit-Support-String-Line-table-rewriting-for-la.patch +Patch260: 0011-Add-option-to-have-unique-debug-source-dirs-across-v.patch # These are not yet upstream Patch302: rpm-4.7.1-geode-i686.patch # Probably to be upstreamed in slightly different form -Patch304: rpm-4.9.1.1-ld-flags.patch +Patch304: rpm-4.13.90-ldflags.patch # Partially GPL/LGPL dual-licensed and some bits with BSD # SourceLicense: (GPLv2+ and LGPLv2+ with exceptions) and BSD @@ -570,6 +582,9 @@ exit 0 %doc doc/librpm/html/* %changelog +* Wed Mar 08 2017 Panu Matilainen - 4.13.0.1-4 +- Mark Wielaard's backports for debuginfo parallel installation etc (#1427970) + * Fri Feb 24 2017 Pavlina Moravcova Varekova - 4.13.0.1-3 - Fix number of references on spec_Type (#1426578)