[Rpm-maint] [PATCH] Generate -debuginfo packages from /usr/lib/debug automatically
Richard Guenther
rguenther at suse.de
Thu Jul 23 09:39:49 UTC 2009
As part of the 4th Hackweek at SUSE I sat down and finally made
debuginfo packages per sub-package possible. Currently for openSUSE
we have one global %name-debuginfo and one %name-debugsource package.
%name-debuginfo contains debug information extracted from the binaries
and stored in the usual /usr/lib/debug shadow tree, including
GNU build-id symlinks pointing back to it from the
/usr/lib/debug/.build-id tree.
One issue with that single debuginfo package is its size. The goal
was to at least have one debuginfo package per shared library package.
The following patch ontop of the openSUSE Factory rpm package source
(yeah, I know ...) implements that by leaving the debuginfo package
creation completely to RPM (while the debuginfo extraction is still
done with the find-debuginfo.sh helper in the post install hook).
processBinaryFiles was deemed the correct place to do this as we
need an expanded file list for a package to determine the corresponding
set of debuginfo files. They are available after processPackageFiles,
so the patch appends new packages to the spec after this point.
The patch works (though it isn't in production yet). For the
openSUSE rpm package the following sub-packages are now generated:
popt-1.7-50.2.x86_64.rpm
popt-debuginfo-1.7-50.2.x86_64.rpm
popt-devel-1.7-50.2.x86_64.rpm
rpm-4.4.2.3-50.2.x86_64.rpm
rpm-debuginfo-4.4.2.3-50.2.x86_64.rpm
rpm-debugsource-4.4.2.3-50.2.x86_64.rpm
rpm-devel-4.4.2.3-50.2.x86_64.rpm
rpm-devel-static-4.4.2.3-50.2.x86_64.rpm
rpm-4.4.2.3-50.2.src.rpm
for the openSUSE gcc43 package we create
cpp43-4.3.3_20090714-2.1.i586.rpm
cpp43-debuginfo-4.3.3_20090714-2.1.i586.rpm
gcc43-4.3.3_20090714-2.1.i586.rpm
gcc43-ada-4.3.3_20090714-2.1.i586.rpm
gcc43-ada-debuginfo-4.3.3_20090714-2.1.i586.rpm
gcc43-c++-4.3.3_20090714-2.1.i586.rpm
gcc43-c++-debuginfo-4.3.3_20090714-2.1.i586.rpm
gcc43-debuginfo-4.3.3_20090714-2.1.i586.rpm
gcc43-debugsource-4.3.3_20090714-2.1.i586.rpm
gcc43-fortran-4.3.3_20090714-2.1.i586.rpm
gcc43-fortran-debuginfo-4.3.3_20090714-2.1.i586.rpm
gcc43-info-4.3.3_20090714-2.1.i586.rpm
gcc43-locale-4.3.3_20090714-2.1.i586.rpm
gcc43-obj-c++-4.3.3_20090714-2.1.i586.rpm
gcc43-obj-c++-debuginfo-4.3.3_20090714-2.1.i586.rpm
gcc43-objc-4.3.3_20090714-2.1.i586.rpm
gcc43-objc-debuginfo-4.3.3_20090714-2.1.i586.rpm
libada43-4.3.3_20090714-2.1.i586.rpm
libada43-debuginfo-4.3.3_20090714-2.1.i586.rpm
libgcc43-4.3.3_20090714-2.1.i586.rpm
libgcc43-debuginfo-4.3.3_20090714-2.1.i586.rpm
libgfortran43-4.3.3_20090714-2.1.i586.rpm
libgfortran43-debuginfo-4.3.3_20090714-2.1.i586.rpm
libgomp43-4.3.3_20090714-2.1.i586.rpm
libgomp43-debuginfo-4.3.3_20090714-2.1.i586.rpm
libobjc43-4.3.3_20090714-2.1.i586.rpm
libobjc43-debuginfo-4.3.3_20090714-2.1.i586.rpm
libstdc++43-4.3.3_20090714-2.1.i586.rpm
libstdc++43-debuginfo-4.3.3_20090714-2.1.i586.rpm
libstdc++43-devel-4.3.3_20090714-2.1.i586.rpm
libstdc++43-doc-4.3.3_20090714-2.1.i586.rpm
libgcc43-debuginfo-x86-4.3.3_20090714-2.1.ia64.rpm
libgcc43-x86-4.3.3_20090714-2.1.ia64.rpm
libstdc++43-debuginfo-x86-4.3.3_20090714-2.1.ia64.rpm
libstdc++43-x86-4.3.3_20090714-2.1.ia64.rpm
gcc43-4.3.3_20090714-2.1.src.rpm
note the chance to install
libstdc++43-debuginfo-4.3.3_20090714-2.1.i586.rpm without also
getting debug information for the Ada frontend for example.
If there is interest in this feature and the approach I took is
deemed viable I can work to move this patch to the current rpm git head.
Thanks for comments (please CC me, I am not subscribed to this list),
Richard.
--
Richard Guenther <rguenther at suse.de>
Novell / SUSE Labs
SUSE LINUX Products GmbH - Nuernberg - AG Nuernberg - HRB 16746 - GF: Markus Rex
Index: rpm-4.4.2.3/build/files.c
===================================================================
--- rpm-4.4.2.3.orig/build/files.c 2009-07-22 14:07:58.000000000 +0200
+++ rpm-4.4.2.3/build/files.c 2009-07-23 11:21:14.000000000 +0200
@@ -28,6 +28,10 @@
#define _RPMTE_INTERNAL
#include "rpmte.h"
+#if HAVE_GELF_H
+#include <gelf.h>
+#endif
+
#include "buildio.h"
#include "legacy.h" /* XXX domd5, expandFileList, compressFileList */
@@ -2485,6 +2489,129 @@ exit:
return rc;
}
+
+/* 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. */
+
+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);
+ if (shdr.sh_type != SHT_NOTE
+ || !(shdr.sh_flags & SHF_ALLOC))
+ 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 (data->d_buf + data->d_size - 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 (src.d_buf + sizeof nh, "GNU", sizeof "GNU"))
+ {
+ build_id = data;
+ build_id_offset = src.d_buf + len - data->d_buf;
+ build_id_size = nh.n_descsz;
+ break;
+ }
+
+ len += nh.n_descsz;
+ len = (len + 3) & ~3;
+ 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, 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,
+ RPMTAG_SUSEBUILDCNT,
+ 0
+};
+
/*@-incondefs@*/
int processBinaryFiles(Spec spec, int installSpecialDoc, int test)
/*@globals check_fileList @*/
@@ -2498,6 +2625,8 @@ int processBinaryFiles(Spec spec, int in
for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
const char *n, *v, *r;
int rc;
+ int type, count;
+ char *ap;
if (pkg->fileList == NULL)
continue;
@@ -2508,6 +2637,106 @@ int processBinaryFiles(Spec spec, int in
if ((rc = processPackageFiles(spec, pkg, installSpecialDoc, test)))
res = rc;
+ /* BEGIN DEBUGPKG */
+#if HAVE_GELF_H && HAVE_LIBELF
+ elf_version(EV_CURRENT);
+
+ /* Now we have the file list of pkg in pkg->cpioList. Iterate over
+ them and build a file list containing debug information for them. */
+ if (headerGetEntry (pkg->header, RPMTAG_ARCH, &type, (void **)&ap, &count)
+ && type == RPM_STRING_TYPE && count == 1
+ && strcmp (ap, "noarch") != 0
+ && strcmp (ap, "src") != 0)
+ {
+ Package dbg;
+ rpmfi fi = pkg->cpioList;
+ char tmp[1024];
+ const char *name;
+ StringBuf files = NULL;
+
+ /* Check if the current package has files with debug info
+ and record them. */
+ fi = rpmfiInit (fi, 0);
+ while (rpmfiNext (fi) >= 0)
+ {
+ const char *base;
+ int i;
+ unsigned char *build_id;
+ size_t build_id_size = 0;
+ struct stat sbuf;
+
+ name = rpmfiFN (fi);
+ /* Skip leading buildroot. */
+ base = name + strlen (spec->buildRootURL);
+ /* Pre-pend %buildroot/usr/lib/debug and append .debug. */
+ snprintf (tmp, 1024, "%s/usr/lib/debug%s.debug",
+ spec->buildRootURL, base);
+ /* If that file exists we have debug information for it. */
+ if (access (tmp, F_OK) != 0)
+ continue;
+
+ /* Append the file list preamble. */
+ if (!files)
+ {
+ files = newStringBuf();
+ appendStringBuf(files, "%defattr(-,root,root)\n");
+ appendStringBuf(files, "%dir /usr/lib/debug\n");
+ appendStringBuf(files, "%dir /usr/lib/debug/.build-id\n");
+ }
+ /* Add the files main debug-info file. */
+ snprintf (tmp, 1024, "/usr/lib/debug/%s.debug\n", base);
+ appendStringBuf(files, tmp);
+
+ /* Do not bother to check build-ids for symbolic links.
+ We'll handle them for the link target. */
+ if (lstat (name, &sbuf) == -1
+ || S_ISLNK (sbuf.st_mode))
+ continue;
+
+ /* Try to gather the build-id from the binary. */
+ if (getELFBuildId (name, &build_id, &build_id_size) == -1)
+ continue;
+
+ /* 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]);
+ appendStringBuf(files, tmp);
+ appendStringBuf(files, "\n");
+ appendStringBuf(files, tmp);
+ appendStringBuf(files, ".debug\n");
+
+ free (build_id);
+ }
+
+ /* If there are debuginfo files for this package add a
+ new debuginfo package. */
+ if (files)
+ {
+ dbg = newPackage (spec);
+ headerNVR (pkg->header, &name, NULL, NULL);
+ /* Set name, summary and group. */
+ snprintf (tmp, 1024, "%s-debuginfo", name);
+ headerAddEntry (dbg->header, RPMTAG_NAME, RPM_STRING_TYPE, tmp, 1);
+ snprintf (tmp, 1024, "Debug information for package %s", name);
+ headerAddEntry (dbg->header, RPMTAG_SUMMARY, RPM_STRING_TYPE,
+ tmp, 1);
+ headerAddEntry (dbg->header, RPMTAG_GROUP, RPM_STRING_TYPE,
+ "Development/Debug", 1);
+ /* Inherit other tags from parent. */
+ headerCopyTags (pkg->header, dbg->header,
+ (int_32 *)copyTagsForDebug);
+
+ /* Build up the files list. */
+ dbg->fileList = files;
+ }
+ }
+
+ /* END DEBUGPKG */
+#endif
+
if ((rc = rpmfcGenerateDepends(spec, pkg)))
res = rc;
}
Index: rpm-4.4.2.3/macros.in
===================================================================
--- rpm-4.4.2.3.orig/macros.in 2009-07-22 14:07:58.000000000 +0200
+++ rpm-4.4.2.3/macros.in 2009-07-22 14:08:05.000000000 +0200
@@ -173,19 +173,6 @@
# 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\
Index: rpm-4.4.2.3/scripts/find-debuginfo.sh
===================================================================
--- rpm-4.4.2.3.orig/scripts/find-debuginfo.sh 2009-07-22 14:07:58.000000000 +0200
+++ rpm-4.4.2.3/scripts/find-debuginfo.sh 2009-07-23 11:24:47.000000000 +0200
@@ -274,19 +274,11 @@ while read nlinks inum f; do
fi
done || exit
-# 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"
More information about the Rpm-maint
mailing list