From: Mathieu Othacehe <othacehe@gnu.org>
To: openembedded-core@lists.openembedded.org
Cc: Alexander Kanavin <alex.kanavin@gmail.com>,
Khem Raj <raj.khem@gmail.com>,
Richard Purdie <richard.purdie@linuxfoundation.org>,
Mathieu Othacehe <othacehe@gnu.org>
Subject: [PATCH 1/1] lib/oe/package: Add strip keep-section support
Date: Sat, 12 Sep 2026 00:01:38 +0200 [thread overview]
Message-ID: <20260911220138.32414-2-othacehe@gnu.org> (raw)
In-Reply-To: <20260911220138.32414-1-othacehe@gnu.org>
On 32-bit Arm, the .ARM.extab and .ARM.exidx unwinding sections do not
always provide enough information to get a full backtrace on C++
exceptions:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117941
In addition to those unwinding sections, GCC also emits unwinding
instructions in DWARF format under the .debug_frame section. By
instructing 'strip' not to remove that section, libunwind can use it
to produce a full backtrace, for example when an unhandled C++
exception is thrown.
Add a PACKAGE_KEEP_SECTIONS variable, listing space separated ELF
section names that should be kept around instead of stripped, for
example:
PACKAGE_KEEP_SECTIONS:pn-myrecipe = ".debug_frame"
This variable is never applied to kernel modules, which are covered
by CONFIG_UNWINDER_* instead.
Add a oe-selftest case building core-image-minimal for qemuarm, and
checking with readelf that busybox loses its .debug_frame section by
default, but keeps it once PACKAGE_KEEP_SECTIONS is set.
Signed-off-by: Mathieu Othacehe <othacehe@gnu.org>
---
meta/classes-global/staging.bbclass | 4 +-
meta/lib/oe/package.py | 19 +++++++--
meta/lib/oeqa/selftest/cases/package.py | 52 ++++++++++++++++++++++++-
3 files changed, 69 insertions(+), 6 deletions(-)
diff --git a/meta/classes-global/staging.bbclass b/meta/classes-global/staging.bbclass
index 5833abebc8..9ea4c644db 100644
--- a/meta/classes-global/staging.bbclass
+++ b/meta/classes-global/staging.bbclass
@@ -91,10 +91,12 @@ python sysroot_strip () {
base_libdir = d.getVar("base_libdir")
qa_already_stripped = 'already-stripped' in (d.getVar('INSANE_SKIP:' + pn) or "").split()
strip_cmd = d.getVar("STRIP")
+ keep_sections = d.getVar('PACKAGE_KEEP_SECTIONS') or ""
max_process = oe.utils.get_bb_number_threads(d)
oe.package.strip_execs(pn, dstdir, strip_cmd, libdir, base_libdir, max_process,
- qa_already_stripped=qa_already_stripped)
+ qa_already_stripped=qa_already_stripped,
+ keep_sections=keep_sections)
}
do_populate_sysroot[dirs] = "${SYSROOT_DESTDIR}"
diff --git a/meta/lib/oe/package.py b/meta/lib/oe/package.py
index b7030643d2..620f9fefc8 100644
--- a/meta/lib/oe/package.py
+++ b/meta/lib/oe/package.py
@@ -19,7 +19,7 @@ import shutil
import bb.parse
import oe.cachedpath
-def runstrip(file, elftype, strip, extra_strip_sections=''):
+def runstrip(file, elftype, strip, extra_strip_sections='', keep_sections=''):
# Function to strip a single file, called from split_and_strip_files below
# A working 'file' (one which works on the target architecture)
#
@@ -49,6 +49,10 @@ def runstrip(file, elftype, strip, extra_strip_sections=''):
for section in extra_strip_sections.split():
stripcmd.extend(["--remove-section=" + section])
+ if keep_sections != '' and not elftype & 16:
+ for section in keep_sections.split():
+ stripcmd.extend(["--keep-section=" + section])
+
stripcmd.append(file)
bb.debug(1, "runstrip: %s" % stripcmd)
@@ -96,7 +100,8 @@ def is_static_lib(path):
return start == magic
return False
-def strip_execs(pn, dstdir, strip_cmd, libdir, base_libdir, max_process, qa_already_stripped=False):
+def strip_execs(pn, dstdir, strip_cmd, libdir, base_libdir, max_process,
+ qa_already_stripped=False, keep_sections=''):
"""
Strip executable code (like executables, shared libraries) _in_place_
- Based on sysroot_strip in staging.bbclass
@@ -107,6 +112,9 @@ def strip_execs(pn, dstdir, strip_cmd, libdir, base_libdir, max_process, qa_alre
:param max_process: number of stripping processes started in parallel
:param qa_already_stripped: Set to True if already-stripped' in ${INSANE_SKIP}
This is for proper logging and messages only.
+ :param keep_sections: Space separated list of ELF sections to keep even
+ though the file is being stripped, for example ".debug_frame" so that
+ libunwind can use it to generate backtraces.
"""
import stat, errno, oe.path, oe.utils
@@ -175,7 +183,8 @@ def strip_execs(pn, dstdir, strip_cmd, libdir, base_libdir, max_process, qa_alre
elf_file = int(elffiles[file])
sfiles.append((file, elf_file, strip_cmd))
- oe.utils.multiprocess_launch_mp(runstrip, sfiles, max_process)
+ oe.utils.multiprocess_launch_mp(runstrip, sfiles, max_process,
+ extraargs=('', keep_sections))
TRANSLATE = (
("@", "@at@"),
@@ -1359,7 +1368,9 @@ def process_split_and_strip_files(d):
for f in staticlibs:
sfiles.append((f, 16, strip))
- oe.utils.multiprocess_launch(oe.package.runstrip, sfiles, d)
+ keep_sections = d.getVar('PACKAGE_KEEP_SECTIONS') or ""
+ oe.utils.multiprocess_launch(oe.package.runstrip, sfiles, d,
+ extraargs=('', keep_sections))
# Build "minidebuginfo" and reinject it back into the stripped binaries
if bb.utils.contains('DISTRO_FEATURES', 'minidebuginfo', True, False, d):
diff --git a/meta/lib/oeqa/selftest/cases/package.py b/meta/lib/oeqa/selftest/cases/package.py
index 38ed7173fe..96aee21177 100644
--- a/meta/lib/oeqa/selftest/cases/package.py
+++ b/meta/lib/oeqa/selftest/cases/package.py
@@ -5,10 +5,12 @@
#
from oeqa.selftest.case import OESelftestTestCase
-from oeqa.utils.commands import bitbake, get_bb_vars, get_bb_var, runqemu
+from oeqa.utils.commands import bitbake, get_bb_vars, get_bb_var, runqemu, runCmd
import subprocess, os
import oe.path
import re
+import tempfile
+import tarfile
class VersionOrdering(OESelftestTestCase):
# version1, version2, sort order
@@ -208,3 +210,51 @@ class PackageTests(OESelftestTestCase):
sysconfdir + "/selftest-chown/symlink",
sysconfdir + "/selftest-chown/fifotest/fifo"]:
check_ownership(qemu, "test", "test", path)
+
+class PackageKeepSections(OESelftestTestCase):
+ def test_package_keep_sections(self):
+ """
+ Verify that PACKAGE_KEEP_SECTIONS prevents 'strip' from removing the
+ listed ELF sections, and that they are removed as usual when the
+ variable isn't set.
+ """
+ # GCC only emits .debug_frame on targets that don't already rely on
+ # .eh_frame for unwinding, which in practice means 32-bit Arm: use
+ # qemuarm so the section actually exists before strip runs. Do this
+ # before querying any other variable below, as they all depend on
+ # MACHINE.
+ self.write_config("""
+MACHINE = "qemuarm"
+IMAGE_FSTYPES = "tar.bz2"
+""")
+
+ target_sys = get_bb_var("TARGET_SYS")
+ bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME', 'READELF'], 'core-image-minimal')
+ binutils = "binutils-cross-{}".format(get_bb_var("TARGET_ARCH"))
+ bitbake("{}:do_addto_recipe_sysroot".format(binutils))
+ native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", binutils)
+
+ def has_section(section):
+ with tempfile.TemporaryDirectory(prefix = "unpackfs-") as unpackedfs:
+ filename = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], "{}.tar.bz2".format(bb_vars['IMAGE_LINK_NAME']))
+ with tarfile.open(filename) as tar:
+ tar.extract("./usr/bin/busybox.nosuid", path=unpackedfs)
+
+ r = runCmd([bb_vars['READELF'], "-W", "-S", os.path.join(unpackedfs, "usr", "bin", "busybox.nosuid")],
+ native_sysroot = native_sysroot, target_sys = target_sys)
+ return section in r.output
+
+ bitbake("core-image-minimal")
+ self.assertFalse(has_section(".debug_frame"),
+ "busybox should not carry a .debug_frame section by default")
+
+ self.write_config("""
+MACHINE = "qemuarm"
+IMAGE_FSTYPES = "tar.bz2"
+PACKAGE_KEEP_SECTIONS:pn-busybox = ".debug_frame"
+""")
+ bitbake("busybox -c package -f")
+ bitbake("core-image-minimal")
+ self.assertTrue(has_section(".debug_frame"),
+ "busybox should carry a .debug_frame section when "
+ "PACKAGE_KEEP_SECTIONS is set")
--
2.34.1
next prev parent reply other threads:[~2026-09-11 22:02 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-11 22:01 [PATCH v3 0/1] lib/oe/package: Add strip keep-section support Mathieu Othacehe
2026-09-11 22:01 ` Mathieu Othacehe [this message]
2026-09-12 12:18 ` [OE-core] [PATCH 1/1] " Mathieu Dubois-Briand
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260911220138.32414-2-othacehe@gnu.org \
--to=othacehe@gnu.org \
--cc=alex.kanavin@gmail.com \
--cc=openembedded-core@lists.openembedded.org \
--cc=raj.khem@gmail.com \
--cc=richard.purdie@linuxfoundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.