A error stack trace of bt cmd observed:
crash> bt 1
PID: 1 TASK: c000000003714b80 CPU: 2 COMMAND: "systemd"
#0 [c0000000037735c0] _end at c0000000037154b0 (unreliable)
#1 [c000000003773770] __switch_to at c00000000001fa9c
#2 [c0000000037737d0] __schedule at c00000000112e4ec
#3 [c0000000037738b0] schedule at c00000000112ea80
...
The #0 stack trace is incorrect, the function address shouldn't exceed _end.
The reason is for kernel commit cd52414d5a6c ("powerpc/64: ELFv2 use
minimal stack frames in int and switch frame sizes"), the offset of pt_regs
to sp changed from STACK_FRAME_OVERHEAD, i.e 112, to STACK_SWITCH_FRAME_REGS.
For CONFIG_PPC64_ELF_ABI_V1, it's 112, for ABI_V2, it's 48. So the nip will
read a wrong value from stack when ABI_V2 enabled.
After the patch:
crash> bt 1
PID: 1 TASK: c000000003714b80 CPU: 2 COMMAND: "systemd"
#0 [c0000000037737d0] __schedule at c00000000112e4ec
#1 [c0000000037738b0] schedule at c00000000112ea80
...
Signed-off-by: Tao Liu <ltao(a)redhat.com>
Suggested-by: Aditya Gupta <adityag(a)linux.ibm.com>
---
v1 Discussion:
https://www.mail-archive.com/devel@lists.crash-utility.osci.io/msg01181.html
v2 No discussion:
https://www.mail-archive.com/devel@lists.crash-utility.osci.io/msg01170.html
v3 -> v2: Rebase to top-most of upstream patch
Regarding to v1's discussion, we cannot run abiv1 program on abiv2
kernel, it's because abiv1 is big-endian and abiv2 is little-endian, and
abiv2, or ppc64le kernel doesn't support big-endian, or abiv1 program
cannot run upon it, see the following:
$ file blkid
blkid: ELF 64-bit MSB executable, 64-bit PowerPC or cisco 7500, Power ELF V1 ABI, version
1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0,
BuildID[sha1]=b36e8a2a5e4d27039591a35fca38fa48735f5540, stripped
$ ~/qemu-10.1.2/build/qemu-ppc64 ./blkid
/dev/mapper/root: UUID="..." TYPE="xfs"
/dev/sda3: UUID="..." TYPE="LVM2_member" PARTUUID="..."
/dev/sda2: UUID="..." TYPE="xfs" PARTUUID="..."
/dev/mapper/swap: UUID="..." TYPE="swap"
/dev/mapper/home: UUID="..." TYPE="xfs"
/dev/sda1: PARTUUID="..."
$ ./blkid
-bash: ./blkid: cannot execute binary file: Exec format error
$ uname -a
Linux 6.12.0-150.el10.ppc64le #1 SMP Fri Oct 31 06:58:14 EDT 2025 ppc64le GNU/Linux
$ file /bin/bash
/bin/bash: 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]=9ab800028ced16c5974f5b19cb6ed754178802a8, for GNU/Linux 3.10.0, stripped
The abiv1 program blkid cannot be run on this machine, except with the
help of qemu. So from my view, we don't need to consider the case that abiv2
kernel might containing a abiv1 program or .ko.
Please feel free to correct me if I'm wrong. @Aditya Gupta
---
defs.h | 3 ++-
netdump.c | 14 ++++++++++----
ppc64.c | 34 +++++++++++++++++++++++++++++++---
symbols.c | 5 +++--
4 files changed, 46 insertions(+), 10 deletions(-)
diff --git a/defs.h b/defs.h
index ab4aee8..19dff88 100644
--- a/defs.h
+++ b/defs.h
@@ -4699,6 +4699,7 @@ struct efi_memory_desc_t {
#define MSR_PR_LG 14 /* Problem State / Privilege Level */
/* Used to find the user or kernel-mode frame*/
+#define STACK_SWITCH_FRAME_REGS 48
#define STACK_FRAME_OVERHEAD 112
#define EXCP_FRAME_MARKER 0x7265677368657265
@@ -5820,7 +5821,7 @@ void dump_offset_table(char *, ulong);
int is_elf_file(char *);
int is_kernel(char *);
int is_shared_object(char *);
-int file_elf_version(char *);
+int file_elf_header(char *, char *);
int is_system_map(char *);
int is_compressed_kernel(char *, char **);
int select_namelist(char *);
diff --git a/netdump.c b/netdump.c
index 69100a9..9806ce9 100644
--- a/netdump.c
+++ b/netdump.c
@@ -665,11 +665,11 @@ resize_elf_header(int fd, char *file, char **eheader_ptr, char
**sect0_ptr,
}
/*
- * Return the e_version number of an ELF file
+ * Return the e_version or e_flags number of an ELF file
* (or -1 if its not readable ELF file)
*/
int
-file_elf_version(char *file)
+file_elf_header(char *file, char *member)
{
int fd, size;
Elf32_Ehdr *elf32;
@@ -699,11 +699,17 @@ file_elf_version(char *file)
(elf32->e_ident[EI_CLASS] == ELFCLASS32) &&
(elf32->e_ident[EI_DATA] == ELFDATA2LSB) &&
(elf32->e_ident[EI_VERSION] == EV_CURRENT)) {
- return (elf32->e_version);
+ if (STRNEQ(member, "e_version"))
+ return (elf32->e_version);
+ else if (STRNEQ(member, "e_flags"))
+ return (elf32->e_flags);
} else if (STRNEQ(elf64->e_ident, ELFMAG) &&
(elf64->e_ident[EI_CLASS] == ELFCLASS64) &&
(elf64->e_ident[EI_VERSION] == EV_CURRENT)) {
- return (elf64->e_version);
+ if (STRNEQ(member, "e_version"))
+ return (elf64->e_version);
+ else if (STRNEQ(member, "e_flags"))
+ return (elf64->e_flags);
}
return -1;
diff --git a/ppc64.c b/ppc64.c
index d1a5067..213ce90 100644
--- a/ppc64.c
+++ b/ppc64.c
@@ -74,6 +74,7 @@ static ulong pud_page_vaddr_l4(ulong pud);
static ulong pmd_page_vaddr_l4(ulong pmd);
static int is_opal_context(ulong sp, ulong nip);
void opalmsg(void);
+static bool is_ppc64_elf_abi_v2(void);
struct user_regs_bitmap_struct {
struct ppc64_pt_regs ur;
@@ -3035,6 +3036,25 @@ ppc64_get_sp(ulong task)
return sp;
}
+static bool
+is_ppc64_elf_abi_v2(void)
+{
+ static bool ret = false;
+ static bool checked = false;
+
+ if (checked)
+ return ret;
+ switch (file_elf_header(pc->namelist, "e_flags")) {
+ case 2:
+ ret = true;
+ case 1:
+ break;
+ default:
+ error(WARNING, "Unknown e_flags for v1/v2 elf_abi detection.\n");
+ }
+ checked = true;
+ return ret;
+}
/*
* get the SP and PC values for idle tasks.
@@ -3056,9 +3076,17 @@ get_ppc64_frame(struct bt_info *bt, ulong *getpc, ulong *getsp)
sp = ppc64_get_sp(task);
if (!INSTACK(sp, bt))
goto out;
- readmem(sp+STACK_FRAME_OVERHEAD, KVADDR, ®s,
- sizeof(struct ppc64_pt_regs),
- "PPC64 pt_regs", FAULT_ON_ERROR);
+
+ if (THIS_KERNEL_VERSION >= LINUX(6,2,0) && is_ppc64_elf_abi_v2()) {
+ readmem(sp+STACK_SWITCH_FRAME_REGS, KVADDR, ®s,
+ sizeof(struct ppc64_pt_regs),
+ "PPC64 pt_regs", FAULT_ON_ERROR);
+ } else {
+ readmem(sp+STACK_FRAME_OVERHEAD, KVADDR, ®s,
+ sizeof(struct ppc64_pt_regs),
+ "PPC64 pt_regs", FAULT_ON_ERROR);
+ }
+
ip = regs.nip;
closest = closest_symbol(ip);
if (STREQ(closest, ".__switch_to") || STREQ(closest, "__switch_to"))
{
diff --git a/symbols.c b/symbols.c
index 480fdb6..0a11c2f 100644
--- a/symbols.c
+++ b/symbols.c
@@ -217,7 +217,7 @@ symtab_init(void)
* Check whether the namelist is a kerntypes file built by
* dwarfextract, which places a magic number in e_version.
*/
- if (file_elf_version(pc->namelist) == EV_DWARFEXTRACT)
+ if (file_elf_header(pc->namelist, "e_version") == EV_DWARFEXTRACT)
pc->flags |= KERNTYPES;
if (pc->flags & SYSMAP) {
@@ -13149,7 +13149,8 @@ load_module_symbols(char *modref, char *namelist, ulong
base_addr)
error(FATAL, "cannot determine object file format: %s\n",
namelist);
- if (LKCD_KERNTYPES() && (file_elf_version(namelist) == EV_DWARFEXTRACT))
+ if (LKCD_KERNTYPES() &&
+ (file_elf_header(namelist, "e_version") == EV_DWARFEXTRACT))
goto add_symbols; /* no symbols, add the debuginfo */
if (!(bfd_get_file_flags(mbfd) & HAS_SYMS))
--
2.47.0