Bug#1117999: dpkg-buildflags: enable ELF package note metadata

Benjamin Drung <[email protected]>
Newsgroups gmane.linux.debian.devel.dpkg.bugs
Message-ID <d060d3c5eed6b1ae41086190a37ab2a9f0ce9e28.camel__26016.3144137309$1763489730$gmane$org@debian.org>
On Mon, 13 Oct 2025 15:53:16 +0200 Benjamin Drung <[email protected]>
wrote:
> Package: dpkg
> Version: 1.22.21
> Severity: normal
> Tags: patch
> X-Debbugs-Cc: [email protected]
> 
> Dear Maintainer,
> 
> as discussed and agreed on at DebConf 25, please add ELF package note
> metadata following the
>
https://systemd.io/PACKAGE_METADATA_FOR_EXECUTABLE_FILES/ specification.

> 
> A tested patch is attached.

Here is an updated patch with following changes:

* fix extra comma when DEB_BUILD_DEBUG_INFO_URL is set
* fix scripts/t/mk.t test case (i.e. make debian/changelog accessible)

-- 
Benjamin Drung
Debian & Ubuntu Developer
0001-dpkg-buildflags-enable-ELF-package-note-metadata.patch (text/x-patch, 6.1 KB)
From 322086638d888fc604fe29db86fa196d7deda70c Mon Sep 17 00:00:00 2001
From: Benjamin Drung <[email protected]>
Date: Tue, 18 Nov 2025 19:01:53 +0100
Subject: [PATCH] dpkg-buildflags: enable ELF package note metadata

Following the https://systemd.io/ELF_PACKAGE_METADATA/ add a new qa
flag, enable by default, that adds this package metadata ELF note to
every binary being built, to ease identification, especially in the
case of a coredump.

Since the ELF package note metadata changes the build ID, include only
"static" data and exclude the package version (as agreed at DebConf 25).

Closes: #1117999
LP: #2071468
---
 man/dpkg-buildflags.pod       |  8 +++++
 scripts/Dpkg/Vendor/Debian.pm | 64 +++++++++++++++++++++++++++++++++++
 scripts/t/Dpkg_BuildFlags.t   | 12 ++++++-
 scripts/t/mk.t                |  4 ++-
 4 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/man/dpkg-buildflags.pod b/man/dpkg-buildflags.pod
index 7a759eb7a..33be2555b 100644
--- a/man/dpkg-buildflags.pod
+++ b/man/dpkg-buildflags.pod
@@ -545,6 +545,14 @@ B<OBJCFLAGS>, B<CXXFLAGS> and B<OBJCXXFLAGS> with flags set
 to B<-D__DEB_CANARY_>I<flag>_I<random-id>B<__>, and
 B<LDFLAGS> set to B<-Wl,-z,deb-canary->I<random-id>.
 
+=item B<elfpackagemetadata>
+
+This setting (since dpkg 1.23.0; enabled by default) adds an ELF
+note containing package metadata to every ELF binary being built. If the
+$DEB_BUILD_DEBUG_INFO_URL environment variable is defined, it will be
+used to include the value as a debuginfod URL. This follows the specification
+defined at: https://systemd.io/ELF_PACKAGE_METADATA/
+
 =back
 
 =head2 optimize
diff --git a/scripts/Dpkg/Vendor/Debian.pm b/scripts/Dpkg/Vendor/Debian.pm
index f7272ad61..a002d7a9f 100644
--- a/scripts/Dpkg/Vendor/Debian.pm
+++ b/scripts/Dpkg/Vendor/Debian.pm
@@ -136,6 +136,7 @@ sub set_build_features {
             bug => undef,
             'bug-implicit-func' => undef,
             canary => 0,
+            elfpackagemetadata => 1,
         },
         reproducible => {
             timeless => 1,
@@ -516,6 +517,10 @@ sub add_build_flags {
         $flags->append('LDFLAGS', "-Wl,-z,deb-canary-${id}");
     }
 
+    if ($flags->use_feature('qa', 'elfpackagemetadata')) {
+        add_elf_package_metadata($flags);
+    }
+
     ## Area: reproducible
 
     # Warn when the __TIME__, __DATE__ and __TIMESTAMP__ macros are used.
@@ -681,6 +686,65 @@ sub add_build_flags {
     }
 }
 
+sub add_elf_package_metadata {
+    my ($flags) = @_;
+    my $arch;
+    my $pkgsrc;
+
+    if (!defined $ENV{DEB_HOST_ARCH}) {
+        require Dpkg::Arch;
+        $arch = Dpkg::Arch::get_host_arch();
+    } else {
+        $arch = $ENV{DEB_HOST_ARCH};
+    }
+
+    if (!defined $ENV{DEB_SOURCE}) {
+        if (! -r 'debian/changelog') {
+            warning(g_('debian/changelog not found. Not setting ELF package metadata parameter.'));
+            return;
+        }
+
+        require Dpkg::Changelog::Debian;
+        my $pkgchangelog = Dpkg::Changelog::Debian->new(range => { "count" => 1 });
+        $pkgchangelog->load('debian/changelog');
+        my $chgentry = @{$pkgchangelog}[0];
+        $pkgsrc = $chgentry->get_source();
+    } else {
+        $pkgsrc = $ENV{DEB_SOURCE};
+    }
+
+    if (! -f '/usr/lib/os-release') {
+        warning(g_('/usr/lib/os-release not found. Not setting ELF package metadata parameter.'));
+        return;
+    }
+    my $os_id = '';
+    open my $os_release, '<', '/usr/lib/os-release'
+        or syserr(g_('failed to read /usr/lib/os-release'));
+    while (<$os_release>) {
+        chomp;
+        # Ensure any quotes are removed, as we don't want to embed them in JSON
+        if (m/^ID=(.*)/) {
+            $os_id = $1;
+            $os_id =~ tr/"//d;
+        }
+    }
+    close $os_release or syserr(g_('failed to close /usr/lib/os-release'));
+    if ($os_id eq '') {
+        warning(g_('/usr/lib/os-release does not set ID. Not setting ELF package metadata parameter.'));
+        return;
+    }
+
+    # Per https://systemd.io/ELF_PACKAGE_METADATA/
+    my $package_metadata = "{%22type%22:%22deb%22%2C%22os%22:%22$os_id%22%2C%22name%22:%22$pkgsrc%22%2C%22architecture%22:%22$arch%22";
+    # The debuginfod URL is optional, skip it if not available.
+    if ($ENV{DEB_BUILD_DEBUG_INFO_URL}) {
+        $package_metadata .= "%2C%22debugInfoUrl%22:%22$ENV{DEB_BUILD_DEBUG_INFO_URL}%22";
+    }
+    $package_metadata .= "}";
+    $flags->append('LDFLAGS', "-Wl,--package-metadata=$package_metadata");
+    return;
+}
+
 sub _build_tainted_by {
     my $self = shift;
     my %tainted;
diff --git a/scripts/t/Dpkg_BuildFlags.t b/scripts/t/Dpkg_BuildFlags.t
index 011a50717..07d8a15d2 100644
--- a/scripts/t/Dpkg_BuildFlags.t
+++ b/scripts/t/Dpkg_BuildFlags.t
@@ -15,7 +15,7 @@
 
 use v5.36;
 
-use Test::More tests => 118;
+use Test::More tests => 119;
 
 BEGIN {
     $ENV{DEB_BUILD_ARCH} = 'amd64';
@@ -121,6 +121,7 @@ my %known_features = (
         bug
         bug-implicit-func
         canary
+        elfpackagemetadata
     ) ],
     reproducible => [ qw(
         fixdebugpath
@@ -316,4 +317,13 @@ test_has_noflag($bf, 'CPPFLAGS', '-D_TIME_BITS=64');
 test_has_noflag($bf, 'CPPFLAGS', '-U_TIME_BITS');
 test_has_flag($bf, 'CFLAGS', '-Werror=implicit-function-declaration');
 
+# ELF package metadata
+$bf = Dpkg::BuildFlags->new();
+my %qa_features = $bf->get_features('qa');
+if ($qa_features{'elfpackagemetadata'} && -r 'debian/changelog') {
+    test_has_flag($bf, 'LDFLAGS', '-Wl,--package-metadata=\{*');
+} else {
+    test_has_noflag($bf, 'LDFLAGS', '-Wl,--package-metadata*');
+}
+
 # TODO: Add more test cases.
diff --git a/scripts/t/mk.t b/scripts/t/mk.t
index 811b7be7e..d77a5adba 100644
--- a/scripts/t/mk.t
+++ b/scripts/t/mk.t
@@ -26,7 +26,7 @@ use Dpkg::IPC;
 use Dpkg::Vendor;
 
 my $srcdir = rel2abs($ENV{srcdir} || '.');
-my $datadir = test_get_data_path();
+my $datadir = rel2abs(test_get_data_path());
 
 # Turn these into absolute names so that we can safely switch to the test
 # directory with «make -C».
@@ -74,6 +74,8 @@ sub cmd_get_vars {
 
 # Test makefiles.
 
+chdir($ENV{DEB_BUILD_PATH});
+
 my %arch = cmd_get_vars($ENV{PERL}, "$srcdir/dpkg-architecture.pl", '-f');
 
 while (my ($k, $v) = each %arch) {
-- 
2.51.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.