A new user interface for you! Read more...

File debugsubpkg.diff of Package rpm

--- ./build/files.c.orig	2017-02-16 09:52:49.292092380 +0000
+++ ./build/files.c	2017-03-22 13:32:42.911865500 +0000
@@ -21,6 +21,10 @@
 #include <rpm/rpmlog.h>
 #include <rpm/rpmbase64.h>
 
+#if HAVE_GELF_H
+#include <gelf.h>
+#endif
+
 #include "rpmio/rpmio_internal.h"	/* XXX rpmioSlurp */
 #include "misc/fts.h"
 #include "lib/rpmfi_internal.h"	/* XXX fi->apath */
@@ -2155,13 +2159,302 @@ exit:
     return rc;
 }
 
+#if HAVE_GELF_H && HAVE_LIBELF
+/* Query the build-id from the ELF file NAME and store it in the newly
+   allocated *build_id array of size *build_id_size.  Returns -1 on
+   error.  */
+
+static int
+getELFBuildId (const char *name,
+	       unsigned char **id, size_t *id_size)
+{
+  int fd, i;
+  Elf *elf;
+  GElf_Ehdr ehdr;
+  Elf_Data *build_id = NULL;
+  size_t build_id_offset = 0, build_id_size = 0;
+
+  /* Now query the build-id of the file and add the
+     corresponding links in the .build-id tree.
+     The following code is based on tools/debugedit.c.  */
+  fd = open (name, O_RDONLY);
+  if (fd < 0)
+    return -1;
+  elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+  if (elf == NULL)
+    {
+      fprintf (stderr, "cannot open ELF file: %s",
+	       elf_errmsg (-1));
+      close (fd);
+      return -1;
+    }
+  if (elf_kind (elf) != ELF_K_ELF
+      || gelf_getehdr (elf, &ehdr) == NULL
+      || (ehdr.e_type != ET_DYN
+	  && ehdr.e_type != ET_EXEC
+	  && ehdr.e_type != ET_REL))
+    {
+      elf_end (elf);
+      close (fd);
+      return -1;
+    }
+  for (i = 0; i < ehdr.e_shnum; ++i)
+    {
+      Elf_Scn *s = elf_getscn (elf, i);
+      GElf_Shdr shdr;
+      Elf_Data *data;
+      Elf32_Nhdr nh;
+      Elf_Data dst =
+	{
+	  .d_version = EV_CURRENT, .d_type = ELF_T_NHDR,
+	  .d_buf = &nh, .d_size = sizeof nh
+	};
+      Elf_Data src = dst;
+
+      gelf_getshdr (s, &shdr);
+      /* LD creates .note.gnu.build-id with SHF_ALLOC but the DWZ
+         common debuginfo only file only has non-allocated sections.  */
+      if (shdr.sh_type != SHT_NOTE)
+	continue;
+
+      /* Look for a build-ID note here.  */
+      data = elf_rawdata (s, NULL);
+      src.d_buf = data->d_buf;
+      assert (sizeof (Elf32_Nhdr) == sizeof (Elf64_Nhdr));
+      while ((unsigned char *)data->d_buf + data->d_size - (unsigned char *)src.d_buf > (int) sizeof nh
+	     && elf32_xlatetom (&dst, &src, ehdr.e_ident[EI_DATA]))
+	{
+	  Elf32_Word len = sizeof nh + nh.n_namesz;
+	  len = (len + 3) & ~3;
+
+	  if (nh.n_namesz == sizeof "GNU" && nh.n_type == 3
+	      && !memcmp ((unsigned char *)src.d_buf + sizeof nh, "GNU", sizeof "GNU"))
+	    {
+	      build_id = data;
+	      build_id_offset = (unsigned char *)src.d_buf + len - (unsigned char *)data->d_buf;
+	      build_id_size = nh.n_descsz;
+	      break;
+	    }
+
+	  len += nh.n_descsz;
+	  len = (len + 3) & ~3;
+	  src.d_buf = (unsigned char *)src.d_buf + len;
+	}
+
+      if (build_id != NULL)
+	break;
+    }
+
+  if (build_id == NULL)
+    return -1;
+
+  *id = malloc (build_id_size);
+  *id_size = build_id_size;
+  memcpy (*id, (unsigned char *)build_id->d_buf + build_id_offset, build_id_size);
+
+  elf_end (elf);
+  close (fd);
+
+  return 0;
+}
+
+
+static rpmTag copyTagsForDebug[] = {
+    RPMTAG_EPOCH,
+    RPMTAG_VERSION,
+    RPMTAG_RELEASE,
+    RPMTAG_LICENSE,
+    RPMTAG_PACKAGER,
+    RPMTAG_DISTRIBUTION,
+    RPMTAG_DISTURL,
+    RPMTAG_VENDOR,
+    RPMTAG_ICON,
+    RPMTAG_URL,
+    RPMTAG_CHANGELOGTIME,
+    RPMTAG_CHANGELOGNAME,
+    RPMTAG_CHANGELOGTEXT,
+    RPMTAG_PREFIXES,
+    RPMTAG_RHNPLATFORM,
+    RPMTAG_OS,
+    RPMTAG_DISTTAG,
+    RPMTAG_CVSID,
+    RPMTAG_ARCH,
+    0
+};
+
+/* Add a new debuginfo package based on PKG with FILES.  */
+
+static Package addDebuginfoPackage(rpmSpec spec, Package pkg, ARGV_t files)
+{
+  const char *name;
+  char tmp[1024];
+  Package dbg = newPackage(NULL, spec->pool, &spec->packages);
+  name = headerGetString(pkg->header, RPMTAG_NAME);
+  /* Set name, summary and group.  */
+  snprintf(tmp, 1024, "%s-debuginfo", name);
+  headerPutString(dbg->header, RPMTAG_NAME, tmp);
+  snprintf(tmp, 1024, "Debug information for package %s", name);
+  headerPutString(dbg->header, RPMTAG_SUMMARY, tmp);
+  snprintf(tmp, 1024, "This package provides debug information for package %s.\n"
+	   "Debug information is useful when developing applications that use this\n"
+	   "package or when debugging this package.", name);
+  headerPutString(dbg->header, RPMTAG_DESCRIPTION, tmp);
+  headerPutString(dbg->header, RPMTAG_GROUP, "Development/Debug");
+  /* Inherit other tags from parent.  */
+  headerCopyTags(spec->packages->header,
+		 dbg->header, copyTagsForDebug);
+
+  /* Add self-provides */
+  dbg->ds = rpmdsThis(dbg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL);
+  addPackageProvides(dbg);
+
+  /* Build up the files list.  */
+  dbg->fileList = files;
+  return dbg;
+}
+
+/* Process the filelist of PKG and see to eventually create a debuginfo
+   packge for it.  */
+
+static Package processDebuginfo(rpmSpec spec, Package pkg, char *buildroot)
+{
+    const char *a;
+
+    elf_version(EV_CURRENT);
+    a = headerGetString(pkg->header, RPMTAG_ARCH);
+    if (strcmp(a, "noarch") != 0)
+      {
+	rpmfi fi = rpmfilesIter(pkg->cpioList, RPMFI_ITER_FWD);
+	char tmp[1024];
+	const char *name;
+	ARGV_t files = NULL;
+	int seen_build_id = 0;
+
+	/* Check if the current package has files with debug info
+	   and record them.  */
+	fi = rpmfiInit(fi, 0);
+	while (rpmfiNext(fi) >= 0)
+	  {
+	    int i;
+	    unsigned char *build_id = NULL;
+	    size_t build_id_size = 0;
+	    struct stat sbuf;
+
+	    name = rpmfiFN(fi);
+	    /* Pre-pend %buildroot/usr/lib/debug and append .debug.  */
+	    snprintf(tmp, 1024, "%s/usr/lib/debug%s.debug",
+		      buildroot, name);
+	    /* If that file exists we have debug information for it.  */
+	    if (access(tmp, F_OK) != 0)
+	      continue;
+
+	    /* Append the file list preamble.  */
+	    if (!files)
+	      {
+		argvAdd(&files, "%defattr(-,root,root)");
+		argvAdd(&files, "%dir /usr/lib/debug");
+	      }
+	    /* Add the files main debug-info file.  */
+	    snprintf(tmp, 1024, "/usr/lib/debug/%s.debug", name);
+	    argvAdd(&files, tmp);
+
+	    snprintf(tmp, 1024, "%s%s", buildroot, name);
+	    /* Do not bother to check build-ids for symbolic links.
+	       We'll handle them for the link target.  */
+	    if (lstat(tmp, &sbuf) == -1 || S_ISLNK(sbuf.st_mode))
+	      continue;
+
+	    /* Try to gather the build-id from the binary.  */
+	    if (getELFBuildId(tmp, &build_id, &build_id_size) == -1)
+	      continue;
+
+	    /* If we see build-id links for the first time add the
+	       directory.  */
+	    if (!seen_build_id)
+	      {
+		seen_build_id = 1;
+		argvAdd(&files, "%dir /usr/lib/debug/.build-id");
+	      }
+
+	    /* From the build-id construct the two links pointing back
+	       to the debug information file and the binary.  */
+	    snprintf(tmp, 1024, "/usr/lib/debug/.build-id/%02x/",
+		      build_id[0]);
+	    for (i = 1; i < build_id_size; ++i)
+	      sprintf(tmp + strlen(tmp), "%02x", build_id[i]);
+	    argvAdd(&files, tmp);
+	    sprintf(tmp + strlen(tmp), ".debug");
+	    argvAdd(&files, tmp);
+
+	    free(build_id);
+	  }
+
+	/* If there are debuginfo files for this package add a
+	   new debuginfo package.  */
+	if (files)
+	  return addDebuginfoPackage (spec, pkg, files);
+      }
+    return NULL;
+}
+
+
+static char *addDebugDWZ(ARGV_t *filesp, char *buildroot)
+{
+    char tmp[1024];
+    struct stat sbuf;
+    char *dwz_dbg_buildid = NULL;
+    DIR *d;
+    struct dirent *de;
+    int i;
+
+    snprintf(tmp, 1024, "%s%s", buildroot, "/usr/lib/debug/.dwz");
+    if (lstat(tmp, &sbuf) != 0 || !S_ISDIR(sbuf.st_mode))
+	return NULL;
+    d = opendir(tmp);
+    if (!d)
+	return NULL;
+
+    argvAdd(filesp, "/usr/lib/debug/.dwz");
+    while ((de = readdir (d))) {
+	unsigned char *build_id = NULL;
+	size_t build_id_size = 0;
+
+	snprintf(tmp, 1024, "%s/usr/lib/debug/.dwz/%s", buildroot, de->d_name);
+	if (lstat(tmp, &sbuf) == -1 || !S_ISREG(sbuf.st_mode))
+	    continue;
+	if (getELFBuildId(tmp, &build_id, &build_id_size) == -1)
+	    continue;
+	snprintf(tmp, 1024, "/usr/lib/debug/.build-id/%02x/", build_id[0]);
+	for (i = 1; i < build_id_size; ++i)
+	    sprintf(tmp + strlen(tmp), "%02x", build_id[i]);
+	sprintf(tmp + strlen(tmp), ".debug");
+	argvAdd(filesp, tmp);
+        if (!dwz_dbg_buildid) {
+	    for (i = 0; i < build_id_size; ++i)
+	        sprintf(tmp + 2 * i, "%02x", build_id[i]);
+	    dwz_dbg_buildid = xstrdup(tmp);
+	}
+	free(build_id);
+    }
+    closedir(d);
+    return dwz_dbg_buildid;
+}
+
+#endif
+
 rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
 			int installSpecialDoc, int test)
 {
     Package pkg;
     rpmRC rc = RPMRC_OK;
+    char *buildroot;
+    Package first_dbg = NULL, dwz_dbg = NULL;
+    int processing_dbg = 0;
+    int main_pkg_got_dbg = 0;
+    char *dwz_dbg_buildid = NULL;
     
     check_fileList = newStringBuf();
+    buildroot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL);
     genSourceRpmName(spec);
     
     for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
@@ -2179,8 +2472,40 @@ rpmRC processBinaryFiles(rpmSpec spec, r
 	rpmlog(RPMLOG_NOTICE, _("Processing files: %s\n"), nvr);
 	free(nvr);
 		   
-	if ((rc = processPackageFiles(spec, pkgFlags, pkg, installSpecialDoc, test)) != RPMRC_OK ||
-	    (rc = rpmfcGenerateDepends(spec, pkg)) != RPMRC_OK)
+#if HAVE_GELF_H && HAVE_LIBELF
+	if (pkg == first_dbg) {
+	    /* If we have multiple debug packages then we put
+	       DWZ generated files into %name-debuginfo which
+	       may already exist.  Otherwise put the DWZ data
+	       into the only debug package.  */
+	    processing_dbg = 1;
+	    if (!first_dbg->next || main_pkg_got_dbg) {
+		dwz_dbg_buildid = addDebugDWZ(&first_dbg->fileList, buildroot);
+		dwz_dbg = pkg;
+	    } else {
+		ARGV_t files = NULL;
+		dwz_dbg_buildid = addDebugDWZ(&files, buildroot);
+		dwz_dbg = addDebuginfoPackage(spec, spec->packages, files);
+	    }
+	}
+#endif
+	if ((rc = processPackageFiles(spec, pkgFlags, pkg, installSpecialDoc, test)) != RPMRC_OK)
+	    goto exit;
+#if HAVE_GELF_H && HAVE_LIBELF
+	if (!processing_dbg) {
+	    Package dbg = processDebuginfo(spec, pkg, buildroot);
+	    if (dbg && !first_dbg) {
+		first_dbg = dbg;
+		if (pkg == spec->packages)
+		    main_pkg_got_dbg = 1;
+	    }
+	}
+	/* If we have DWZ info and it is not in PKG then add a requires.  */
+	if (dwz_dbg_buildid && pkg != dwz_dbg)
+	  addReqProv(pkg, RPMTAG_REQUIRENAME,
+		     "debuginfo(build-id)", dwz_dbg_buildid, RPMSENSE_EQUAL, 0);
+#endif
+	if ((rc = rpmfcGenerateDepends(spec, pkg)) != RPMRC_OK)
 	    goto exit;
 
 	a = headerGetString(pkg->header, RPMTAG_ARCH);
@@ -2215,6 +2540,7 @@ rpmRC processBinaryFiles(rpmSpec spec, r
     }
 exit:
     check_fileList = freeStringBuf(check_fileList);
+    _free(dwz_dbg_buildid);
     
     return rc;
 }
--- ./build/parseSpec.c.orig	2017-03-22 12:10:11.304029953 +0000
+++ ./build/parseSpec.c	2017-03-22 12:10:20.142010341 +0000
@@ -564,7 +564,7 @@ static void initSourceHeader(rpmSpec spe
 }
 
 /* Add extra provides to package.  */
-static void addPackageProvides(Package pkg)
+void addPackageProvides(Package pkg)
 {
     const char *arch, *name;
     char *evr, *isaprov;
--- ./build/rpmbuild_internal.h.orig	2017-02-16 09:40:09.788649545 +0000
+++ ./build/rpmbuild_internal.h	2017-03-22 12:10:20.143010339 +0000
@@ -442,6 +442,13 @@ int addReqProv(Package pkg, rpmTagVal ta
 
 
 /** \ingroup rpmbuild
+ * Add self-provides to package.
+ * @param pkg          package
+ */
+RPM_GNUC_INTERNAL
+void addPackageProvides(Package pkg);
+
+/** \ingroup rpmbuild
  * Add rpmlib feature dependency.
  * @param pkg		package
  * @param feature	rpm feature name (i.e. "rpmlib(Foo)" for feature Foo)
--- ./macros.in.orig	2017-03-22 12:10:11.307029946 +0000
+++ ./macros.in	2017-03-22 12:10:20.143010339 +0000
@@ -186,24 +186,10 @@
 #	Template for debug information sub-package.
 %debug_package \
 %global __debug_package 1\
-%package debuginfo\
-Summary: Debug information for package %{name}\
-Group: Development/Debug\
-AutoReq: 0\
-AutoProv: 1\
-#Requires: %{?!debug_package_requires:%{name} = %{version}-%{release}}%{?debug_package_requires}\
-%description debuginfo\
-This package provides debug information for package %{name}.\
-Debug information is useful when developing applications that use this\
-package or when debugging this package.\
-%files debuginfo -f debugfiles.list\
-%defattr(-,root,root)\
-\
 %package debugsource\
 Summary: Debug sources for package %{name}\
 Group: Development/Debug\
 AutoReqProv: 0\
-Requires: %{name}-debuginfo = %{version}-%{release}\
 %description debugsource\
 This package provides debug sources for package %{name}.\
 Debug sources are useful when developing applications that use this\
--- ./scripts/find-debuginfo.sh.orig	2017-03-22 12:10:11.303029955 +0000
+++ ./scripts/find-debuginfo.sh	2017-03-22 12:10:20.144010337 +0000
@@ -220,6 +220,11 @@ debug_link()
 # Provide .2, .3, ... symlinks to all filename instances of this build-id.
 make_id_dup_link()
 {
+  # See https://bugzilla.redhat.com/show_bug.cgi?id=641377 for the reasoning, 
+  # but it has seveal drawbacks as we would need to split the .1 suffixes into 
+  # different subpackages and it's about impossible to predict the number
+  # -> perhaps later
+  return
   local id="$1" file="$2" idfile
 
   local n=1
@@ -424,19 +429,11 @@ if $run_dwz && type dwz >/dev/null 2>&1
   fi
 fi
 
-# For each symlink whose target has a .debug file,
-# make a .debug symlink to that file.
-find "$RPM_BUILD_ROOT" ! -path "${debugdir}/*" -type l -print |
-while read f
-do
-  t=$(readlink -m "$f").debug
-  f=${f#$RPM_BUILD_ROOT}
-  t=${t#$RPM_BUILD_ROOT}
-  if [ -f "$debugdir$t" ]; then
-    echo "symlinked /usr/lib/debug$t to /usr/lib/debug${f}.debug"
-    debug_link "/usr/lib/debug$t" "${f}.debug"
-  fi
-done
+# We used to make a .debug symlink for each symlink whose target
+# has a .debug file to that file.  This is not necessary because
+# the debuglink section contains only the destination of those links.
+# Creating those links anyway results in debuginfo packages for
+# devel packages just because of the .so symlinks in them.
 
 if [ -s "$SOURCEFILE" ]; then
   mkdir -p "${RPM_BUILD_ROOT}/usr/src/debug"