runc no_pivot test fails on ppc64le
Description
runc no_pivot test fails on ppc64le.
Tracking in SUSE as https://bugzilla.suse.com/show_bug.cgi?id=1247568
openQA job: https://openqa.opensuse.org/tests/5220249
Steps to reproduce the issue
### RUN AS root
curl -sL --retry 9 --retry-delay 100 --retry-max-time 900 https://github.com/bats-core/bats-core/archive/refs/tags/v1.11.1.tar.gz | tar -zxf -
bash bats-core-1.11.1/install.sh /usr/local
rm -rf bats-core-1.11.1
mkdir -pm 0750 /etc/sudoers.d/
echo 'Defaults secure_path="/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin"' > /etc/sudoers.d/usrlocal
zypper --gpg-auto-import-keys -n install glibc-devel-static go1.24 jq libseccomp-devel make runc criu
mkdir -p /etc/systemd/system/[email protected]/
echo -e "[Service]\nDelegate=cpu cpuset io memory pids" > /etc/systemd/system/[email protected]/60-delegate.conf
reboot
### RUN AS user
cd /var/tmp/
git clone https://github.com/opencontainers/runc.git
cd /var/tmp/runc
git checkout v1.3.0
make memfd-bind fs-idmap pidfd-kill recvtty remap-rootfs sd-helper seccompagent || true
### RUN AS root
cd /var/tmp/runc
mkdir -p /var/tmp/test.qFQ71a
env BATS_TMPDIR=/var/tmp/test.qFQ71a PATH=/usr/local/bin:$PATH:/usr/sbin:/sbin RUNC=/usr/bin/runc RUNC_USE_SYSTEMD=1 bats --tap -T tests/integration/no_pivot.bats
sudo rm -rf /var/tmp/test.qFQ71a || true
Describe the results you received and expected
not ok 174 runc run --no-pivot must not expose bare /proc in 961ms
# (in test file tests/integration/no_pivot.bats, line 21)
# `[ "$status" -eq 1 ]' failed
# runc spec (status=0):
#
# runc run --no-pivot test_no_pivot (status=139):
# + mount -t proc none /proc
# mount: permission denied (are you root?)
# --- teardown ---
What version of runc are you using?
runc version 1.3.0 commit: v1.3.0-0-g4ca628d1d4c9 spec: 1.2.1 go: go1.24.2 libseccomp: 2.6.0
Host OS information
openSUSE Tumbleweed 20250803
Host kernel information
Linux opensuse 6.15.8-1-default #1 SMP PREEMPT_DYNAMIC Thu Jul 24 07:19:58 UTC 2025 (e03d052) x86_64 x86_64 x86_64 GNU/Linux
Are you replacing BUSYBOX_IMAGE with some other rootfs? Our scripts (tests/integration/get-images.sh) download the archives from the internet but I suspect that isn't allowed in openQA scripts.
We do expect an error here, and the error message is correct -- but the error code is 139 not 1. The most obvious cause I can think of would be that the mount (or unshare) binaries are not the ones we use in our tests, and so have different error codes.
Or maybe there's something up with the ppc64le binaries we use? We don't have CI for some of the more "esoteric" architectures like ppc64le and s390x. I think that the only time I've managed to test these was using SUSE equipment internally...
Are you replacing
BUSYBOX_IMAGEwith some other rootfs? Our scripts (tests/integration/get-images.sh) download the archives from the internet but I suspect that isn't allowed in openQA scripts.
We don't call this script directly and I guess it's called by helpers.bash.
We do expect an error here, and the error message is correct -- but the error code is
139not1. The most obvious cause I can think of would be that themount(orunshare) binaries are not the ones we use in our tests, and so have different error codes.
Will look into this.
We do expect an error here, and the error message is correct -- but the error code is
139not1. The most obvious cause I can think of would be that themount(orunshare) binaries are not the ones we use in our tests, and so have different error codes.
Or a SIGSEGV. I see a call trace here:
https://openqa.opensuse.org/tests/5220249/file/serial0.txt
Or a SIGSEGV. I see a call trace here:
https://openqa.opensuse.org/tests/5220249/file/serial0.txt
The call trace might be from the OOM killer, there are some tests that cause an OOM, so unless you're running this test separately, this might be unrelated.
You can run specific bats tests by supplying either file name(s) or -f <test-name-regex> to bats.
In case it's SIGSEGV (looks like it), a stack trace will be helpful.
I ran this job running only this test: https://openqa.opensuse.org/tests/5224428
Please tell me if you need any more information before the garbage collector removes some logs. I can restart the test anyway.
Output of journalctl here: https://openqa.opensuse.org/tests/5224428/file/runc-journalctl-b.txt
Aug 06 04:14:54 susetest systemd[1]: Started libcontainer container test_no_pivot.
Aug 06 04:14:54 susetest kernel: unshare[3528]: segfault (11) at 4699b2e8eead9efc nip 4699b2e8eead9efc lr 126be4fe8 code 3
Aug 06 04:14:54 susetest kernel: unshare[3528]: code: XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
Aug 06 04:14:54 susetest kernel: unshare[3528]: code: XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
Aug 06 04:14:54 susetest systemd-coredump[3536]: Process 3528 (unshare) of user 0 terminated abnormally with signal 11/SEGV, processing...
Aug 06 04:14:54 susetest systemd[1]: Created slice Slice /system/systemd-coredump.
Aug 06 04:14:54 susetest systemd[1]: Started Process Core Dump (PID 3536/UID 0).
Aug 06 04:14:54 susetest systemd-coredump[3537]: Process 3528 (unshare) of user 0 dumped core.
Stack trace of thread 1:
#0 0x4699b2e8eead9efc n/a (n/a + 0x0)
#1 0x0000000126be4fe8 n/a (/bin/unshare + 0x14fe8)
#2 0x0000000126be6520 n/a (/bin/unshare + 0x16520)
#3 0x0000000126be65a0 n/a (/bin/unshare + 0x165a0)
#4 0x0000000126c8819c n/a (/bin/unshare + 0xb819c)
#5 0x0000000126be31e0 n/a (/bin/unshare + 0x131e0)
#6 0x0000000126be3684 n/a (/bin/unshare + 0x13684)
#7 0x0000000126be3748 n/a (/bin/unshare + 0x13748)
#8 0x00007fff8d094bcc n/a (/lib/libc.so.6 + 0x24bcc)
#9 0x00007fff8d094db0 n/a (/lib/libc.so.6 + 0x24db0)
ELF object binary architecture: PowerPC64
If you can grab the coredump and provide it to me (either here or slack), that could be useful. Though I suspect that our busybox images don't have any debug symbols...
If you can grab the coredump and provide it to me (either here or slack), that could be useful. Though I suspect that our busybox images don't have any debug symbols...
You can download it from here: https://openqa.opensuse.org/tests/5224801/file/runc-unshare.3494.core.gz
So, we are using a somewhat old version of busybox:glibc in our tests (1.34.1). Might make sense to bump to 1.35.1, although I just realized I have no idea what to put into https://github.com/opencontainers/runc/blob/main/tests/integration/bootstrap-get-images.sh#L13 to make it happen. @tianon can you help 🥺🙏🏻
There's also kernel call traces in the original job:
https://openqa.opensuse.org/tests/5220249/file/serial0.txt
There's also kernel call traces in the original job:
https://openqa.opensuse.org/tests/5220249/file/serial0.txt
Those can be ignored as they were triggered by the OOM killer.
I don’t think this issue has anything to do with the OOM killer either.
Following the steps shared by @ricardobranco777, I was able to reproduce the problem on a local setup. Every time I ran the no_pivot testcase, I consistently saw the following kernel logs:
[ 357.237289][ T6093] unshare[6093]: segfault (11) at 4699b2e8eead9efc nip 4699b2e8eead9efc lr 1161a4fe8 code 3
[ 357.237331][ T6093] unshare[6093]: code: XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
[ 357.237338][ T6093] unshare[6093]: code: XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
Narrowing down the problem
I tried changing the testcase parameters:
- Removing the
--no-pivotoption fromrunc run→ the segfault still occurred. - Tweaking the arguments passed to
unshare→ when I removed-ffrom-mrpf, the segfault disappeared.
This pointed to an issue with the unshare binary itself.
Checking the unshare version
I modified the testcase to run unshare -V instead:
- update_config ' .process.args |= ["unshare", "-mrpf", "sh", "-euxc", "mount -t proc none /proc && echo h > /proc/sysrq-trigger"]
+ update_config ' .process.args |= ["unshare", "-V"]
| .process.capabilities.bounding += ["CAP_SETFCAP"]
| .process.capabilities.permitted += ["CAP_SETFCAP"]'
This showed that the unshare used in the testcase was v1.34.1, while the system’s native unshare was v2.32.1.
Running the command directly with the system’s unshare worked fine:
unshare -mrpf sh -euxc 'mount -t proc none /proc && echo h > /proc/sysrq-trigger'
So the issue seemed isolated to BusyBox’s version of unshare.
Debugging further
I collected a coredump from the crashing BusyBox unshare, but without debug symbols the backtrace was useless.
Next, I rebuilt BusyBox with debug symbols and re-ran the same command. This time, the segfault reproduced even without involving runc.
The backtrace was:
(gdb) bt
#0 0x00000000100080e4 in 0000004f.plt_call.waitpid@@GLIBC_2.17 ()
#1 0x000000001000dbfc in safe_waitpid (pid=<optimized out>, wstat=0x7fffdf2ef8c0, options=<optimized out>) at libbb/xfuncs.c:398
#2 0x000000001000f218 in wait_for_exitstatus (pid=<optimized out>) at libbb/xfuncs_printf.c:721
#3 0x000000001000f298 in xvfork_parent_waits_and_exits () at libbb/xfuncs_printf.c:707
#4 0x00000000100b27d0 in unshare_main (argc=<optimized out>, argv=<optimized out>) at util-linux/unshare.c:376
#5 0x000000001000bde4 in run_applet_no_and_exit (applet_no=<optimized out>, name=name@entry=0x7fffdf2ff525 "unshare", argv=argv@entry=0x7fffdf2eff58)
at libbb/appletlib.c:971
#6 0x000000001000c260 in run_applet_and_exit (name=0x7fffdf2ff525 "unshare", argv=argv@entry=0x7fffdf2eff58) at libbb/appletlib.c:990
#7 0x000000001000c328 in main (argc=<optimized out>, argv=0x7fffdf2eff58) at libbb/appletlib.c:1130
Conclusion
The segfault is not related to runc or the kernel. The problem lies in BusyBox’s implementation of unshare when used with -mrpf. The system’s unshare binary works correctly, so this looks like a BusyBox bug.
So, we are using a somewhat old version of busybox:glibc in our tests (1.34.1). Might make sense to bump to 1.35.1, although I just realized I have no idea what to put into
Bumping will not help, because I tried with the latest busybox version v1.37.X, and the issue is still present there.
I have been able to boil things down to this
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int child = vfork();
printf("Child: %d\n", child);
if (child) {
sleep(1);
int ret = waitpid(child,0, 0);
}
return EXIT_SUCCESS;
}
📎 logs
# ./a.out
Child: 0
Segmentation fault (core dumped)
Program gets SIGSEGV for ppc64le.
Further debugging reveals,
For ppc64le architecture, when a child created using vfork() exits|returns without calling exit(), the program gets SIGSEGV
Without exit()
$ cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int child = vfork();
printf("Child: %d\n", child);
if (child) {
int ret = waitpid(child,0, 0);
}
sleep(2);
return 0;
}
$ gcc test.c
$ ./a.out
Child: 0
Segmentation fault (core dumped)
With exit()
$ cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int child = vfork();
printf("Child: %d\n", child);
if (child) {
int ret = waitpid(child,0, 0);
}
sleep(2);
exit(1);
return 0;
}
$ gcc test.c
$ ./a.out
Child: 0
Child: 38115
🧾 Program can get into undefined state if vforked child did something that if was not supposed to do.
Manpage describes the issue at hand like this
DESCRIPTION
Standard description
(From POSIX.1) The vfork() function has the same effect as fork(2), except that the behavior is undefined if the process created by vfork() either modifies any data other than a variable of type
pid_t used to store the return value from vfork(), or returns from the function in which vfork() was called, or calls any other function before successfully calling _exit(2) or one of the
exec(3) family of functions.
Linux description
vfork(), just like fork(2), creates a child process of the calling process. For details and return value and errors, see fork(2).
vfork() is a special case of clone(2). It is used to create new processes without copying the page tables of the parent process. It may be useful in performance-sensitive applications where a
child is created which then immediately issues an execve(2).
vfork() differs from fork(2) in that the calling thread is suspended until the child terminates (either normally, by calling _exit(2), or abnormally, after delivery of a fatal signal), or it
makes a call to execve(2). Until that point, the child shares all memory with its parent, including the stack. The child must not return from the current function or call exit(3) (which would
have the effect of calling exit handlers established by the parent process and flushing the parent's stdio(3) buffers), but may call _exit(2).
As with fork(2), the child process created by vfork() inherits copies of various of the caller's process attributes (e.g., file descriptors, signal dispositions, and current working directory);
the vfork() call differs only in the treatment of the virtual address space, as described above.
Signals sent to the parent arrive after the child releases the parent's memory (i.e., after the child terminates or calls execve(2)).
Also, if I run busybox's unshare with just the -f flag which does the same vfork invocation but without any other -mrp options then I don't see any SIGSEGV.
Please correct if I am wrong. It seems like after child is created, it messes with parent's stack modifying something (perhaps the link register or TOC register) such that when the child finishes and when the parent gets to run again it is jumping to an invalid address which is causing the SIGSEGV
Seems like a bug for busybox to be using vfork for this...
@vishalc-ibm Thanks a lot. Tracking in Bugzilla as https://bugzilla.opensuse.org/show_bug.cgi?id=1249237
Also notified the BB ML: https://lists.busybox.net/pipermail/busybox/2025-September/091718.html
Note that my understanding is that the only things you are really allowed to do in a vfork() child are:
- Call
_exit(2)immediately (notexit(3)!). - Call
execve(2)immediately.
You cannot touch any other memory (including the stack). I am very surprised they appear to be doing actual function calls in the vfork() child? I'll take a closer look at the code on Monday.
We also observed one more thing while debugging this:
While compiling the busybox with static libs, they were not crashing. The issue seems to be related to dynamic libs.
debian@ltc-zz14-lp9:~/test-busybox/rootfs-3$ file ./bin/busybox
./bin/busybox: ELF 64-bit LSB executable, 64-bit PowerPC or cisco 7500, OpenPOWER ELF V2 ABI, version 1 (GNU/Linux), statically linked, BuildID[sha1]=8492f602d0fe71fba3cd0d5a1b1807c443f97048, for GNU/Linux 3.10.0, with debug_info, not stripped
debian@ltc-zz14-lp9:~/test-busybox/rootfs-3$ ./bin/busybox unshare -mrpf bash -c ls
bin dev etc proc sbin sys tmp usr
debian@ltc-zz14-lp9:~/test-busybox/rootfs-3$
According to POSIX, there is an udefined behaviour if the child process created by vfork() either modifies the data other than a variable of type pid_t or calls any other functions before successfully calling exec (3) or _exit(2) family functions.
From the strace logs,it looks like the child after vfork performed syscalls like writing uid_map, gid_map, mounting, etc, which violates the minimal action requirements of vfork() resulting in a SIGSEGV maybe due to race conditions.
[ 282] [00003fff9378986c] unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWPID) = 0
[ 45] [00003fff9377727c] brk(NULL) = 0x1003c213000
[ 45] [00003fff9377727c] brk(0x1003c234000) = 0x1003c234000
[ 286] [00003fff936d7a80] openat(AT_FDCWD, "/proc/self/setgroups", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
[ 108] [00003fff9376e7f4] fstat(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
[ 4] [00003fff936d7a80] write(3, "deny\n", 5) = 5
[ 6] [00003fff93775664] close(3) = 0
[ 286] [00003fff936d7a80] openat(AT_FDCWD, "/proc/self/uid_map", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
[ 24] [00003fff93748de8] getuid() = 65534
[ 108] [00003fff9376e7f4] fstat(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
[ 4] [00003fff936d7a80] write(3, "0 65534 1\n", 10) = -1 EPERM (Operation not permitted)
[ 6] [00003fff93775664] close(3) = 0
[ 286] [00003fff936d7a80] openat(AT_FDCWD, "/proc/self/gid_map", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
[ 47] [00003fff93747680] getgid() = 65534
[ 108] [00003fff9376e7f4] fstat(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
[ 4] [00003fff936d7a80] write(3, "0 65534 1\n", 10) = -1 EPERM (Operation not permitted)
[ 6] [00003fff93775664] close(3) = 0
[ 189] [00003fff9376726c] vfork(strace: Process 376786 attached
<unfinished ...>
[pid 376786] [ 286] [00003fff936d7a80] openat(AT_FDCWD, "/proc/self/setgroups", O_WRONLY) = 3
[pid 376786] [ 4] [00003fff936d7a80] write(3, "deny", 4) = 4
[pid 376786] [ 6] [00003fff936d7a80] close(3) = 0
[pid 376786] [ 286] [00003fff936d7a80] openat(AT_FDCWD, "/proc/self/uid_map", O_WRONLY) = 3
[pid 376786] [ 4] [00003fff936d7a80] write(3, "0 1000 1", 8) = 8
[pid 376786] [ 6] [00003fff936d7a80] close(3) = 0
[pid 376786] [ 286] [00003fff936d7a80] openat(AT_FDCWD, "/proc/self/gid_map", O_WRONLY) = 3
[pid 376786] [ 4] [00003fff936d7a80] write(3, "0 1000 1", 8) = 8
[pid 376786] [ 6] [00003fff936d7a80] close(3) = 0
[pid 376786] [ 21] [00003fff937890fc] mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL) = 0
[pid 376786] [ 11] [00003fff93741230] execve("/usr/local/bin/bash", ["bash", "-c", "ls"], 0x3fffe38b2780 /* 22 vars */) = -1 ENOENT (No such file or directory)
[pid 376786] [ 11] [00003fff93741230] execve("/usr/bin/bash", ["bash", "-c", "ls"], 0x3fffe38b2780 /* 22 vars */ <unfinished ...>
[pid 376785] [ 189] [00003fff9376726c] <... vfork resumed>) = 376786
[pid 376785] [ 189] [f37838210030e840] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_BNDERR, si_addr=0xf37838210030e840, si_lower=NULL, si_upper=NULL} ---
[pid 376786] [ 11] [00003fffaf368aa0] <... execve resumed>) = 0
[pid 376786] [ 45] [00003fffaf371188] brk(NULL) = 0x100157a6000
[pid 376786] [ 90] [00003fffaf374638] mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x3fffaf391000
[pid 376786] [ 33] [00003fffaf373e90] access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
[pid 376786] [ 286] [00003fffaf374340] openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
[pid 376785] [ 189] [????????????????] +++ killed by SIGSEGV +++
Replacing vfork() with fork() will eliminate this issue because fork() creates a separate memory space, so the child’s operations cannot corrupt the parent’s stack and both processes operate independently.
Following is the fix for that:
debian@ltc-zz14-lp9:~/test-busybox/busybox-1.37.0$ cat tshah-runc-issue.patch
diff --git a/include/libbb.h b/include/libbb.h
index 1962d93..76a04c3 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1234,6 +1234,7 @@ int BB_EXECVP(const char *file, char *const argv[]) FAST_FUNC;
#endif
void BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC;
+#if !BB_MMU
/* xvfork() can't be a _function_, return after vfork in child mangles stack
* in the parent. It must be a macro. */
#define xvfork() \
@@ -1243,8 +1244,12 @@ void BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC;
bb_simple_perror_msg_and_die("vfork"); \
bb__xvfork_pid; \
})
-#if BB_MMU
+#else
pid_t xfork(void) FAST_FUNC;
+/* Using fork instead of vfork on MMU-enabled targets to avoid segmentation
+ * fault.
+ */
+#define xvfork() xfork()
#endif
void xvfork_parent_waits_and_exits(void) FAST_FUNC;
Built busybox with these changes and tested it. I am not seeing the segmentation fault anymore.
debian@ltc-zz14-lp9:~/test-busybox/busybox-1.37.0$ ./busybox_unstripped unshare -mrpf bash -c ls
applets Config.in findutils Makefile printutils testsuite
applets_sh configs include Makefile.custom procps TODO
arch console-tools init Makefile.flags qemu_multiarch_testing TODO_unicode
archival coreutils INSTALL Makefile.help README trace.log
AUTHORS debian klibc-utils make_single_applets.sh runit trace_sfter_change.log
busybox debianutils libbb miscutils scripts util-linux
busybox_ldscript.README.txt docs libpwdgrp modutils selinux
busybox_unstripped e2fsprogs LICENSE networking shell
busybox_unstripped.map editors loginutils NOFORK_NOEXEC.lst size_single_applets.sh
busybox_unstripped.out examples mailutils NOFORK_NOEXEC.sh sysklogd
debian@ltc-zz14-lp9:~/test-busybox/busybox-1.37.0$ file busybox_unstripped
busybox_unstripped: ELF 64-bit LSB pie executable, 64-bit PowerPC or cisco 7500, OpenPOWER ELF V2 ABI, version 1 (SYSV), dynamically linked, interpreter /lib64/ld64.so.2, BuildID[sha1]=d321db257a5e0a503516d6417277adb22fb19a7d, for GNU/Linux 3.10.0, with debug_info, not stripped
This may have been fixed upstream. Haven't tested it yet:
https://git.busybox.net/busybox/commit/?id=3621595939e43a831d66f6b757d4f410029bff95
Hello team, I tried running unshare with the latest version of the busybox, and we are not seeing the crash anymore. The issue has been fixed with this commit https://git.busybox.net/busybox/commit/?id=3621595939e43a831d66f6b757d4f410029bff95
debian@ltc-zz14-lp9:~/test-busybox2/busybox$ ./busybox unshare -mrpf bash -c ls
applets busybox_unstripped.map e2fsprogs libbb Makefile.help procps sysklogd
applets_sh busybox_unstripped.out editors libpwdgrp make_single_applets.sh qemu_multiarch_testing testsuite
arch Config.in examples LICENSE miscutils README TODO
archival configs findutils loginutils modutils runit TODO_unicode
AUTHORS console-tools include mailutils networking scripts util-linux
busybox coreutils init Makefile NOFORK_NOEXEC.lst selinux
busybox_ldscript.README.txt debianutils INSTALL Makefile.custom NOFORK_NOEXEC.sh shell
busybox_unstripped docs klibc-utils Makefile.flags printutils size_single_applets.sh
debian@ltc-zz14-lp9:~/test-busybox2/busybox$ file busybox_unstripped
busybox_unstripped: ELF 64-bit LSB pie executable, 64-bit PowerPC or cisco 7500, OpenPOWER ELF V2 ABI, version 1 (SYSV), dynamically linked, interpreter /lib64/ld64.so.2, BuildID[sha1]=69c8ba08befea72b2c0a4a07dfbd5e9a0d9a01aa, for GNU/Linux 3.10.0, with debug_info, not stripped
debian@ltc-zz14-lp9:~/test-busybox2/busybox$
debian@ltc-zz14-lp9:~/test-busybox2/busybox$ ./busybox --version
BusyBox v1.38.0.git (2025-10-18 13:39:08 IST)