<div class="gmail_quote">On Thu, Jul 23, 2009 at 11:39 AM, Richard Guenther <span dir="ltr">&lt;<a href="mailto:rguenther@suse.de">rguenther@suse.de</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
<br>
As part of the 4th Hackweek at SUSE I sat down and finally made<br>
debuginfo packages per sub-package possible.  Currently for openSUSE<br>
we have one global %name-debuginfo and one %name-debugsource package.<br>
%name-debuginfo contains debug information extracted from the binaries<br>
and stored in the usual /usr/lib/debug shadow tree, including<br>
GNU build-id symlinks pointing back to it from the<br>
/usr/lib/debug/.build-id tree.<br>
<br>
One issue with that single debuginfo package is its size.  The goal<br>
was to at least have one debuginfo package per shared library package.<br>
<br>
The following patch ontop of the openSUSE Factory rpm package source<br>
(yeah, I know ...) implements that by leaving the debuginfo package<br>
creation completely to RPM (while the debuginfo extraction is still<br>
done with the find-debuginfo.sh helper in the post install hook).<br>
<br>
processBinaryFiles was deemed the correct place to do this as we<br>
need an expanded file list for a package to determine the corresponding<br>
set of debuginfo files.  They are available after processPackageFiles,<br>
so the patch appends new packages to the spec after this point.<br>
<br>
The patch works (though it isn&#39;t in production yet).  For the<br>
openSUSE rpm package the following sub-packages are now generated:<br>
<br>
popt-1.7-50.2.x86_64.rpm<br>
popt-debuginfo-1.7-50.2.x86_64.rpm<br>
popt-devel-1.7-50.2.x86_64.rpm<br>
rpm-4.4.2.3-50.2.x86_64.rpm<br>
rpm-debuginfo-4.4.2.3-50.2.x86_64.rpm<br>
rpm-debugsource-4.4.2.3-50.2.x86_64.rpm<br>
rpm-devel-4.4.2.3-50.2.x86_64.rpm<br>
rpm-devel-static-4.4.2.3-50.2.x86_64.rpm<br>
rpm-4.4.2.3-50.2.src.rpm<br>
<br>
for the openSUSE gcc43 package we create<br>
<br>
cpp43-4.3.3_20090714-2.1.i586.rpm<br>
cpp43-debuginfo-4.3.3_20090714-2.1.i586.rpm<br>
gcc43-4.3.3_20090714-2.1.i586.rpm<br>
gcc43-ada-4.3.3_20090714-2.1.i586.rpm<br>
gcc43-ada-debuginfo-4.3.3_20090714-2.1.i586.rpm<br>
gcc43-c++-4.3.3_20090714-2.1.i586.rpm<br>
gcc43-c++-debuginfo-4.3.3_20090714-2.1.i586.rpm<br>
gcc43-debuginfo-4.3.3_20090714-2.1.i586.rpm<br>
gcc43-debugsource-4.3.3_20090714-2.1.i586.rpm<br>
gcc43-fortran-4.3.3_20090714-2.1.i586.rpm<br>
gcc43-fortran-debuginfo-4.3.3_20090714-2.1.i586.rpm<br>
gcc43-info-4.3.3_20090714-2.1.i586.rpm<br>
gcc43-locale-4.3.3_20090714-2.1.i586.rpm<br>
gcc43-obj-c++-4.3.3_20090714-2.1.i586.rpm<br>
gcc43-obj-c++-debuginfo-4.3.3_20090714-2.1.i586.rpm<br>
gcc43-objc-4.3.3_20090714-2.1.i586.rpm<br>
gcc43-objc-debuginfo-4.3.3_20090714-2.1.i586.rpm<br>
libada43-4.3.3_20090714-2.1.i586.rpm<br>
libada43-debuginfo-4.3.3_20090714-2.1.i586.rpm<br>
libgcc43-4.3.3_20090714-2.1.i586.rpm<br>
libgcc43-debuginfo-4.3.3_20090714-2.1.i586.rpm<br>
libgfortran43-4.3.3_20090714-2.1.i586.rpm<br>
libgfortran43-debuginfo-4.3.3_20090714-2.1.i586.rpm<br>
libgomp43-4.3.3_20090714-2.1.i586.rpm<br>
libgomp43-debuginfo-4.3.3_20090714-2.1.i586.rpm<br>
libobjc43-4.3.3_20090714-2.1.i586.rpm<br>
libobjc43-debuginfo-4.3.3_20090714-2.1.i586.rpm<br>
libstdc++43-4.3.3_20090714-2.1.i586.rpm<br>
libstdc++43-debuginfo-4.3.3_20090714-2.1.i586.rpm<br>
libstdc++43-devel-4.3.3_20090714-2.1.i586.rpm<br>
libstdc++43-doc-4.3.3_20090714-2.1.i586.rpm<br>
libgcc43-debuginfo-x86-4.3.3_20090714-2.1.ia64.rpm<br>
libgcc43-x86-4.3.3_20090714-2.1.ia64.rpm<br>
libstdc++43-debuginfo-x86-4.3.3_20090714-2.1.ia64.rpm<br>
libstdc++43-x86-4.3.3_20090714-2.1.ia64.rpm<br>
gcc43-4.3.3_20090714-2.1.src.rpm<br>
<br>
note the chance to install<br>
libstdc++43-debuginfo-4.3.3_20090714-2.1.i586.rpm without also<br>
getting debug information for the Ada frontend for example.<br>
<br>
If there is interest in this feature and the approach I took is<br>
deemed viable I can work to move this patch to the current rpm git head.<br>
<br>
Thanks for comments (please CC me, I am not subscribed to this list),<br>
Richard.<br>
<br>
--<br>
Richard Guenther &lt;<a href="mailto:rguenther@suse.de">rguenther@suse.de</a>&gt;<br>
Novell / SUSE Labs<br>
SUSE LINUX Products GmbH - Nuernberg - AG Nuernberg - HRB 16746 - GF: Markus Rex<br>
<br>
<br>
Index: rpm-4.4.2.3/build/files.c<br>
===================================================================<br>
--- rpm-4.4.2.3.orig/build/files.c      2009-07-22 14:07:58.000000000 +0200<br>
+++ rpm-4.4.2.3/build/files.c   2009-07-23 11:21:14.000000000 +0200<br>
@@ -28,6 +28,10 @@<br>
 #define        _RPMTE_INTERNAL<br>
 #include &quot;rpmte.h&quot;<br>
<br>
+#if HAVE_GELF_H<br>
+#include &lt;gelf.h&gt;<br>
+#endif<br>
+<br>
 #include &quot;buildio.h&quot;<br>
<br>
 #include &quot;legacy.h&quot;    /* XXX domd5, expandFileList, compressFileList */<br>
@@ -2485,6 +2489,129 @@ exit:<br>
     return rc;<br>
 }<br>
<br>
+<br>
+/* Query the build-id from the ELF file NAME and store it in the newly<br>
+   allocated *build_id array of size *build_id_size.  Returns -1 on<br>
+   error.  */<br>
+<br>
+int<br>
+getELFBuildId (const char *name,<br>
+              unsigned char **id, size_t *id_size)<br>
+{<br>
+  int fd, i;<br>
+  Elf *elf;<br>
+  GElf_Ehdr ehdr;<br>
+  Elf_Data *build_id = NULL;<br>
+  size_t build_id_offset = 0, build_id_size = 0;<br>
+<br>
+  /* Now query the build-id of the file and add the<br>
+     corresponding links in the .build-id tree.<br>
+     The following code is based on tools/debugedit.c.  */<br>
+  fd = open (name, O_RDONLY);<br>
+  if (fd &lt; 0)<br>
+    return -1;<br>
+  elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);<br>
+  if (elf == NULL)<br>
+    {<br>
+      fprintf (stderr, &quot;cannot open ELF file: %s&quot;,<br>
+              elf_errmsg (-1));<br>
+      close (fd);<br>
+      return -1;<br>
+    }<br>
+  if (elf_kind (elf) != ELF_K_ELF<br>
+      || gelf_getehdr (elf, &amp;ehdr) == NULL<br>
+      || (ehdr.e_type != ET_DYN<br>
+         &amp;&amp; ehdr.e_type != ET_EXEC<br>
+         &amp;&amp; ehdr.e_type != ET_REL))<br>
+    {<br>
+      elf_end (elf);<br>
+      close (fd);<br>
+      return -1;<br>
+    }<br>
+  for (i = 0; i &lt; ehdr.e_shnum; ++i)<br>
+    {<br>
+      Elf_Scn *s = elf_getscn (elf, i);<br>
+      GElf_Shdr shdr;<br>
+      Elf_Data *data;<br>
+      Elf32_Nhdr nh;<br>
+      Elf_Data dst =<br>
+       {<br>
+         .d_version = EV_CURRENT, .d_type = ELF_T_NHDR,<br>
+         .d_buf = &amp;nh, .d_size = sizeof nh<br>
+       };<br>
+      Elf_Data src = dst;<br>
+<br>
+      gelf_getshdr (s, &amp;shdr);<br>
+      if (shdr.sh_type != SHT_NOTE<br>
+         || !(shdr.sh_flags &amp; SHF_ALLOC))<br>
+       continue;<br>
+<br>
+      /* Look for a build-ID note here.  */<br>
+      data = elf_rawdata (s, NULL);<br>
+      src.d_buf = data-&gt;d_buf;<br>
+      assert (sizeof (Elf32_Nhdr) == sizeof (Elf64_Nhdr));<br>
+      while (data-&gt;d_buf + data-&gt;d_size - src.d_buf &gt; (int) sizeof nh<br>
+            &amp;&amp; elf32_xlatetom (&amp;dst, &amp;src, ehdr.e_ident[EI_DATA]))<br>
+       {<br>
+         Elf32_Word len = sizeof nh + nh.n_namesz;<br>
+         len = (len + 3) &amp; ~3;<br>
+<br>
+         if (nh.n_namesz == sizeof &quot;GNU&quot; &amp;&amp; nh.n_type == 3<br>
+             &amp;&amp; !memcmp (src.d_buf + sizeof nh, &quot;GNU&quot;, sizeof &quot;GNU&quot;))<br>
+           {<br>
+             build_id = data;<br>
+             build_id_offset = src.d_buf + len - data-&gt;d_buf;<br>
+             build_id_size = nh.n_descsz;<br>
+             break;<br>
+           }<br>
+<br>
+         len += nh.n_descsz;<br>
+         len = (len + 3) &amp; ~3;<br>
+         src.d_buf += len;<br>
+       }<br>
+<br>
+      if (build_id != NULL)<br>
+       break;<br>
+    }<br>
+<br>
+  if (build_id == NULL)<br>
+    return -1;<br>
+<br>
+  *id = malloc (build_id_size);<br>
+  *id_size = build_id_size;<br>
+  memcpy (*id, build_id-&gt;d_buf + build_id_offset, build_id_size);<br>
+<br>
+  elf_end (elf);<br>
+  close (fd);<br>
+<br>
+  return 0;<br>
+}<br>
+<br>
+<br>
+static rpmTag copyTagsForDebug[] = {<br>
+    RPMTAG_EPOCH,<br>
+    RPMTAG_VERSION,<br>
+    RPMTAG_RELEASE,<br>
+    RPMTAG_LICENSE,<br>
+    RPMTAG_PACKAGER,<br>
+    RPMTAG_DISTRIBUTION,<br>
+    RPMTAG_DISTURL,<br>
+    RPMTAG_VENDOR,<br>
+    RPMTAG_ICON,<br>
+    RPMTAG_URL,<br>
+    RPMTAG_CHANGELOGTIME,<br>
+    RPMTAG_CHANGELOGNAME,<br>
+    RPMTAG_CHANGELOGTEXT,<br>
+    RPMTAG_PREFIXES,<br>
+    RPMTAG_RHNPLATFORM,<br>
+    RPMTAG_OS,<br>
+    RPMTAG_DISTTAG,<br>
+    RPMTAG_CVSID,<br>
+    RPMTAG_ARCH,<br>
+    RPMTAG_SUSEBUILDCNT,</blockquote><div>SIA. But RPMTAG_SUSEBUILDCNT is a local - e.g. non upstream - SUSE rpm tag.<br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
<br>
+    0<br>
+};<br>
+<br>
 /*@-incondefs@*/<br>
 int processBinaryFiles(Spec spec, int installSpecialDoc, int test)<br>
        /*@globals check_fileList @*/<br>
@@ -2498,6 +2625,8 @@ int processBinaryFiles(Spec spec, int in<br>
     for (pkg = spec-&gt;packages; pkg != NULL; pkg = pkg-&gt;next) {<br>
        const char *n, *v, *r;<br>
        int rc;<br>
+       int type, count;<br>
+       char *ap;<br>
<br>
        if (pkg-&gt;fileList == NULL)<br>
            continue;<br>
@@ -2508,6 +2637,106 @@ int processBinaryFiles(Spec spec, int in<br>
        if ((rc = processPackageFiles(spec, pkg, installSpecialDoc, test)))<br>
            res = rc;<br>
<br>
+       /* BEGIN DEBUGPKG */<br>
+#if HAVE_GELF_H &amp;&amp; HAVE_LIBELF<br>
+       elf_version(EV_CURRENT);<br>
+<br>
+       /* Now we have the file list of pkg in pkg-&gt;cpioList.  Iterate over<br>
+          them and build a file list containing debug information for them.  */<br>
+       if (headerGetEntry (pkg-&gt;header, RPMTAG_ARCH, &amp;type, (void **)&amp;ap, &amp;count)<br>
+           &amp;&amp; type == RPM_STRING_TYPE &amp;&amp; count == 1<br>
+           &amp;&amp; strcmp (ap, &quot;noarch&quot;) != 0<br>
+           &amp;&amp; strcmp (ap, &quot;src&quot;) != 0)<br>
+         {<br>
+           Package dbg;<br>
+           rpmfi fi = pkg-&gt;cpioList;<br>
+           char tmp[1024];<br>
+           const char *name;<br>
+           StringBuf files = NULL;<br>
+<br>
+           /* Check if the current package has files with debug info<br>
+              and record them.  */<br>
+           fi = rpmfiInit (fi, 0);<br>
+           while (rpmfiNext (fi) &gt;= 0)<br>
+             {<br>
+               const char *base;<br>
+               int i;<br>
+               unsigned char *build_id;<br>
+               size_t build_id_size = 0;<br>
+               struct stat sbuf;<br>
+<br>
+               name = rpmfiFN (fi);<br>
+               /* Skip leading buildroot.  */<br>
+               base = name + strlen (spec-&gt;buildRootURL);<br>
+               /* Pre-pend %buildroot/usr/lib/debug and append .debug.  */<br>
+               snprintf (tmp, 1024, &quot;%s/usr/lib/debug%s.debug&quot;,<br>
+                         spec-&gt;buildRootURL, base);<br>
+               /* If that file exists we have debug information for it.  */<br>
+               if (access (tmp, F_OK) != 0)<br>
+                 continue;<br>
+<br>
+               /* Append the file list preamble.  */<br>
+               if (!files)<br>
+                 {<br>
+                   files = newStringBuf();<br>
+                   appendStringBuf(files, &quot;%defattr(-,root,root)\n&quot;);<br>
+                   appendStringBuf(files, &quot;%dir /usr/lib/debug\n&quot;);<br>
+                   appendStringBuf(files, &quot;%dir /usr/lib/debug/.build-id\n&quot;);<br>
+                 }<br>
+               /* Add the files main debug-info file.  */<br>
+               snprintf (tmp, 1024, &quot;/usr/lib/debug/%s.debug\n&quot;, base);<br>
+               appendStringBuf(files, tmp);<br>
+<br>
+               /* Do not bother to check build-ids for symbolic links.<br>
+                  We&#39;ll handle them for the link target.  */<br>
+               if (lstat (name, &amp;sbuf) == -1<br>
+                   || S_ISLNK (sbuf.st_mode))<br>
+                 continue;<br>
+<br>
+               /* Try to gather the build-id from the binary.  */<br>
+               if (getELFBuildId (name, &amp;build_id, &amp;build_id_size) == -1)<br>
+                 continue;<br>
+<br>
+               /* From the build-id construct the two links pointing back<br>
+                  to the debug information file and the binary.  */<br>
+               snprintf (tmp, 1024, &quot;/usr/lib/debug/.build-id/%02x/&quot;,<br>
+                         build_id[0]);<br>
+               for (i = 1; i &lt; build_id_size; ++i)<br>
+                 sprintf (tmp + strlen (tmp), &quot;%02x&quot;, build_id[i]);<br>
+               appendStringBuf(files, tmp);<br>
+               appendStringBuf(files, &quot;\n&quot;);<br>
+               appendStringBuf(files, tmp);<br>
+               appendStringBuf(files, &quot;.debug\n&quot;);<br>
+<br>
+               free (build_id);<br>
+             }<br>
+<br>
+           /* If there are debuginfo files for this package add a<br>
+              new debuginfo package.  */<br>
+           if (files)<br>
+             {<br>
+               dbg = newPackage (spec);<br>
+               headerNVR (pkg-&gt;header, &amp;name, NULL, NULL);<br>
+               /* Set name, summary and group.  */<br>
+               snprintf (tmp, 1024, &quot;%s-debuginfo&quot;, name);<br>
+               headerAddEntry (dbg-&gt;header, RPMTAG_NAME, RPM_STRING_TYPE, tmp, 1);<br>
+               snprintf (tmp, 1024, &quot;Debug information for package %s&quot;, name);<br>
+               headerAddEntry (dbg-&gt;header, RPMTAG_SUMMARY, RPM_STRING_TYPE,<br>
+                               tmp, 1);<br>
+               headerAddEntry (dbg-&gt;header, RPMTAG_GROUP, RPM_STRING_TYPE,<br>
+                               &quot;Development/Debug&quot;, 1);<br>
+               /* Inherit other tags from parent.  */<br>
+               headerCopyTags (pkg-&gt;header, dbg-&gt;header,<br>
+                               (int_32 *)copyTagsForDebug);<br>
+<br>
+               /* Build up the files list.  */<br>
+               dbg-&gt;fileList = files;<br>
+             }<br>
+         }<br>
+<br>
+       /* END DEBUGPKG */<br>
+#endif<br>
+<br>
        if ((rc = rpmfcGenerateDepends(spec, pkg)))<br>
            res = rc;<br>
     }<br>
Index: rpm-4.4.2.3/<a href="http://macros.in" target="_blank">macros.in</a><br>
===================================================================<br>
--- rpm-4.4.2.3.orig/<a href="http://macros.in" target="_blank">macros.in</a>  2009-07-22 14:07:58.000000000 +0200<br>
+++ rpm-4.4.2.3/<a href="http://macros.in" target="_blank">macros.in</a>       2009-07-22 14:08:05.000000000 +0200<br>
@@ -173,19 +173,6 @@<br>
 #      Template for debug information sub-package.<br>
 %debug_package \<br>
 %global __debug_package 1\<br>
-%package debuginfo\<br>
-Summary: Debug information for package %{name}\<br>
-Group: Development/Debug\<br>
-AutoReq: 0\<br>
-AutoProv: 1\<br>
-#Requires: %{?!debug_package_requires:%{name} = %{version}-%{release}}%{?debug_package_requires}\<br>
-%description debuginfo\<br>
-This package provides debug information for package %{name}.\<br>
-Debug information is useful when developing applications that use this\<br>
-package or when debugging this package.\<br>
-%files debuginfo -f debugfiles.list\<br>
-%defattr(-,root,root)\<br>
-\<br>
 %package debugsource\<br>
 Summary: Debug sources for package %{name}\<br>
 Group: Development/Debug\<br>
Index: rpm-4.4.2.3/scripts/find-debuginfo.sh<br>
===================================================================<br>
--- rpm-4.4.2.3.orig/scripts/find-debuginfo.sh  2009-07-22 14:07:58.000000000 +0200<br>
+++ rpm-4.4.2.3/scripts/find-debuginfo.sh       2009-07-23 11:24:47.000000000 +0200<br>
@@ -274,19 +274,11 @@ while read nlinks inum f; do<br>
   fi<br>
 done || exit<br>
<br>
-# For each symlink whose target has a .debug file,<br>
-# make a .debug symlink to that file.<br>
-find $RPM_BUILD_ROOT ! -path &quot;${debugdir}/*&quot; -type l -print |<br>
-while read f<br>
-do<br>
-  t=$(readlink -m &quot;$f&quot;).debug<br>
-  f=${f#$RPM_BUILD_ROOT}<br>
-  t=${t#$RPM_BUILD_ROOT}<br>
-  if [ -f &quot;$debugdir$t&quot; ]; then<br>
-    echo &quot;symlinked /usr/lib/debug$t to /usr/lib/debug${f}.debug&quot;<br>
-    debug_link &quot;/usr/lib/debug$t&quot; &quot;${f}.debug&quot;<br>
-  fi<br>
-done<br>
+# We used to make a .debug symlink for each symlink whose target<br>
+# has a .debug file to that file.  This is not necessary because<br>
+# the debuglink section contains only the destination of those links.<br>
+# Creating those links anyway results in debuginfo packages for<br>
+# devel packages just because of the .so symlinks in them.<br>
<br>
 if [ -s &quot;$SOURCEFILE&quot; ]; then<br>
   mkdir -p &quot;${RPM_BUILD_ROOT}/usr/src/debug&quot;<br>
_______________________________________________<br>
Rpm-maint mailing list<br>
<a href="mailto:Rpm-maint@lists.rpm.org">Rpm-maint@lists.rpm.org</a><br>
<a href="http://lists.rpm.org/mailman/listinfo/rpm-maint" target="_blank">http://lists.rpm.org/mailman/listinfo/rpm-maint</a><br>
</blockquote></div><br>