<div class="gmail_quote">On Thu, Jul 23, 2009 at 11:39 AM, Richard Guenther <span dir="ltr"><<a href="mailto:rguenther@suse.de">rguenther@suse.de</a>></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'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 <<a href="mailto:rguenther@suse.de">rguenther@suse.de</a>><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 "rpmte.h"<br>
<br>
+#if HAVE_GELF_H<br>
+#include <gelf.h><br>
+#endif<br>
+<br>
#include "buildio.h"<br>
<br>
#include "legacy.h" /* 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 < 0)<br>
+ return -1;<br>
+ elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);<br>
+ if (elf == NULL)<br>
+ {<br>
+ fprintf (stderr, "cannot open ELF file: %s",<br>
+ elf_errmsg (-1));<br>
+ close (fd);<br>
+ return -1;<br>
+ }<br>
+ if (elf_kind (elf) != ELF_K_ELF<br>
+ || gelf_getehdr (elf, &ehdr) == NULL<br>
+ || (ehdr.e_type != ET_DYN<br>
+ && ehdr.e_type != ET_EXEC<br>
+ && ehdr.e_type != ET_REL))<br>
+ {<br>
+ elf_end (elf);<br>
+ close (fd);<br>
+ return -1;<br>
+ }<br>
+ for (i = 0; i < 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 = &nh, .d_size = sizeof nh<br>
+ };<br>
+ Elf_Data src = dst;<br>
+<br>
+ gelf_getshdr (s, &shdr);<br>
+ if (shdr.sh_type != SHT_NOTE<br>
+ || !(shdr.sh_flags & SHF_ALLOC))<br>
+ continue;<br>
+<br>
+ /* Look for a build-ID note here. */<br>
+ data = elf_rawdata (s, NULL);<br>
+ src.d_buf = data->d_buf;<br>
+ assert (sizeof (Elf32_Nhdr) == sizeof (Elf64_Nhdr));<br>
+ while (data->d_buf + data->d_size - src.d_buf > (int) sizeof nh<br>
+ && elf32_xlatetom (&dst, &src, ehdr.e_ident[EI_DATA]))<br>
+ {<br>
+ Elf32_Word len = sizeof nh + nh.n_namesz;<br>
+ len = (len + 3) & ~3;<br>
+<br>
+ if (nh.n_namesz == sizeof "GNU" && nh.n_type == 3<br>
+ && !memcmp (src.d_buf + sizeof nh, "GNU", sizeof "GNU"))<br>
+ {<br>
+ build_id = data;<br>
+ build_id_offset = src.d_buf + len - data->d_buf;<br>
+ build_id_size = nh.n_descsz;<br>
+ break;<br>
+ }<br>
+<br>
+ len += nh.n_descsz;<br>
+ len = (len + 3) & ~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->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->packages; pkg != NULL; pkg = pkg->next) {<br>
const char *n, *v, *r;<br>
int rc;<br>
+ int type, count;<br>
+ char *ap;<br>
<br>
if (pkg->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 && HAVE_LIBELF<br>
+ elf_version(EV_CURRENT);<br>
+<br>
+ /* Now we have the file list of pkg in pkg->cpioList. Iterate over<br>
+ them and build a file list containing debug information for them. */<br>
+ if (headerGetEntry (pkg->header, RPMTAG_ARCH, &type, (void **)&ap, &count)<br>
+ && type == RPM_STRING_TYPE && count == 1<br>
+ && strcmp (ap, "noarch") != 0<br>
+ && strcmp (ap, "src") != 0)<br>
+ {<br>
+ Package dbg;<br>
+ rpmfi fi = pkg->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) >= 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->buildRootURL);<br>
+ /* Pre-pend %buildroot/usr/lib/debug and append .debug. */<br>
+ snprintf (tmp, 1024, "%s/usr/lib/debug%s.debug",<br>
+ spec->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, "%defattr(-,root,root)\n");<br>
+ appendStringBuf(files, "%dir /usr/lib/debug\n");<br>
+ appendStringBuf(files, "%dir /usr/lib/debug/.build-id\n");<br>
+ }<br>
+ /* Add the files main debug-info file. */<br>
+ snprintf (tmp, 1024, "/usr/lib/debug/%s.debug\n", base);<br>
+ appendStringBuf(files, tmp);<br>
+<br>
+ /* Do not bother to check build-ids for symbolic links.<br>
+ We'll handle them for the link target. */<br>
+ if (lstat (name, &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, &build_id, &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, "/usr/lib/debug/.build-id/%02x/",<br>
+ build_id[0]);<br>
+ for (i = 1; i < build_id_size; ++i)<br>
+ sprintf (tmp + strlen (tmp), "%02x", build_id[i]);<br>
+ appendStringBuf(files, tmp);<br>
+ appendStringBuf(files, "\n");<br>
+ appendStringBuf(files, tmp);<br>
+ appendStringBuf(files, ".debug\n");<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->header, &name, NULL, NULL);<br>
+ /* Set name, summary and group. */<br>
+ snprintf (tmp, 1024, "%s-debuginfo", name);<br>
+ headerAddEntry (dbg->header, RPMTAG_NAME, RPM_STRING_TYPE, tmp, 1);<br>
+ snprintf (tmp, 1024, "Debug information for package %s", name);<br>
+ headerAddEntry (dbg->header, RPMTAG_SUMMARY, RPM_STRING_TYPE,<br>
+ tmp, 1);<br>
+ headerAddEntry (dbg->header, RPMTAG_GROUP, RPM_STRING_TYPE,<br>
+ "Development/Debug", 1);<br>
+ /* Inherit other tags from parent. */<br>
+ headerCopyTags (pkg->header, dbg->header,<br>
+ (int_32 *)copyTagsForDebug);<br>
+<br>
+ /* Build up the files list. */<br>
+ dbg->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 "${debugdir}/*" -type l -print |<br>
-while read f<br>
-do<br>
- t=$(readlink -m "$f").debug<br>
- f=${f#$RPM_BUILD_ROOT}<br>
- t=${t#$RPM_BUILD_ROOT}<br>
- if [ -f "$debugdir$t" ]; then<br>
- echo "symlinked /usr/lib/debug$t to /usr/lib/debug${f}.debug"<br>
- debug_link "/usr/lib/debug$t" "${f}.debug"<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 "$SOURCEFILE" ]; then<br>
mkdir -p "${RPM_BUILD_ROOT}/usr/src/debug"<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>