The vmcore-dmesg tool could fail with the message: "No program header covering vaddr 0x%llx found kexec bug?"
This occurred when a virtual address belonged to the kernel’s direct mapping region, which may not be covered by any PT_LOAD segment in the vmcore ELF headers. Add a direct-map fallback in vaddr_to_offset() that converts such virtual addresses using the known page and physical offsets. This allows resolving these addresses correctly. Tested on Linux 6.16 (RISC-V) Signed-off-by: Pnina Feder <[email protected]> --- util_lib/elf_info.c | 58 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/util_lib/elf_info.c b/util_lib/elf_info.c index b005245..589bc1a 100644 --- a/util_lib/elf_info.c +++ b/util_lib/elf_info.c @@ -72,6 +72,7 @@ static uint16_t log_offset_len = UINT16_MAX; static uint16_t log_offset_text_len = UINT16_MAX; static uint64_t phys_offset = UINT64_MAX; +static uint64_t page_offset = UINT64_MAX; #if __BYTE_ORDER == __LITTLE_ENDIAN #define ELFDATANATIVE ELFDATA2LSB @@ -115,7 +116,26 @@ static uint64_t vaddr_to_offset(uint64_t vaddr) continue; return (vaddr - phdr[i].p_vaddr) + phdr[i].p_offset; } - fprintf(stderr, "No program header covering vaddr 0x%llxfound kexec bug?\n", + + /* Direct map fallback */ + if (page_offset != UINT64_MAX && + phys_offset != UINT64_MAX && + vaddr >= page_offset) { + + uint64_t paddr = 0; + + paddr = vaddr - (page_offset - phys_offset); + + for (i = 0; i < ehdr.e_phnum; i++) { + if (phdr[i].p_paddr > paddr) + continue; + if ((phdr[i].p_paddr + phdr[i].p_memsz) <= paddr) + continue; + return phdr[i].p_offset + (paddr - phdr[i].p_paddr); + } + } + + fprintf(stderr, "No program header covering vaddr 0x%llx found kexec bug?\n", (unsigned long long)vaddr); exit(30); } @@ -309,6 +329,20 @@ int get_pt_load(int idx, return 1; } +static inline int parse_phys_offset(const char *str, char *pos) +{ + char *endp; + + phys_offset = strtoul(pos + strlen(str), &endp, 10); + if (strlen(endp) != 0) + phys_offset = strtoul(pos + strlen(str), &endp, 16); + if ((phys_offset == LONG_MAX) || strlen(endp) != 0) { + fprintf(stderr, "Invalid data %s\n", pos); + return -1; + } + return 0; +} + #define NOT_FOUND_LONG_VALUE (-1) void (*arch_scan_vmcoreinfo)(char *pos); @@ -319,7 +353,7 @@ void scan_vmcoreinfo(char *start, size_t size) char *pos, *eol; char temp_buf[1024]; bool last_line = false; - char *str, *endp; + char *str; #define SYMBOL(sym) { \ .str = "SYMBOL(" #sym ")=", \ @@ -543,17 +577,21 @@ void scan_vmcoreinfo(char *start, size_t size) /* Check for PHYS_OFFSET number */ str = "NUMBER(PHYS_OFFSET)="; if (memcmp(str, pos, strlen(str)) == 0) { - phys_offset = strtoul(pos + strlen(str), &endp, - 10); - if (strlen(endp) != 0) - phys_offset = strtoul(pos + strlen(str), &endp, 16); - if ((phys_offset == LONG_MAX) || strlen(endp) != 0) { - fprintf(stderr, "Invalid data %s\n", - pos); + if (parse_phys_offset(str, pos) != 0) break; - } } + /* Check for PHYS_OFFSET number on some arch it called phys_ram_base*/ + str = "NUMBER(phys_ram_base)="; + if (memcmp(str, pos, strlen(str)) == 0) { + if (parse_phys_offset(str, pos) != 0) + break; + } + + str = "NUMBER(PAGE_OFFSET)="; + if (memcmp(str, pos, strlen(str)) == 0) + page_offset = strtoull(pos + strlen(str), NULL, 16); + if (arch_scan_vmcoreinfo != NULL) (*arch_scan_vmcoreinfo)(pos); -- 2.43.0
