diff --git a/0001-Fix-kernel-version-macros-for-revision-numbers-over-.patch b/0001-Fix-kernel-version-macros-for-revision-numbers-over-.patch new file mode 100644 index 0000000..b044206 --- /dev/null +++ b/0001-Fix-kernel-version-macros-for-revision-numbers-over-.patch @@ -0,0 +1,52 @@ +From 040a56e9f9d0df15a2f8161ed3a0a907d70dda03 Mon Sep 17 00:00:00 2001 +From: Kazuhito Hagio +Date: Wed, 10 May 2023 16:09:03 +0900 +Subject: [PATCH 01/13] Fix kernel version macros for revision numbers over 255 + +The current comparison macros for kernel version shift minor number only +8 bits. This can cause an unexpected result on kernels with revision +number over 255, e.g. Linux 4.14.314. + +In fact, on Linux 4.14.314 for x86_64 without CONFIG_RANDOMIZE_BASE=y +(KASLR), the following condition became false in x86_64_init(). + + ((THIS_KERNEL_VERSION >= LINUX(4,14,84)) && + (THIS_KERNEL_VERSION < LINUX(4,15,0))) + +As a result, crash used a wrong hard-coded value for PAGE_OFFSET and +failed to start a session with the following seek error. + + crash: seek error: physical address: 200e000 type: "pud page" + +Shift the major and minor number by 24 and 16 bits respectively to fix +this issue. + +Reported-by: Luiz Capitulino +Tested-by: Luiz Capitulino +Signed-off-by: Kazuhito Hagio +Signed-off-by: Lianbo Jiang +--- + defs.h | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/defs.h b/defs.h +index 12ad6aaa0998..211fc9d55d33 100644 +--- a/defs.h ++++ b/defs.h +@@ -807,10 +807,10 @@ struct kernel_table { /* kernel data */ + } \ + } + +-#define THIS_KERNEL_VERSION ((kt->kernel_version[0] << 16) + \ +- (kt->kernel_version[1] << 8) + \ ++#define THIS_KERNEL_VERSION ((kt->kernel_version[0] << 24) + \ ++ (kt->kernel_version[1] << 16) + \ + (kt->kernel_version[2])) +-#define LINUX(x,y,z) (((uint)(x) << 16) + ((uint)(y) << 8) + (uint)(z)) ++#define LINUX(x,y,z) (((uint)(x) << 24) + ((uint)(y) << 16) + (uint)(z)) + + #define THIS_GCC_VERSION ((kt->gcc_version[0] << 16) + \ + (kt->gcc_version[1] << 8) + \ +-- +2.37.1 + diff --git a/0002-Fix-failure-of-dev-d-D-options-on-Linux-6.4-and-late.patch b/0002-Fix-failure-of-dev-d-D-options-on-Linux-6.4-and-late.patch new file mode 100644 index 0000000..c7789e7 --- /dev/null +++ b/0002-Fix-failure-of-dev-d-D-options-on-Linux-6.4-and-late.patch @@ -0,0 +1,179 @@ +From 58c1816521c2e6bece3d69256b1866c9df8d93aa Mon Sep 17 00:00:00 2001 +From: Kazuhito Hagio +Date: Tue, 16 May 2023 08:59:50 +0900 +Subject: [PATCH 02/13] Fix failure of "dev -d|-D" options on Linux 6.4 and + later kernels + +Kernel commit 2df418cf4b72 ("driver core: class: remove subsystem +private pointer from struct class"), which is contained in Linux 6.4 and +later kernels, removed the class.p member for struct subsys_private. As +a result, the "dev -d|-D" options fail with the following error. + + dev: invalid structure member offset: class_p + FILE: dev.c LINE: 4689 FUNCTION: init_iter() + +Search the class_kset list for the subsys_private of block class to fix +this. + +As a preparation, introduce get_subsys_private() function, which is +abstracted from the same search procedure in init_memory_block(). + +Signed-off-by: Kazuhito Hagio +Signed-off-by: Lianbo Jiang +--- + defs.h | 1 + + dev.c | 20 +++++++++++++++++--- + memory.c | 35 +++-------------------------------- + tools.c | 43 +++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 64 insertions(+), 35 deletions(-) + +diff --git a/defs.h b/defs.h +index 211fc9d55d33..21cc760444d1 100644 +--- a/defs.h ++++ b/defs.h +@@ -5521,6 +5521,7 @@ struct rb_node *rb_left(struct rb_node *, struct rb_node *); + struct rb_node *rb_next(struct rb_node *); + struct rb_node *rb_last(struct rb_root *); + long percpu_counter_sum_positive(ulong fbc); ++ulong get_subsys_private(char *, char *); + + /* + * symbols.c +diff --git a/dev.c b/dev.c +index 75d30bd022a1..9d38aef9b3db 100644 +--- a/dev.c ++++ b/dev.c +@@ -4686,9 +4686,16 @@ init_iter(struct iter *i) + } else { + /* kernel version > 2.6.27, klist */ + unsigned long class_private_addr; +- readmem(block_class_addr + OFFSET(class_p), KVADDR, +- &class_private_addr, sizeof(class_private_addr), +- "class.p", FAULT_ON_ERROR); ++ ++ if (INVALID_MEMBER(class_p)) /* kernel version >= 6.4 */ ++ class_private_addr = get_subsys_private("class_kset", "block"); ++ else ++ readmem(block_class_addr + OFFSET(class_p), KVADDR, ++ &class_private_addr, sizeof(class_private_addr), ++ "class.p", FAULT_ON_ERROR); ++ ++ if (!class_private_addr) ++ error(FATAL, "cannot determine subsys_private for block.\n"); + + if (VALID_STRUCT(class_private)) { + /* 2.6.27 < kernel version <= 2.6.37-rc2 */ +@@ -4823,6 +4830,13 @@ void diskio_init(void) + if (INVALID_MEMBER(class_devices)) + MEMBER_OFFSET_INIT(class_devices, "class", "devices"); + MEMBER_OFFSET_INIT(class_p, "class", "p"); ++ if (INVALID_MEMBER(class_p)) { ++ MEMBER_OFFSET_INIT(kset_list, "kset", "list"); ++ MEMBER_OFFSET_INIT(kset_kobj, "kset", "kobj"); ++ MEMBER_OFFSET_INIT(kobject_name, "kobject", "name"); ++ MEMBER_OFFSET_INIT(kobject_entry, "kobject", "entry"); ++ MEMBER_OFFSET_INIT(subsys_private_subsys, "subsys_private", "subsys"); ++ } + MEMBER_OFFSET_INIT(class_private_devices, "class_private", + "class_devices"); + MEMBER_OFFSET_INIT(device_knode_class, "device", "knode_class"); +diff --git a/memory.c b/memory.c +index 0568f18eb9b7..953fc380c03c 100644 +--- a/memory.c ++++ b/memory.c +@@ -17865,38 +17865,9 @@ init_memory_block(int *klistcnt, ulong **klistbuf) + * v6.3-rc1 + * d2bf38c088e0 driver core: remove private pointer from struct bus_type + */ +- if (INVALID_MEMBER(bus_type_p)) { +- int i, cnt; +- char buf[32]; +- ulong bus_kset, list, name; +- +- BZERO(ld, sizeof(struct list_data)); +- +- get_symbol_data("bus_kset", sizeof(ulong), &bus_kset); +- readmem(bus_kset + OFFSET(kset_list), KVADDR, &list, +- sizeof(ulong), "bus_kset.list", FAULT_ON_ERROR); +- +- ld->flags |= LIST_ALLOCATE; +- ld->start = list; +- ld->end = bus_kset + OFFSET(kset_list); +- ld->list_head_offset = OFFSET(kobject_entry); +- +- cnt = do_list(ld); +- for (i = 0; i < cnt; i++) { +- readmem(ld->list_ptr[i] + OFFSET(kobject_name), KVADDR, &name, +- sizeof(ulong), "kobject.name", FAULT_ON_ERROR); +- read_string(name, buf, sizeof(buf)-1); +- if (CRASHDEBUG(1)) +- fprintf(fp, "kobject: %lx name: %s\n", ld->list_ptr[i], buf); +- if (STREQ(buf, "memory")) { +- /* entry is subsys_private.subsys.kobj. See bus_to_subsys(). */ +- private = ld->list_ptr[i] - OFFSET(kset_kobj) +- - OFFSET(subsys_private_subsys); +- break; +- } +- } +- FREEBUF(ld->list_ptr); +- } else { ++ if (INVALID_MEMBER(bus_type_p)) ++ private = get_subsys_private("bus_kset", "memory"); ++ else { + ulong memory_subsys = symbol_value("memory_subsys"); + readmem(memory_subsys + OFFSET(bus_type_p), KVADDR, &private, + sizeof(void *), "memory_subsys.private", FAULT_ON_ERROR); +diff --git a/tools.c b/tools.c +index c2cfa7e280bc..392a79707e61 100644 +--- a/tools.c ++++ b/tools.c +@@ -6963,3 +6963,46 @@ percpu_counter_sum_positive(ulong fbc) + + return (ret < 0) ? 0 : ret; + } ++ ++ulong ++get_subsys_private(char *kset_name, char *target_name) ++{ ++ ulong kset_addr, kset_list, name_addr, private = 0; ++ struct list_data list_data, *ld; ++ char buf[32]; ++ int i, cnt; ++ ++ if (!symbol_exists(kset_name)) ++ return 0; ++ ++ ld = &list_data; ++ BZERO(ld, sizeof(struct list_data)); ++ ++ get_symbol_data(kset_name, sizeof(ulong), &kset_addr); ++ readmem(kset_addr + OFFSET(kset_list), KVADDR, &kset_list, ++ sizeof(ulong), "kset.list", FAULT_ON_ERROR); ++ ++ ld->flags |= LIST_ALLOCATE; ++ ld->start = kset_list; ++ ld->end = kset_addr + OFFSET(kset_list); ++ ld->list_head_offset = OFFSET(kobject_entry); ++ ++ cnt = do_list(ld); ++ ++ for (i = 0; i < cnt; i++) { ++ readmem(ld->list_ptr[i] + OFFSET(kobject_name), KVADDR, &name_addr, ++ sizeof(ulong), "kobject.name", FAULT_ON_ERROR); ++ read_string(name_addr, buf, sizeof(buf)-1); ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "kobject: %lx name: %s\n", ld->list_ptr[i], buf); ++ if (STREQ(buf, target_name)) { ++ /* entry is subsys_private.subsys.kobj. See bus_to_subsys(). */ ++ private = ld->list_ptr[i] - OFFSET(kset_kobj) ++ - OFFSET(subsys_private_subsys); ++ break; ++ } ++ } ++ FREEBUF(ld->list_ptr); ++ ++ return private; ++} +-- +2.37.1 + diff --git a/0003-Fix-kmem-v-option-displaying-no-regions-on-Linux-6.3.patch b/0003-Fix-kmem-v-option-displaying-no-regions-on-Linux-6.3.patch new file mode 100644 index 0000000..8d9a657 --- /dev/null +++ b/0003-Fix-kmem-v-option-displaying-no-regions-on-Linux-6.3.patch @@ -0,0 +1,86 @@ +From 342cf340ed0386880fe2a3115d6bef32eabb511b Mon Sep 17 00:00:00 2001 +From: Kazuhito Hagio +Date: Thu, 18 May 2023 11:48:28 +0900 +Subject: [PATCH 03/13] Fix "kmem -v" option displaying no regions on Linux 6.3 + and later + +Kernel commit 869176a09606 ("mm/vmalloc.c: add flags to mark vm_map_ram +area"), which is contained in Linux 6.3 and later, added "flags" member +to struct vmap_area. This was the revival of the "flags" member as +kernel commit 688fcbfc06e4 had eliminated it before. + +As a result, crash started to use the old procedure using the member and +displays no vmalloc'd regions, because it does not have the same flag +value as the old one. + + crash> kmem -v + VMAP_AREA VM_STRUCT ADDRESS RANGE SIZE + crash> + +To fix this, also check if vmap_area.purge_list exists, which was +introduced with the flags and removed later, to determine that the flags +member is the old one. + +Related vmap_area history: + v2.6.28 db64fe02258f introduced vmap_area with flags and purge_list + v5.4 688fcbfc06e4 removed flags + v5.11 96e2db456135 removed purge_list + v6.3 869176a09606 added flags again + +Signed-off-by: Kazuhito Hagio +Signed-off-by: Lianbo Jiang +--- + defs.h | 1 + + memory.c | 4 +++- + symbols.c | 1 + + 3 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/defs.h b/defs.h +index 21cc760444d1..bfa07c3f5150 100644 +--- a/defs.h ++++ b/defs.h +@@ -2216,6 +2216,7 @@ struct offset_table { /* stash of commonly-used offsets */ + long in6_addr_in6_u; + long kset_kobj; + long subsys_private_subsys; ++ long vmap_area_purge_list; + }; + + struct size_table { /* stash of commonly-used sizes */ +diff --git a/memory.c b/memory.c +index 953fc380c03c..15fa8b2f08f1 100644 +--- a/memory.c ++++ b/memory.c +@@ -429,6 +429,7 @@ vm_init(void) + MEMBER_OFFSET_INIT(vmap_area_vm, "vmap_area", "vm"); + if (INVALID_MEMBER(vmap_area_vm)) + MEMBER_OFFSET_INIT(vmap_area_vm, "vmap_area", "private"); ++ MEMBER_OFFSET_INIT(vmap_area_purge_list, "vmap_area", "purge_list"); + STRUCT_SIZE_INIT(vmap_area, "vmap_area"); + if (VALID_MEMBER(vmap_area_va_start) && + VALID_MEMBER(vmap_area_va_end) && +@@ -9063,7 +9064,8 @@ dump_vmap_area(struct meminfo *vi) + readmem(ld->list_ptr[i], KVADDR, vmap_area_buf, + SIZE(vmap_area), "vmap_area struct", FAULT_ON_ERROR); + +- if (VALID_MEMBER(vmap_area_flags)) { ++ if (VALID_MEMBER(vmap_area_flags) && ++ VALID_MEMBER(vmap_area_purge_list)) { + flags = ULONG(vmap_area_buf + OFFSET(vmap_area_flags)); + if (flags != VM_VM_AREA) + continue; +diff --git a/symbols.c b/symbols.c +index f0721023816d..7b1d59203b90 100644 +--- a/symbols.c ++++ b/symbols.c +@@ -9169,6 +9169,7 @@ dump_offset_table(char *spec, ulong makestruct) + OFFSET(vmap_area_vm)); + fprintf(fp, " vmap_area_flags: %ld\n", + OFFSET(vmap_area_flags)); ++ fprintf(fp, " vmap_area_purge_list: %ld\n", OFFSET(vmap_area_purge_list)); + + fprintf(fp, " module_size_of_struct: %ld\n", + OFFSET(module_size_of_struct)); +-- +2.37.1 + diff --git a/0004-arm64-x86_64-Enhance-vtop-command-to-show-zero_pfn-i.patch b/0004-arm64-x86_64-Enhance-vtop-command-to-show-zero_pfn-i.patch new file mode 100644 index 0000000..55464d3 --- /dev/null +++ b/0004-arm64-x86_64-Enhance-vtop-command-to-show-zero_pfn-i.patch @@ -0,0 +1,225 @@ +From a0eceb041dfa248d66f9f9a455106184b7823bec Mon Sep 17 00:00:00 2001 +From: Rongwei Wang +Date: Mon, 29 May 2023 19:55:51 +0800 +Subject: [PATCH 04/13] arm64/x86_64: Enhance "vtop" command to show zero_pfn + information + +Enhance the "vtop" command to show "ZERO PAGE" information when PTE or +PMD has attached to {huge_}zero_pfn. For example: + + crash> vtop -c 13674 ffff8917e000 + VIRTUAL PHYSICAL + ffff8917e000 836e71000 + + PAGE DIRECTORY: ffff000802f8d000 + PGD: ffff000802f8dff8 => 884e29003 + PUD: ffff000844e29ff0 => 884e93003 + PMD: ffff000844e93240 => 840413003 + PTE: ffff000800413bf0 => 160000836e71fc3 + PAGE: 836e71000 (ZERO PAGE) + ... + +Hugepage case: + crash> vtop -c 14538 ffff95800000 + VIRTUAL PHYSICAL + ffff95800000 910c00000 + + PAGE DIRECTORY: ffff000801fa0000 + PGD: ffff000801fa0ff8 => 884f53003 + PUD: ffff000844f53ff0 => 8426cb003 + PMD: ffff0008026cb560 => 60000910c00fc1 + PAGE: 910c00000 (2MB, ZERO PAGE) + ... + +Note that +1. support displaying zero page only for THP (except for 1G THP) +2. do not support hugetlb cases. + +Signed-off-by: Rongwei Wang +Signed-off-by: Kazuhito Hagio +Signed-off-by: Lianbo Jiang +--- + arm64.c | 24 ++++++++++++++++-------- + defs.h | 5 +++++ + memory.c | 23 +++++++++++++++++++++++ + x86_64.c | 9 +++++---- + 4 files changed, 49 insertions(+), 12 deletions(-) + +diff --git a/arm64.c b/arm64.c +index 56fb841f43f8..efbdccbec9d3 100644 +--- a/arm64.c ++++ b/arm64.c +@@ -1787,7 +1787,8 @@ arm64_vtop_2level_64k(ulong pgd, ulong vaddr, physaddr_t *paddr, int verbose) + if ((pgd_val & PMD_TYPE_MASK) == PMD_TYPE_SECT) { + ulong sectionbase = (pgd_val & SECTION_PAGE_MASK_512MB) & PHYS_MASK; + if (verbose) { +- fprintf(fp, " PAGE: %lx (512MB)\n\n", sectionbase); ++ fprintf(fp, " PAGE: %lx (512MB%s)\n\n", sectionbase, ++ IS_ZEROPAGE(sectionbase) ? ", ZERO PAGE" : ""); + arm64_translate_pte(pgd_val, 0, 0); + } + *paddr = sectionbase + (vaddr & ~SECTION_PAGE_MASK_512MB); +@@ -1806,7 +1807,8 @@ arm64_vtop_2level_64k(ulong pgd, ulong vaddr, physaddr_t *paddr, int verbose) + if (pte_val & PTE_VALID) { + *paddr = (PAGEBASE(pte_val) & PHYS_MASK) + PAGEOFFSET(vaddr); + if (verbose) { +- fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr)); ++ fprintf(fp, " PAGE: %lx %s\n\n", PAGEBASE(*paddr), ++ IS_ZEROPAGE(PAGEBASE(*paddr)) ? "(ZERO PAGE)" : ""); + arm64_translate_pte(pte_val, 0, 0); + } + } else { +@@ -1859,7 +1861,8 @@ arm64_vtop_3level_64k(ulong pgd, ulong vaddr, physaddr_t *paddr, int verbose) + if ((pmd_val & PMD_TYPE_MASK) == PMD_TYPE_SECT) { + ulong sectionbase = PTE_TO_PHYS(pmd_val) & SECTION_PAGE_MASK_512MB; + if (verbose) { +- fprintf(fp, " PAGE: %lx (512MB)\n\n", sectionbase); ++ fprintf(fp, " PAGE: %lx (512MB%s)\n\n", sectionbase, ++ IS_ZEROPAGE(sectionbase) ? ", ZERO PAGE" : ""); + arm64_translate_pte(pmd_val, 0, 0); + } + *paddr = sectionbase + (vaddr & ~SECTION_PAGE_MASK_512MB); +@@ -1878,7 +1881,8 @@ arm64_vtop_3level_64k(ulong pgd, ulong vaddr, physaddr_t *paddr, int verbose) + if (pte_val & PTE_VALID) { + *paddr = PTE_TO_PHYS(pte_val) + PAGEOFFSET(vaddr); + if (verbose) { +- fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr)); ++ fprintf(fp, " PAGE: %lx %s\n\n", PAGEBASE(*paddr), ++ IS_ZEROPAGE(PAGEBASE(*paddr)) ? "(ZERO PAGE)" : ""); + arm64_translate_pte(pte_val, 0, 0); + } + } else { +@@ -1940,7 +1944,8 @@ arm64_vtop_3level_4k(ulong pgd, ulong vaddr, physaddr_t *paddr, int verbose) + if ((pmd_val & PMD_TYPE_MASK) == PMD_TYPE_SECT) { + ulong sectionbase = (pmd_val & SECTION_PAGE_MASK_2MB) & PHYS_MASK; + if (verbose) { +- fprintf(fp, " PAGE: %lx (2MB)\n\n", sectionbase); ++ fprintf(fp, " PAGE: %lx (2MB%s)\n\n", sectionbase, ++ IS_ZEROPAGE(sectionbase) ? ", ZERO PAGE" : ""); + arm64_translate_pte(pmd_val, 0, 0); + } + *paddr = sectionbase + (vaddr & ~SECTION_PAGE_MASK_2MB); +@@ -1959,7 +1964,8 @@ arm64_vtop_3level_4k(ulong pgd, ulong vaddr, physaddr_t *paddr, int verbose) + if (pte_val & PTE_VALID) { + *paddr = (PAGEBASE(pte_val) & PHYS_MASK) + PAGEOFFSET(vaddr); + if (verbose) { +- fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr)); ++ fprintf(fp, " PAGE: %lx %s\n\n", PAGEBASE(*paddr), ++ IS_ZEROPAGE(PAGEBASE(*paddr)) ? "(ZERO PAGE)" : ""); + arm64_translate_pte(pte_val, 0, 0); + } + } else { +@@ -2029,7 +2035,8 @@ arm64_vtop_4level_4k(ulong pgd, ulong vaddr, physaddr_t *paddr, int verbose) + if ((pmd_val & PMD_TYPE_MASK) == PMD_TYPE_SECT) { + ulong sectionbase = (pmd_val & SECTION_PAGE_MASK_2MB) & PHYS_MASK; + if (verbose) { +- fprintf(fp, " PAGE: %lx (2MB)\n\n", sectionbase); ++ fprintf(fp, " PAGE: %lx (2MB%s)\n\n", sectionbase, ++ IS_ZEROPAGE(sectionbase) ? ", ZERO PAGE" : ""); + arm64_translate_pte(pmd_val, 0, 0); + } + *paddr = sectionbase + (vaddr & ~SECTION_PAGE_MASK_2MB); +@@ -2048,7 +2055,8 @@ arm64_vtop_4level_4k(ulong pgd, ulong vaddr, physaddr_t *paddr, int verbose) + if (pte_val & PTE_VALID) { + *paddr = (PAGEBASE(pte_val) & PHYS_MASK) + PAGEOFFSET(vaddr); + if (verbose) { +- fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr)); ++ fprintf(fp, " PAGE: %lx %s\n\n", PAGEBASE(*paddr), ++ IS_ZEROPAGE(PAGEBASE(*paddr)) ? "(ZERO PAGE)" : ""); + arm64_translate_pte(pte_val, 0, 0); + } + } else { +diff --git a/defs.h b/defs.h +index bfa07c3f5150..7d8bb8ab3de1 100644 +--- a/defs.h ++++ b/defs.h +@@ -2619,6 +2619,8 @@ struct vm_table { /* kernel VM-related data */ + char *name; + } *pageflags_data; + ulong max_mem_section_nr; ++ ulong zero_paddr; ++ ulong huge_zero_paddr; + }; + + #define NODES (0x1) +@@ -3000,6 +3002,9 @@ struct load_module { + #define VIRTPAGEBASE(X) (((ulong)(X)) & (ulong)machdep->pagemask) + #define PHYSPAGEBASE(X) (((physaddr_t)(X)) & (physaddr_t)machdep->pagemask) + ++#define IS_ZEROPAGE(paddr) ((paddr) == vt->zero_paddr || \ ++ (paddr) == vt->huge_zero_paddr) ++ + /* + * Sparse memory stuff + * These must follow the definitions in the kernel mmzone.h +diff --git a/memory.c b/memory.c +index 15fa8b2f08f1..ea3005a5c01f 100644 +--- a/memory.c ++++ b/memory.c +@@ -1209,6 +1209,27 @@ vm_init(void) + machdep->memory_size())); + vt->paddr_prlen = strlen(buf); + ++ vt->zero_paddr = ~0UL; ++ if (kernel_symbol_exists("zero_pfn")) { ++ ulong zero_pfn; ++ ++ if (readmem(symbol_value("zero_pfn"), KVADDR, ++ &zero_pfn, sizeof(zero_pfn), ++ "read zero_pfn", QUIET|RETURN_ON_ERROR)) ++ vt->zero_paddr = zero_pfn << PAGESHIFT(); ++ } ++ ++ vt->huge_zero_paddr = ~0UL; ++ if (kernel_symbol_exists("huge_zero_pfn")) { ++ ulong huge_zero_pfn; ++ ++ if (readmem(symbol_value("huge_zero_pfn"), KVADDR, ++ &huge_zero_pfn, sizeof(huge_zero_pfn), ++ "read huge_zero_pfn", QUIET|RETURN_ON_ERROR) && ++ huge_zero_pfn != ~0UL) ++ vt->huge_zero_paddr = huge_zero_pfn << PAGESHIFT(); ++ } ++ + if (vt->flags & PERCPU_KMALLOC_V1) + vt->dump_kmem_cache = dump_kmem_cache_percpu_v1; + else if (vt->flags & PERCPU_KMALLOC_V2) +@@ -14065,6 +14086,8 @@ dump_vm_table(int verbose) + } else { + fprintf(fp, " node_online_map: (unused)\n"); + } ++ fprintf(fp, " zero_paddr: %lx\n", vt->zero_paddr); ++ fprintf(fp, " huge_zero_paddr: %lx\n", vt->huge_zero_paddr); + fprintf(fp, " nr_vm_stat_items: %d\n", vt->nr_vm_stat_items); + fprintf(fp, " vm_stat_items: %s", (vt->flags & VM_STAT) ? + "\n" : "(not used)\n"); +diff --git a/x86_64.c b/x86_64.c +index 5019c69e452e..693a08bea758 100644 +--- a/x86_64.c ++++ b/x86_64.c +@@ -2114,8 +2114,9 @@ x86_64_uvtop_level4(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, in + goto no_upage; + if (pmd_pte & _PAGE_PSE) { + if (verbose) { +- fprintf(fp, " PAGE: %lx (2MB)\n\n", +- PAGEBASE(pmd_pte) & PHYSICAL_PAGE_MASK); ++ fprintf(fp, " PAGE: %lx (2MB%s)\n\n", ++ PAGEBASE(pmd_pte) & PHYSICAL_PAGE_MASK, ++ IS_ZEROPAGE(PAGEBASE(pmd_pte) & PHYSICAL_PAGE_MASK) ? ", ZERO PAGE" : ""); + x86_64_translate_pte(pmd_pte, 0, 0); + } + +@@ -2143,8 +2144,8 @@ x86_64_uvtop_level4(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, in + *paddr = (PAGEBASE(pte) & PHYSICAL_PAGE_MASK) + PAGEOFFSET(uvaddr); + + if (verbose) { +- fprintf(fp, " PAGE: %lx\n\n", +- PAGEBASE(*paddr) & PHYSICAL_PAGE_MASK); ++ fprintf(fp, " PAGE: %lx %s\n\n", PAGEBASE(*paddr) & PHYSICAL_PAGE_MASK, ++ IS_ZEROPAGE(PAGEBASE(*paddr) & PHYSICAL_PAGE_MASK) ? "(ZERO PAGE)" : ""); + x86_64_translate_pte(pte, 0, 0); + } + +-- +2.37.1 + diff --git a/0005-diskdump-netdump-fix-segmentation-fault-caused-by-fa.patch b/0005-diskdump-netdump-fix-segmentation-fault-caused-by-fa.patch new file mode 100644 index 0000000..e9e9dd0 --- /dev/null +++ b/0005-diskdump-netdump-fix-segmentation-fault-caused-by-fa.patch @@ -0,0 +1,165 @@ +From db8c030857b4e318728c51c20da687906c109d0d Mon Sep 17 00:00:00 2001 +From: HATAYAMA Daisuke +Date: Tue, 30 May 2023 19:38:34 +0900 +Subject: [PATCH 05/13] diskdump/netdump: fix segmentation fault caused by + failure of stopping CPUs + +There's no NMI on ARM. Hence, stopping the non-panicking CPUs from the +panicking CPU via IPI can fail easily if interrupts are being masked +in those moment. Moreover, crash_notes are not initialized for such +unstopped CPUs and the corresponding NT_PRSTATUS notes are not +attached to vmcore. However, crash utility never takes it +consideration such uninitialized crash_notes and then ends with +mapping different NT_PRSTATUS to actually unstopped CPUs. This corrupt +mapping can result crash utility into segmentation fault in the +operations where register values in NT_PRSTATUS notes are used. + +For example: + + crash> bt 1408 + PID: 1408 TASK: ffff000003e22200 CPU: 2 COMMAND: "repro" + Segmentation fault (core dumped) + + crash> help -D + diskdump_data: + filename: 127.0.0.1-2023-05-26-02:21:27/vmcore-ld1 + flags: 46 (KDUMP_CMPRS_LOCAL|ERROR_EXCLUDED|LZO_SUPPORTED) + ...snip... + notes_buf: 1815df0 + num_vmcoredd_notes: 0 + num_prstatus_notes: 5 + notes[0]: 1815df0 (NT_PRSTATUS) + si.signo: 0 si.code: 0 si.errno: 0 + ...snip... + PSTATE: 80400005 FPVALID: 00000000 + notes[4]: 1808f10 (NT_PRSTATUS) + Segmentation fault (core dumped) + +To fix this issue, let's map NT_PRSTATUS to some CPU only if the +corresponding crash_notes is checked to be initialized. + +[ kh: moved existence check for crash_notes out of the loop ] + +Signed-off-by: HATAYAMA Daisuke +Signed-off-by: Kazuhito Hagio +Signed-off-by: Lianbo Jiang +--- + defs.h | 1 + + diskdump.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- + netdump.c | 7 ++++++- + 3 files changed, 51 insertions(+), 2 deletions(-) + +diff --git a/defs.h b/defs.h +index 7d8bb8ab3de1..6520d2f13f48 100644 +--- a/defs.h ++++ b/defs.h +@@ -7118,6 +7118,7 @@ int dumpfile_is_split(void); + void show_split_dumpfiles(void); + void x86_process_elf_notes(void *, unsigned long); + void *diskdump_get_prstatus_percpu(int); ++int have_crash_notes(int cpu); + void map_cpus_to_prstatus_kdump_cmprs(void); + void diskdump_display_regs(int, FILE *); + void process_elf32_notes(void *, ulong); +diff --git a/diskdump.c b/diskdump.c +index 94bca4ded572..2c284ff3f97f 100644 +--- a/diskdump.c ++++ b/diskdump.c +@@ -101,12 +101,54 @@ int dumpfile_is_split(void) + return KDUMP_SPLIT(); + } + ++int have_crash_notes(int cpu) ++{ ++ ulong crash_notes, notes_ptr; ++ char *buf, *p; ++ Elf64_Nhdr *note = NULL; ++ ++ if (!readmem(symbol_value("crash_notes"), KVADDR, &crash_notes, ++ sizeof(crash_notes), "crash_notes", RETURN_ON_ERROR)) { ++ error(WARNING, "cannot read \"crash_notes\"\n"); ++ return FALSE; ++ } ++ ++ if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) ++ notes_ptr = crash_notes + kt->__per_cpu_offset[cpu]; ++ else ++ notes_ptr = crash_notes; ++ ++ buf = GETBUF(SIZE(note_buf)); ++ ++ if (!readmem(notes_ptr, KVADDR, buf, ++ SIZE(note_buf), "note_buf_t", RETURN_ON_ERROR)) { ++ error(WARNING, "cpu %d: cannot read NT_PRSTATUS note\n", cpu); ++ return FALSE; ++ } ++ ++ note = (Elf64_Nhdr *)buf; ++ p = buf + sizeof(Elf64_Nhdr); ++ ++ if (note->n_type != NT_PRSTATUS) { ++ error(WARNING, "cpu %d: invalid NT_PRSTATUS note (n_type != NT_PRSTATUS)\n", cpu); ++ return FALSE; ++ } ++ ++ if (!STRNEQ(p, "CORE")) { ++ error(WARNING, "cpu %d: invalid NT_PRSTATUS note (name != \"CORE\")\n", cpu); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ + void + map_cpus_to_prstatus_kdump_cmprs(void) + { + void **nt_ptr; + int online, i, j, nrcpus; + size_t size; ++ int crash_notes_exists; + + if (pc->flags2 & QEMU_MEM_DUMP_COMPRESSED) /* notes exist for all cpus */ + goto resize_note_pointers; +@@ -129,9 +171,10 @@ map_cpus_to_prstatus_kdump_cmprs(void) + * Re-populate the array with the notes mapping to online cpus + */ + nrcpus = (kt->kernel_NR_CPUS ? kt->kernel_NR_CPUS : NR_CPUS); ++ crash_notes_exists = kernel_symbol_exists("crash_notes"); + + for (i = 0, j = 0; i < nrcpus; i++) { +- if (in_cpu_map(ONLINE_MAP, i)) { ++ if (in_cpu_map(ONLINE_MAP, i) && (!crash_notes_exists || have_crash_notes(i))) { + dd->nt_prstatus_percpu[i] = nt_ptr[j++]; + dd->num_prstatus_notes = + MAX(dd->num_prstatus_notes, i+1); +diff --git a/netdump.c b/netdump.c +index 4eba66cecb55..61ddeaa08831 100644 +--- a/netdump.c ++++ b/netdump.c +@@ -75,6 +75,7 @@ map_cpus_to_prstatus(void) + void **nt_ptr; + int online, i, j, nrcpus; + size_t size; ++ int crash_notes_exists; + + if (pc->flags2 & QEMU_MEM_DUMP_ELF) /* notes exist for all cpus */ + return; +@@ -97,10 +98,14 @@ map_cpus_to_prstatus(void) + * Re-populate the array with the notes mapping to online cpus + */ + nrcpus = (kt->kernel_NR_CPUS ? kt->kernel_NR_CPUS : NR_CPUS); ++ crash_notes_exists = kernel_symbol_exists("crash_notes"); + + for (i = 0, j = 0; i < nrcpus; i++) { +- if (in_cpu_map(ONLINE_MAP, i)) ++ if (in_cpu_map(ONLINE_MAP, i) && (!crash_notes_exists || have_crash_notes(i))) { + nd->nt_prstatus_percpu[i] = nt_ptr[j++]; ++ nd->num_prstatus_notes = ++ MAX(nd->num_prstatus_notes, i+1); ++ } + } + + FREEBUF(nt_ptr); +-- +2.37.1 + diff --git a/0006-Fix-segfault-in-arm64_is_kernel_exception_frame-when.patch b/0006-Fix-segfault-in-arm64_is_kernel_exception_frame-when.patch new file mode 100644 index 0000000..2f62c6b --- /dev/null +++ b/0006-Fix-segfault-in-arm64_is_kernel_exception_frame-when.patch @@ -0,0 +1,62 @@ +From 9868ebc8e648e5791764a51567a23efae7170d9b Mon Sep 17 00:00:00 2001 +From: HATAYAMA Daisuke +Date: Tue, 30 May 2023 19:38:35 +0900 +Subject: [PATCH 06/13] Fix segfault in arm64_is_kernel_exception_frame() when + corrupt stack pointer address is given + +Due to the corrupted mapping fixed by the previous commit, +arm64_is_kernel_exception_frame() can receive invalid stack pointer +address via the 2nd argument; different NT_PRSTATUS contains different +task's stack pointer address. However, macro STACK_OFFSET_TYPE() never +checks if a given address is within the range of the kernel stack of +the corresponding task and hence can result in referring to outside of +bt->stackbuf. + + static int + arm64_is_kernel_exception_frame(struct bt_info *bt, ulong stkptr) + { + struct arm64_pt_regs *regs; + struct machine_specific *ms = machdep->machspec; + + regs = (struct arm64_pt_regs *)&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(stkptr))]; + + => if (INSTACK(regs->sp, bt) && INSTACK(regs->regs[29], bt) && + !(regs->pstate & (0xffffffff00000000ULL | PSR_MODE32_BIT)) && + is_kernel_text(regs->pc) && + is_kernel_text(regs->regs[30] | ms->CONFIG_ARM64_KERNELPACMASK)) { + +To fix this issue, check if the given stack pointer address points to +the range of the kernel stack of the corresponding task, and abort if +it turns out to be invalid. + +Although the corrupted mapping has already been fixed, this fix is +still needed because corrupt stack pointer address can still be passed +here from different reasons. Consider, for example, that data on the +kernel stack can be modified abnormally due to any kernel bugs or +hardware issues. + +Signed-off-by: HATAYAMA Daisuke +Signed-off-by: Lianbo Jiang +--- + defs.h | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/defs.h b/defs.h +index 6520d2f13f48..11fdc17e60d0 100644 +--- a/defs.h ++++ b/defs.h +@@ -976,7 +976,10 @@ struct bt_info { + + #define STACK_OFFSET_TYPE(OFF) \ + (((ulong)(OFF) > STACKSIZE()) ? \ +- (ulong)((ulong)(OFF) - (ulong)(bt->stackbase)) : (ulong)(OFF)) ++ (((ulong)(OFF) < (ulong)(bt->stackbase) || (ulong)(OFF) >= (ulong)(bt->stackbase) + STACKSIZE()) ? \ ++ error(FATAL, "invalid stack pointer is given\n") : \ ++ (ulong)((ulong)(OFF) - (ulong)(bt->stackbase))) : \ ++ (ulong)(OFF)) + + #define GET_STACK_ULONG(OFF) \ + *((ulong *)((char *)(&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(OFF))]))) +-- +2.37.1 + diff --git a/0007-Output-prompt-when-stdin-is-not-a-TTY.patch b/0007-Output-prompt-when-stdin-is-not-a-TTY.patch new file mode 100644 index 0000000..7590e7f --- /dev/null +++ b/0007-Output-prompt-when-stdin-is-not-a-TTY.patch @@ -0,0 +1,71 @@ +From 8527bbff71cbdfd90a67d5cec4a1d94156e6bf13 Mon Sep 17 00:00:00 2001 +From: Hsin-Yi Wang +Date: Wed, 31 May 2023 14:01:36 +0800 +Subject: [PATCH 07/13] Output prompt when stdin is not a TTY + +When stdin is not a TTY, prompt ("crash> ") won't be displayed. If +another process interact with crash with piped stdin/stdout, it will not +get the prompt as a delimiter. + +Compared to other debugger like gdb, crash seems intended to give a +prompt in this case in the beginning of process_command_line(). It +checks if pc->flags does NOT have any of +READLINE|SILENT|CMDLINE_IFILE|RCHOME_IFILE|RCLOCAL_IFILE, a +prompt should be printed. The check will never be true since READLINE is +set in setup_environment() unconditionally. + +It makes more sense to change the READLINE flag in the check to TTY +instead. Besides this change, the prompt in process_command_line() should +only be print when it's not in the middle of processing the input file +recovering from a previous FATAL command, because the prompt will be +displayed by the exec_input_file(). + +Additionally, when stdin is not TTY, repeat the command line from user +after prompt, which can give more context. + +The prompt and command line can be opt out by using the silent (-s) flag. + +Signed-off-by: Hsin-Yi Wang +Signed-off-by: Lianbo Jiang +--- + cmdline.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/cmdline.c b/cmdline.c +index ded6551c2597..b7f919ae2279 100644 +--- a/cmdline.c ++++ b/cmdline.c +@@ -64,8 +64,8 @@ process_command_line(void) + fp = stdout; + BZERO(pc->command_line, BUFSIZE); + +- if (!(pc->flags & +- (READLINE|SILENT|CMDLINE_IFILE|RCHOME_IFILE|RCLOCAL_IFILE))) ++ if (!pc->ifile_in_progress && !(pc->flags & ++ (TTY|SILENT|CMDLINE_IFILE|RCHOME_IFILE|RCLOCAL_IFILE))) + fprintf(fp, "%s", pc->prompt); + fflush(fp); + +@@ -136,12 +136,16 @@ process_command_line(void) + add_history(pc->command_line); + + check_special_handling(pc->command_line); +- } else { +- if (fgets(pc->command_line, BUFSIZE-1, stdin) == NULL) ++ } else { ++ if (fgets(pc->command_line, BUFSIZE-1, stdin) == NULL) + clean_exit(1); ++ if (!(pc->flags & SILENT)) { ++ fprintf(fp, "%s", pc->command_line); ++ fflush(fp); ++ } + clean_line(pc->command_line); + strcpy(pc->orig_line, pc->command_line); +- } ++ } + + /* + * First clean out all linefeeds and leading/trailing spaces. +-- +2.37.1 + diff --git a/0008-x86_64-Fix-bt-command-printing-stale-entries-on-Linu.patch b/0008-x86_64-Fix-bt-command-printing-stale-entries-on-Linu.patch new file mode 100644 index 0000000..27440d1 --- /dev/null +++ b/0008-x86_64-Fix-bt-command-printing-stale-entries-on-Linu.patch @@ -0,0 +1,345 @@ +From 77d8621876c1c6a3a25b91e464ba588a542485fb Mon Sep 17 00:00:00 2001 +From: Kazuhito Hagio +Date: Thu, 18 May 2023 16:53:54 +0900 +Subject: [PATCH 08/13] x86_64: Fix "bt" command printing stale entries on + Linux 6.4 and later + +Kernel commit fb799447ae29 ("x86,objtool: Split UNWIND_HINT_EMPTY in +two"), which is contained in Linux 6.4 and later kernels, changed +ORC_TYPE_CALL macro from 0 to 2. As a result, the "bt" command cannot +use ORC entries, and can display stale entries in a call trace. + + crash> bt 1 + PID: 1 TASK: ffff93cd06294180 CPU: 51 COMMAND: "systemd" + #0 [ffffb72bc00cbc98] __schedule at ffffffff86e52aae + #1 [ffffb72bc00cbd00] schedule at ffffffff86e52f6a + #2 [ffffb72bc00cbd18] schedule_hrtimeout_range_clock at ffffffff86e58ef5 + #3 [ffffb72bc00cbd88] ep_poll at ffffffff8669624d + #4 [ffffb72bc00cbe28] do_epoll_wait at ffffffff86696371 + #5 [ffffb72bc00cbe30] do_timerfd_settime at ffffffff8669902b << + #6 [ffffb72bc00cbe60] __x64_sys_epoll_wait at ffffffff86696bf0 + #7 [ffffb72bc00cbeb0] do_syscall_64 at ffffffff86e3feb9 + #8 [ffffb72bc00cbee0] __task_pid_nr_ns at ffffffff863330d7 << + #9 [ffffb72bc00cbf08] syscall_exit_to_user_mode at ffffffff86e466b2 << stale entries + #10 [ffffb72bc00cbf18] do_syscall_64 at ffffffff86e3fec9 << + #11 [ffffb72bc00cbf50] entry_SYSCALL_64_after_hwframe at ffffffff870000aa + +Also, kernel commit ffb1b4a41016 added a member to struct orc_entry. +Although this does not affect the crash's unwinder, its debugging +information can be displayed incorrectly. + +To fix these, +(1) introduce "kernel_orc_entry_6_4" structure corresponding to 6.4 and + abstruction layer "orc_entry" structure in crash, +(2) switch ORC_TYPE_CALL to 2 or 0 with kernel's orc_entry structure. + +Related orc_entry history: + v4.14 39358a033b2e introduced struct orc_entry + v4.19 d31a580266ee added orc_entry.end member + v6.3 ffb1b4a41016 added orc_entry.signal member + v6.4 fb799447ae29 removed end member and changed type member to 3 bits + +Signed-off-by: Kazuhito Hagio +Signed-off-by: Lianbo Jiang +--- + defs.h | 28 ++++++++++++- + x86_64.c | 119 +++++++++++++++++++++++++++++++++++++++++++------------ + 2 files changed, 119 insertions(+), 28 deletions(-) + +diff --git a/defs.h b/defs.h +index 11fdc17e60d0..bfda0c48d37b 100644 +--- a/defs.h ++++ b/defs.h +@@ -6363,9 +6363,29 @@ typedef struct __attribute__((__packed__)) { + unsigned int sp_reg:4; + unsigned int bp_reg:4; + unsigned int type:2; ++ unsigned int signal:1; + unsigned int end:1; + } kernel_orc_entry; + ++typedef struct __attribute__((__packed__)) { ++ signed short sp_offset; ++ signed short bp_offset; ++ unsigned int sp_reg:4; ++ unsigned int bp_reg:4; ++ unsigned int type:3; ++ unsigned int signal:1; ++} kernel_orc_entry_6_4; ++ ++typedef struct orc_entry { ++ signed short sp_offset; ++ signed short bp_offset; ++ unsigned int sp_reg; ++ unsigned int bp_reg; ++ unsigned int type; ++ unsigned int signal; ++ unsigned int end; ++} orc_entry; ++ + struct ORC_data { + int module_ORC; + uint lookup_num_blocks; +@@ -6376,10 +6396,13 @@ struct ORC_data { + ulong orc_lookup; + ulong ip_entry; + ulong orc_entry; +- kernel_orc_entry kernel_orc_entry; ++ orc_entry orc_entry_data; ++ int has_signal; ++ int has_end; + }; + +-#define ORC_TYPE_CALL 0 ++#define ORC_TYPE_CALL ((machdep->flags & ORC_6_4) ? 2 : 0) ++/* The below entries are not used and must be updated if we use them. */ + #define ORC_TYPE_REGS 1 + #define ORC_TYPE_REGS_IRET 2 + #define UNWIND_HINT_TYPE_SAVE 3 +@@ -6456,6 +6479,7 @@ struct machine_specific { + #define ORC (0x4000) + #define KPTI (0x8000) + #define L1TF (0x10000) ++#define ORC_6_4 (0x20000) + + #define VM_FLAGS (VM_ORIG|VM_2_6_11|VM_XEN|VM_XEN_RHEL4|VM_5LEVEL) + +diff --git a/x86_64.c b/x86_64.c +index 693a08bea758..87e87ae6e1e8 100644 +--- a/x86_64.c ++++ b/x86_64.c +@@ -132,9 +132,9 @@ static void GART_init(void); + static void x86_64_exception_stacks_init(void); + static int in_START_KERNEL_map(ulong); + static ulong orc_ip(ulong); +-static kernel_orc_entry *__orc_find(ulong, ulong, uint, ulong); +-static kernel_orc_entry *orc_find(ulong); +-static kernel_orc_entry *orc_module_find(ulong); ++static orc_entry *__orc_find(ulong, ulong, uint, ulong); ++static orc_entry *orc_find(ulong); ++static orc_entry *orc_module_find(ulong); + static ulong ip_table_to_vaddr(ulong); + static void orc_dump(ulong); + +@@ -806,6 +806,8 @@ x86_64_dump_machdep_table(ulong arg) + fprintf(fp, "%sFRAMESIZE_DEBUG", others++ ? "|" : ""); + if (machdep->flags & ORC) + fprintf(fp, "%sORC", others++ ? "|" : ""); ++ if (machdep->flags & ORC_6_4) ++ fprintf(fp, "%sORC_6_4", others++ ? "|" : ""); + if (machdep->flags & FRAMEPOINTER) + fprintf(fp, "%sFRAMEPOINTER", others++ ? "|" : ""); + if (machdep->flags & GART_REGION) +@@ -980,6 +982,8 @@ x86_64_dump_machdep_table(ulong arg) + fprintf(fp, " ORC_data: %s", machdep->flags & ORC ? "\n" : "(unused)\n"); + if (machdep->flags & ORC) { + fprintf(fp, " module_ORC: %s\n", ms->orc.module_ORC ? "TRUE" : "FALSE"); ++ fprintf(fp, " has_signal: %s\n", ms->orc.has_signal ? "TRUE" : "FALSE"); ++ fprintf(fp, " has_end: %s\n", ms->orc.has_end ? "TRUE" : "FALSE"); + fprintf(fp, " lookup_num_blocks: %d\n", ms->orc.lookup_num_blocks); + fprintf(fp, " __start_orc_unwind_ip: %lx\n", ms->orc.__start_orc_unwind_ip); + fprintf(fp, " __stop_orc_unwind_ip: %lx\n", ms->orc.__stop_orc_unwind_ip); +@@ -988,14 +992,18 @@ x86_64_dump_machdep_table(ulong arg) + fprintf(fp, " orc_lookup: %lx\n", ms->orc.orc_lookup); + fprintf(fp, " ip_entry: %lx\n", ms->orc.ip_entry); + fprintf(fp, " orc_entry: %lx\n", ms->orc.orc_entry); +- fprintf(fp, " kernel_orc_entry:\n"); +- fprintf(fp, " sp_offset: %d\n", ms->orc.kernel_orc_entry.sp_offset); +- fprintf(fp, " bp_offset: %d\n", ms->orc.kernel_orc_entry.bp_offset); +- fprintf(fp, " sp_reg: %d\n", ms->orc.kernel_orc_entry.sp_reg); +- fprintf(fp, " bp_reg: %d\n", ms->orc.kernel_orc_entry.bp_reg); +- fprintf(fp, " type: %d\n", ms->orc.kernel_orc_entry.type); +- if (MEMBER_EXISTS("orc_entry", "end")) +- fprintf(fp, " end: %d\n", ms->orc.kernel_orc_entry.end); ++ fprintf(fp, " orc_entry_data:\n"); ++ fprintf(fp, " sp_offset: %d\n", ms->orc.orc_entry_data.sp_offset); ++ fprintf(fp, " bp_offset: %d\n", ms->orc.orc_entry_data.bp_offset); ++ fprintf(fp, " sp_reg: %d\n", ms->orc.orc_entry_data.sp_reg); ++ fprintf(fp, " bp_reg: %d\n", ms->orc.orc_entry_data.bp_reg); ++ fprintf(fp, " type: %d\n", ms->orc.orc_entry_data.type); ++ if (ms->orc.has_signal) ++ fprintf(fp, " signal: %d\n", ms->orc.orc_entry_data.signal); ++ else ++ fprintf(fp, " signal: (n/a)\n"); ++ if (ms->orc.has_end) ++ fprintf(fp, " end: %d\n", ms->orc.orc_entry_data.end); + else + fprintf(fp, " end: (n/a)\n"); + } +@@ -6440,6 +6448,12 @@ x86_64_ORC_init(void) + MEMBER_OFFSET_INIT(inactive_task_frame_bp, "inactive_task_frame", "bp"); + MEMBER_OFFSET_INIT(inactive_task_frame_ret_addr, "inactive_task_frame", "ret_addr"); + ++ orc->has_signal = MEMBER_EXISTS("orc_entry", "signal"); /* added at 6.3 */ ++ orc->has_end = MEMBER_EXISTS("orc_entry", "end"); /* removed at 6.4 */ ++ ++ if (orc->has_signal && !orc->has_end) ++ machdep->flags |= ORC_6_4; ++ + machdep->flags |= ORC; + } + +@@ -8522,7 +8536,7 @@ x86_64_get_framesize(struct bt_info *bt, ulong textaddr, ulong rsp, char *stack_ + int reterror; + int arg_exists; + int exception; +- kernel_orc_entry *korc; ++ orc_entry *korc; + + if (!(bt->flags & BT_FRAMESIZE_DEBUG)) { + if ((bt->flags & BT_FRAMESIZE_IGNORE_MASK) || +@@ -8608,11 +8622,14 @@ x86_64_get_framesize(struct bt_info *bt, ulong textaddr, ulong rsp, char *stack_ + + if ((machdep->flags & ORC) && (korc = orc_find(textaddr))) { + if (CRASHDEBUG(1)) { ++ struct ORC_data *orc = &machdep->machspec->orc; + fprintf(fp, + "rsp: %lx textaddr: %lx -> spo: %d bpo: %d spr: %d bpr: %d type: %d", + rsp, textaddr, korc->sp_offset, korc->bp_offset, + korc->sp_reg, korc->bp_reg, korc->type); +- if (MEMBER_EXISTS("orc_entry", "end")) ++ if (orc->has_signal) ++ fprintf(fp, " signal: %d", korc->signal); ++ if (orc->has_end) + fprintf(fp, " end: %d", korc->end); + fprintf(fp, "\n"); + } +@@ -9118,7 +9135,53 @@ orc_ip(ulong ip) + return (ip + ip_entry); + } + +-static kernel_orc_entry * ++static orc_entry * ++orc_get_entry(struct ORC_data *orc) ++{ ++ struct orc_entry *entry = &orc->orc_entry_data; ++ ++ if (machdep->flags & ORC_6_4) { ++ kernel_orc_entry_6_4 korc; ++ ++ if (!readmem(orc->orc_entry, KVADDR, &korc, sizeof(kernel_orc_entry_6_4), ++ "kernel orc_entry", RETURN_ON_ERROR|QUIET)) ++ return NULL; ++ ++ entry->sp_offset = korc.sp_offset; ++ entry->bp_offset = korc.bp_offset; ++ entry->sp_reg = korc.sp_reg; ++ entry->bp_reg = korc.bp_reg; ++ entry->type = korc.type; ++ entry->signal = korc.signal; ++ } else { ++ kernel_orc_entry korc; ++ ++ if (!readmem(orc->orc_entry, KVADDR, &korc, sizeof(kernel_orc_entry), ++ "kernel orc_entry", RETURN_ON_ERROR|QUIET)) ++ return NULL; ++ ++ entry->sp_offset = korc.sp_offset; ++ entry->bp_offset = korc.bp_offset; ++ entry->sp_reg = korc.sp_reg; ++ entry->bp_reg = korc.bp_reg; ++ entry->type = korc.type; ++ if (orc->has_end) { ++ /* ++ * orc_entry.signal was inserted before orc_entry.end. ++ * see ffb1b4a41016. ++ */ ++ if (orc->has_signal) { ++ entry->signal = korc.signal; ++ entry->end = korc.end; ++ } else ++ entry->end = korc.signal; /* on purpose */ ++ } ++ } ++ ++ return entry; ++} ++ ++static orc_entry * + __orc_find(ulong ip_table_ptr, ulong u_table_ptr, uint num_entries, ulong ip) + { + int index; +@@ -9128,7 +9191,7 @@ __orc_find(ulong ip_table_ptr, ulong u_table_ptr, uint num_entries, ulong ip) + int *ip_table = (int *)ip_table_ptr; + struct ORC_data *orc = &machdep->machspec->orc; + ulong vaddr; +- kernel_orc_entry *korc; ++ orc_entry *korc; + + if (CRASHDEBUG(2)) { + int i, ip_entry; +@@ -9172,18 +9235,20 @@ __orc_find(ulong ip_table_ptr, ulong u_table_ptr, uint num_entries, ulong ip) + + orc->ip_entry = (ulong)found; + orc->orc_entry = u_table_ptr + (index * SIZE(orc_entry)); +- if (!readmem(orc->orc_entry, KVADDR, &orc->kernel_orc_entry, +- sizeof(kernel_orc_entry), "kernel orc_entry", RETURN_ON_ERROR|QUIET)) ++ ++ if (!orc_get_entry(orc)) + return NULL; + +- korc = &orc->kernel_orc_entry; ++ korc = &orc->orc_entry_data; + + if (CRASHDEBUG(2)) { + fprintf(fp, " found: %lx index: %d\n", (ulong)found, index); + fprintf(fp, + " orc_entry: %lx sp_offset: %d bp_offset: %d sp_reg: %d bp_reg: %d type: %d", + orc->orc_entry, korc->sp_offset, korc->bp_offset, korc->sp_reg, korc->bp_reg, korc->type); +- if (MEMBER_EXISTS("orc_entry", "end")) ++ if (orc->has_signal) ++ fprintf(fp, " signal: %d", korc->signal); ++ if (orc->has_end) + fprintf(fp, " end: %d", korc->end); + fprintf(fp, "\n"); + } +@@ -9196,7 +9261,7 @@ __orc_find(ulong ip_table_ptr, ulong u_table_ptr, uint num_entries, ulong ip) + #define LOOKUP_START_IP (unsigned long)kt->stext + #define LOOKUP_STOP_IP (unsigned long)kt->etext + +-static kernel_orc_entry * ++static orc_entry * + orc_find(ulong ip) + { + unsigned int idx, start, stop; +@@ -9266,7 +9331,7 @@ orc_find(ulong ip) + orc->__start_orc_unwind + (start * SIZE(orc_entry)), stop - start, ip); + } + +-static kernel_orc_entry * ++static orc_entry * + orc_module_find(ulong ip) + { + struct load_module *lm; +@@ -9313,7 +9378,7 @@ static void + orc_dump(ulong ip) + { + struct ORC_data *orc = &machdep->machspec->orc; +- kernel_orc_entry *korc; ++ orc_entry *korc; + ulong vaddr, offset; + struct syment *sp, *orig; + +@@ -9336,13 +9401,15 @@ next_in_func: + fprintf(fp, "%s+%ld -> ", sp->name, offset); + else + fprintf(fp, "(unresolved) -> "); +- if (!readmem(orc->orc_entry, KVADDR, &orc->kernel_orc_entry, sizeof(kernel_orc_entry), +- "kernel orc_entry", RETURN_ON_ERROR)) ++ ++ if (!orc_get_entry(orc)) + error(FATAL, "cannot read orc_entry\n"); +- korc = &orc->kernel_orc_entry; ++ korc = &orc->orc_entry_data; + fprintf(fp, "orc: %lx spo: %d bpo: %d spr: %d bpr: %d type: %d", + orc->orc_entry, korc->sp_offset, korc->bp_offset, korc->sp_reg, korc->bp_reg, korc->type); +- if (MEMBER_EXISTS("orc_entry", "end")) ++ if (orc->has_signal) ++ fprintf(fp, " signal: %d", korc->signal); ++ if (orc->has_end) + fprintf(fp, " end: %d", korc->end); + fprintf(fp, "\n"); + +-- +2.37.1 + diff --git a/0009-Fix-invalid-structure-size-error-during-crash-startu.patch b/0009-Fix-invalid-structure-size-error-during-crash-startu.patch new file mode 100644 index 0000000..91c8fc9 --- /dev/null +++ b/0009-Fix-invalid-structure-size-error-during-crash-startu.patch @@ -0,0 +1,48 @@ +From ec1e61b33a705b8be8d116a541c7b076b0429deb Mon Sep 17 00:00:00 2001 +From: Lianbo Jiang +Date: Mon, 12 Jun 2023 18:50:05 +0800 +Subject: [PATCH 09/13] Fix invalid structure size error during crash startup + on ppc64 + +The crash utility will fail to start session on ppc64 with the following +error: + + # crash vmlinux vmcore -s + + crash: invalid structure size: note_buf + FILE: diskdump.c LINE: 121 FUNCTION: have_crash_notes() + + [./crash] error trace: 101859ac => 10291798 => 10291450 => 10266038 + + 10266038: SIZE_verify+156 + 10291450: have_crash_notes+308 + 10291798: map_cpus_to_prstatus_kdump_cmprs+448 + 101859ac: task_init+11980 + +The reason is that the size of note_buf is not initialized before using +SIZE(note_buf) in the have_crash_notes() on some architectures including +ppc64. Let's initialize it in task_init() to fix this issue. + +Fixes: db8c030857b4 ("diskdump/netdump: fix segmentation fault caused by failure of stopping CPUs") +Signed-off-by: Lianbo Jiang +--- + task.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/task.c b/task.c +index 88941c7b0e4d..2b7467b4193d 100644 +--- a/task.c ++++ b/task.c +@@ -675,6 +675,9 @@ task_init(void) + tt->this_task = pid_to_task(active_pid); + } + else { ++ if (INVALID_SIZE(note_buf)) ++ STRUCT_SIZE_INIT(note_buf, "note_buf_t"); ++ + if (KDUMP_DUMPFILE()) + map_cpus_to_prstatus(); + else if (ELF_NOTES_VALID() && DISKDUMP_DUMPFILE()) +-- +2.37.1 + diff --git a/0010-Revert-Fix-segfault-in-arm64_is_kernel_exception_fra.patch b/0010-Revert-Fix-segfault-in-arm64_is_kernel_exception_fra.patch new file mode 100644 index 0000000..a6a46bb --- /dev/null +++ b/0010-Revert-Fix-segfault-in-arm64_is_kernel_exception_fra.patch @@ -0,0 +1,69 @@ +From 91a76958e4a8a9fb67ac61166ff36e8dc961b3b9 Mon Sep 17 00:00:00 2001 +From: HATAYAMA Daisuke +Date: Wed, 7 Jun 2023 18:37:33 +0900 +Subject: [PATCH 10/13] Revert "Fix segfault in + arm64_is_kernel_exception_frame() when corrupt stack pointer address is + given" + +This reverts commit 9868ebc8e648e5791764a51567a23efae7170d9b. + +The commit 9868ebc8e648e5791764a51567a23efae7170d9b causes the issue +that bt command fails to show backtraces for the tasks that is running +in the user mode at the moment of the kernel panic as follows: + + crash> bt 1734 + PID: 1734 TASK: ffff000000392200 CPU: 4 COMMAND: "insmod" + bt: invalid stack pointer is given + +The root cause is that while the commit added a sanity check into +STACK_OFFSET_TYPE() to validate if a given candidate address of any +interrupt or exception frame is contained within the range of the +corresponding kernel stack, the premise that the STACK_OFFSET_TYPE() +should not return out-of-the-buffer address, is wrong. + +Reexamining the relevant surrounding part of the backtracing code, it +looks to me now that the STACK_OFFSET_TYPE() is originally expected to +return an out-of-the-buffer address, like the address of the top of +the corresponding kernel stack, e.g. at here: + + static int + arm64_in_kdump_text(struct bt_info *bt, struct arm64_stackframe *frame) + { + ... + if (bt->flags & BT_USER_SPACE) + start = (ulong *)&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(bt->stacktop))]; + else { + +Note that the above bt 1734 aborts here. + +Hence, the current implementation policy around STACK_OFFSET_TYPE() +looks that the caller side is responsible for understanding the fact +in advance and for avoiding making buffer overrun carefully. + +To fix this issue, revert the commit. + +Signed-off-by: HATAYAMA Daisuke +Signed-off-by: Lianbo Jiang +--- + defs.h | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/defs.h b/defs.h +index bfda0c48d37b..3e7d6cfbc6a8 100644 +--- a/defs.h ++++ b/defs.h +@@ -976,10 +976,7 @@ struct bt_info { + + #define STACK_OFFSET_TYPE(OFF) \ + (((ulong)(OFF) > STACKSIZE()) ? \ +- (((ulong)(OFF) < (ulong)(bt->stackbase) || (ulong)(OFF) >= (ulong)(bt->stackbase) + STACKSIZE()) ? \ +- error(FATAL, "invalid stack pointer is given\n") : \ +- (ulong)((ulong)(OFF) - (ulong)(bt->stackbase))) : \ +- (ulong)(OFF)) ++ (ulong)((ulong)(OFF) - (ulong)(bt->stackbase)) : (ulong)(OFF)) + + #define GET_STACK_ULONG(OFF) \ + *((ulong *)((char *)(&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(OFF))]))) +-- +2.37.1 + diff --git a/0011-arm64-Fix-again-segfault-in-arm64_is_kernel_exceptio.patch b/0011-arm64-Fix-again-segfault-in-arm64_is_kernel_exceptio.patch new file mode 100644 index 0000000..f6a53cc --- /dev/null +++ b/0011-arm64-Fix-again-segfault-in-arm64_is_kernel_exceptio.patch @@ -0,0 +1,45 @@ +From 6c8cd9b5dcf48221e5f75fc5850bb4719d77acce Mon Sep 17 00:00:00 2001 +From: HATAYAMA Daisuke +Date: Wed, 7 Jun 2023 18:37:34 +0900 +Subject: [PATCH 11/13] arm64: Fix again segfault in + arm64_is_kernel_exception_frame() when corrupt stack pointer address is given + +This is the second trial from the commit +9868ebc8e648e5791764a51567a23efae7170d9b that was reverted at the +previous commit. + +As described in the previous commit, result of STACK_OFFSET_TYPE() can +be an address out of bt->stackbuf and hence the address needs to be +checked prior to being referred to as an pt_regs object. + +So, to fix the issue, let's check if stkptr points to within the range +of the kernel stack first. + +[ kh: added a warning at Lianbo's suggestion ] + +Signed-off-by: HATAYAMA Daisuke +Signed-off-by: Lianbo Jiang +--- + arm64.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/arm64.c b/arm64.c +index efbdccbec9d3..67b1a2244810 100644 +--- a/arm64.c ++++ b/arm64.c +@@ -2381,6 +2381,12 @@ arm64_is_kernel_exception_frame(struct bt_info *bt, ulong stkptr) + struct arm64_pt_regs *regs; + struct machine_specific *ms = machdep->machspec; + ++ if (stkptr > STACKSIZE() && !INSTACK(stkptr, bt)) { ++ if (CRASHDEBUG(1)) ++ error(WARNING, "stkptr: %lx is outside the kernel stack range\n", stkptr); ++ return FALSE; ++ } ++ + regs = (struct arm64_pt_regs *)&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(stkptr))]; + + if (INSTACK(regs->sp, bt) && INSTACK(regs->regs[29], bt) && +-- +2.37.1 + diff --git a/0012-ppc64-Remove-redundant-PTE-checks.patch b/0012-ppc64-Remove-redundant-PTE-checks.patch new file mode 100644 index 0000000..33d7689 --- /dev/null +++ b/0012-ppc64-Remove-redundant-PTE-checks.patch @@ -0,0 +1,53 @@ +From 8b24b2025fb4ae9bd6102bb054bd23987c35387e Mon Sep 17 00:00:00 2001 +From: Likhitha Korrapati +Date: Fri, 16 Jun 2023 17:25:19 +0530 +Subject: [PATCH 12/13] ppc64: Remove redundant PTE checks + +Remove redundant checks for PTE (Page Table Entry) because those +conditions are already covered. + + if (!(pte & _PAGE_PRESENT)) { + ... + return FALSE; + } + + if (!pte) + return FALSE; + +The second pte check is redundant because it holds true only when pte is +0. If pte is 0 then (!(pte & _PAGE_PRESENT)) is true and it will return +false. So there is no need for one more pte check. + +Signed-off-by: Likhitha Korrapati +Signed-off-by: Lianbo Jiang +--- + ppc64.c | 6 ------ + 1 file changed, 6 deletions(-) + +diff --git a/ppc64.c b/ppc64.c +index b95a621d8fe4..fc34006f4863 100644 +--- a/ppc64.c ++++ b/ppc64.c +@@ -968,9 +968,6 @@ ppc64_vtop(ulong vaddr, ulong *pgd, physaddr_t *paddr, int verbose) + return FALSE; + } + +- if (!pte) +- return FALSE; +- + *paddr = PAGEBASE(PTOB(pte >> PTE_RPN_SHIFT_DEFAULT)) + PAGEOFFSET(vaddr); + + if (verbose) { +@@ -1077,9 +1074,6 @@ ppc64_vtop_level4(ulong vaddr, ulong *level4, physaddr_t *paddr, int verbose) + return FALSE; + } + +- if (!pte) +- return FALSE; +- + out: + if (hugepage_type) { + if (hugepage_type == 2) { +-- +2.37.1 + diff --git a/0013-Support-module-memory-layout-change-on-Linux-6.4.patch b/0013-Support-module-memory-layout-change-on-Linux-6.4.patch new file mode 100644 index 0000000..e311fce --- /dev/null +++ b/0013-Support-module-memory-layout-change-on-Linux-6.4.patch @@ -0,0 +1,2443 @@ +From 7750e61fdb2a083f26156a5338aa2ebe26447f3f Mon Sep 17 00:00:00 2001 +From: Kazuhito Hagio +Date: Thu, 22 Jun 2023 16:09:07 +0900 +Subject: [PATCH 13/13] Support module memory layout change on Linux 6.4 + +Support module memory layout change on Linux 6.4 by kernel commit +ac3b43283923 ("module: replace module_layout with module_memory") [1]. +Without the patch, crash cannot even start a session with an error +message like this: + + crash: invalid structure member offset: module_core_size + FILE: kernel.c LINE: 3787 FUNCTION: module_init() + +[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ac3b43283923 + +Signed-off-by: Kazuhito Hagio +Signed-off-by: Lianbo Jiang +--- + defs.h | 46 +- + gdb-10.2.patch | 25 + + kernel.c | 56 +- + memory.c | 38 +- + symbols.c | 1609 ++++++++++++++++++++++++++++++++++++++++++++---- + 5 files changed, 1629 insertions(+), 145 deletions(-) + +diff --git a/defs.h b/defs.h +index 3e7d6cfbc6a8..414853660dc1 100644 +--- a/defs.h ++++ b/defs.h +@@ -675,6 +675,7 @@ struct new_utsname { + #define IRQ_DESC_TREE_RADIX (0x40ULL) + #define IRQ_DESC_TREE_XARRAY (0x80ULL) + #define KMOD_PAX (0x100ULL) ++#define KMOD_MEMORY (0x200ULL) + + #define XEN() (kt->flags & ARCH_XEN) + #define OPENVZ() (kt->flags & ARCH_OPENVZ) +@@ -682,6 +683,7 @@ struct new_utsname { + #define PVOPS_XEN() (kt->flags & ARCH_PVOPS_XEN) + + #define PAX_MODULE_SPLIT() (kt->flags2 & KMOD_PAX) ++#define MODULE_MEMORY() (kt->flags2 & KMOD_MEMORY) + + #define XEN_MACHINE_TO_MFN(m) ((ulonglong)(m) >> PAGESHIFT()) + #define XEN_PFN_TO_PSEUDO(p) ((ulonglong)(p) << PAGESHIFT()) +@@ -2217,6 +2219,9 @@ struct offset_table { /* stash of commonly-used offsets */ + long kset_kobj; + long subsys_private_subsys; + long vmap_area_purge_list; ++ long module_mem; ++ long module_memory_base; ++ long module_memory_size; + }; + + struct size_table { /* stash of commonly-used sizes */ +@@ -2390,6 +2395,7 @@ struct size_table { /* stash of commonly-used sizes */ + long percpu_counter; + long maple_tree; + long maple_node; ++ long module_memory; + }; + + struct array_table { +@@ -2922,6 +2928,23 @@ struct mod_section_data { + ulong size; + int priority; + int flags; ++ ulong addr; ++}; ++ ++/* Emulate enum mod_mem_type in include/linux/module.h */ ++#define MOD_TEXT (0) ++#define MOD_DATA (1) ++#define MOD_RODATA (2) ++#define MOD_RO_AFTER_INIT (3) ++#define MOD_INIT_TEXT (4) ++#define MOD_INIT_DATA (5) ++#define MOD_INIT_RODATA (6) ++#define MOD_MEM_NUM_TYPES (7) ++#define MOD_INVALID (-1) ++ ++struct module_memory { ++ ulong base; ++ uint size; + }; + + struct load_module { +@@ -2957,19 +2980,29 @@ struct load_module { + ulong mod_percpu; + ulong mod_percpu_size; + struct objfile *loaded_objfile; +-}; + +-#define IN_MODULE(A,L) \ +- (((ulong)(A) >= (L)->mod_base) && ((ulong)(A) < ((L)->mod_base+(L)->mod_size))) +- +-#define IN_MODULE_INIT(A,L) \ +- (((ulong)(A) >= (L)->mod_init_module_ptr) && ((ulong)(A) < ((L)->mod_init_module_ptr+(L)->mod_init_size))) ++ /* For 6.4 module_memory */ ++ struct module_memory mem[MOD_MEM_NUM_TYPES]; ++ struct syment **symtable; ++ struct syment **symend; ++ struct syment *ext_symtable[MOD_MEM_NUM_TYPES]; ++ struct syment *ext_symend[MOD_MEM_NUM_TYPES]; ++ struct syment *load_symtable[MOD_MEM_NUM_TYPES]; ++ struct syment *load_symend[MOD_MEM_NUM_TYPES]; ++}; + ++#define IN_MODULE(A,L) (in_module_range(A, L, MOD_TEXT, MOD_RO_AFTER_INIT) != MOD_INVALID) ++#define IN_MODULE_INIT(A,L) (in_module_range(A, L, MOD_INIT_TEXT, MOD_INIT_RODATA) != MOD_INVALID) ++#define IN_MODULE_TEXT(A,L) (in_module_range(A, L, MOD_TEXT, MOD_TEXT) == MOD_TEXT || \ ++ in_module_range(A, L, MOD_INIT_TEXT, MOD_INIT_TEXT) == MOD_INIT_TEXT) + #define IN_MODULE_PERCPU(A,L) \ + (((ulong)(A) >= (L)->mod_percpu) && ((ulong)(A) < ((L)->mod_percpu+(L)->mod_percpu_size))) + + #define MODULE_PERCPU_SYMS_LOADED(L) ((L)->mod_percpu && (L)->mod_percpu_size) + ++#define for_each_mod_mem_type(type) \ ++ for ((type) = MOD_TEXT; (type) < MOD_MEM_NUM_TYPES; (type)++) ++ + #ifndef GDB_COMMON + + #define KVADDR (0x1) +@@ -5588,6 +5621,7 @@ void dump_struct_member(char *, ulong, unsigned); + void dump_union(char *, ulong, unsigned); + void store_module_symbols_v1(ulong, int); + void store_module_symbols_v2(ulong, int); ++void store_module_symbols_6_4(ulong, int); + int is_datatype_command(void); + int is_typedef(char *); + int arg_to_datatype(char *, struct datatype_member *, ulong); +diff --git a/gdb-10.2.patch b/gdb-10.2.patch +index 835aae9859be..16228b1dbf73 100644 +--- a/gdb-10.2.patch ++++ b/gdb-10.2.patch +@@ -3120,3 +3120,28 @@ exit 0 + return result; + } + ++--- gdb-10.2/gdb/symtab.c.orig +++++ gdb-10.2/gdb/symtab.c ++@@ -7476,7 +7476,7 @@ gdb_add_symbol_file(struct gnu_request * ++ int i; ++ int allsect = 0; ++ char *secname; ++- char buf[80]; +++ char buf[96]; ++ ++ gdb_current_load_module = lm = (struct load_module *)req->addr; ++ ++@@ -7515,8 +7515,11 @@ gdb_add_symbol_file(struct gnu_request * ++ secname = lm->mod_section_data[i].name; ++ if ((lm->mod_section_data[i].flags & SEC_FOUND) && ++ !STREQ(secname, ".text")) { ++- sprintf(buf, " -s %s 0x%lx", secname, ++- lm->mod_section_data[i].offset + lm->mod_base); +++ if (lm->mod_section_data[i].addr) +++ sprintf(buf, " -s %s 0x%lx", secname, lm->mod_section_data[i].addr); +++ else +++ sprintf(buf, " -s %s 0x%lx", secname, +++ lm->mod_section_data[i].offset + lm->mod_base); ++ strcat(req->buf, buf); ++ } ++ } +diff --git a/kernel.c b/kernel.c +index 6e98f5f6f6b1..639ed64f306a 100644 +--- a/kernel.c ++++ b/kernel.c +@@ -3571,7 +3571,21 @@ module_init(void) + MEMBER_OFFSET_INIT(module_num_gpl_syms, "module", + "num_gpl_syms"); + +- if (MEMBER_EXISTS("module", "module_core")) { ++ if (MEMBER_EXISTS("module", "mem")) { /* 6.4 and later */ ++ kt->flags2 |= KMOD_MEMORY; /* MODULE_MEMORY() can be used. */ ++ ++ MEMBER_OFFSET_INIT(module_mem, "module", "mem"); ++ MEMBER_OFFSET_INIT(module_memory_base, "module_memory", "base"); ++ MEMBER_OFFSET_INIT(module_memory_size, "module_memory", "size"); ++ STRUCT_SIZE_INIT(module_memory, "module_memory"); ++ ++ if (CRASHDEBUG(1)) ++ error(INFO, "struct module_memory detected.\n"); ++ ++ if (get_array_length("module.mem", NULL, 0) != MOD_MEM_NUM_TYPES) ++ error(WARNING, "module memory types have changed!\n"); ++ ++ } else if (MEMBER_EXISTS("module", "module_core")) { + MEMBER_OFFSET_INIT(module_core_size, "module", + "core_size"); + MEMBER_OFFSET_INIT(module_init_size, "module", +@@ -3757,6 +3771,8 @@ module_init(void) + total += nsyms; + total += 2; /* store the module's start/ending addresses */ + total += 2; /* and the init start/ending addresses */ ++ if (MODULE_MEMORY()) /* 7 regions at most -> 14, so needs +10 */ ++ total += 10; + + /* + * If the module has kallsyms, set up to grab them as well. +@@ -3784,7 +3800,11 @@ module_init(void) + case KALLSYMS_V2: + if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) { + numksyms = UINT(modbuf + OFFSET(module_num_symtab)); +- size = UINT(modbuf + MODULE_OFFSET2(module_core_size, rx)); ++ if (MODULE_MEMORY()) ++ /* check mem[MOD_TEXT].size only */ ++ size = UINT(modbuf + OFFSET(module_mem) + OFFSET(module_memory_size)); ++ else ++ size = UINT(modbuf + MODULE_OFFSET2(module_core_size, rx)); + } else { + numksyms = ULONG(modbuf + OFFSET(module_num_symtab)); + size = ULONG(modbuf + MODULE_OFFSET2(module_core_size, rx)); +@@ -3822,7 +3842,10 @@ module_init(void) + store_module_symbols_v1(total, kt->mods_installed); + break; + case KMOD_V2: +- store_module_symbols_v2(total, kt->mods_installed); ++ if (MODULE_MEMORY()) ++ store_module_symbols_6_4(total, kt->mods_installed); ++ else ++ store_module_symbols_v2(total, kt->mods_installed); + break; + } + +@@ -3836,7 +3859,7 @@ module_init(void) + static int + verify_modules(void) + { +- int i; ++ int i, t; + int found, irregularities; + ulong mod, mod_next, mod_base; + long mod_size; +@@ -3893,8 +3916,13 @@ verify_modules(void) + mod_base = mod; + break; + case KMOD_V2: +- mod_base = ULONG(modbuf + +- MODULE_OFFSET2(module_module_core, rx)); ++ if (MODULE_MEMORY()) ++ /* mem[MOD_TEXT].base */ ++ mod_base = ULONG(modbuf + OFFSET(module_mem) + ++ OFFSET(module_memory_base)); ++ else ++ mod_base = ULONG(modbuf + ++ MODULE_OFFSET2(module_module_core, rx)); + break; + } + +@@ -3916,7 +3944,17 @@ verify_modules(void) + case KMOD_V2: + module_name = modbuf + + OFFSET(module_name); +- if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) ++ if (MODULE_MEMORY()) { ++ mod_size = 0; ++ for_each_mod_mem_type(t) { ++ if (t == MOD_INIT_TEXT) ++ break; ++ ++ mod_size += UINT(modbuf + OFFSET(module_mem) + ++ SIZE(module_memory) * t + ++ OFFSET(module_memory_size)); ++ } ++ } else if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) + mod_size = UINT(modbuf + + MODULE_OFFSET2(module_core_size, rx)); + else +@@ -4536,7 +4574,7 @@ do_module_cmd(ulong flag, char *modref, ulong address, + "MODULE"), + mkstring(buf2, maxnamelen, LJUST, "NAME"), + mkstring(buf4, VADDR_PRLEN, CENTER|LJUST, +- "BASE"), ++ MODULE_MEMORY() ? "TEXT_BASE" : "BASE"), + mkstring(buf3, maxsizelen, RJUST, "SIZE")); + } + +@@ -6144,6 +6182,8 @@ dump_kernel_table(int verbose) + fprintf(fp, "%sIRQ_DESC_TREE_XARRAY", others++ ? "|" : ""); + if (kt->flags2 & KMOD_PAX) + fprintf(fp, "%sKMOD_PAX", others++ ? "|" : ""); ++ if (kt->flags2 & KMOD_MEMORY) ++ fprintf(fp, "%sKMOD_MEMORY", others++ ? "|" : ""); + fprintf(fp, ")\n"); + + fprintf(fp, " stext: %lx\n", kt->stext); +diff --git a/memory.c b/memory.c +index ea3005a5c01f..acbee6389472 100644 +--- a/memory.c ++++ b/memory.c +@@ -15712,10 +15712,44 @@ in_vmlist_segment(ulong vaddr) + static int + next_module_vaddr(ulong vaddr, ulong *nextvaddr) + { +- int i; +- ulong start, end; ++ int i, t; ++ ulong start, end, min = (ulong)-1; + struct load_module *lm; + ++ if (!MODULE_MEMORY()) ++ goto old_module; ++ ++ for (i = 0; i < st->mods_installed; i++) { ++ lm = &st->load_modules[i]; ++ ++ for_each_mod_mem_type(t) { ++ if (!lm->mem[t].size) ++ continue; ++ ++ start = lm->mem[t].base; ++ end = start + lm->mem[t].size; ++ ++ if (vaddr >= end) ++ continue; ++ ++ if (vaddr < start) { ++ if (start < min) /* replace candidate */ ++ min = start; ++ continue; ++ } ++ ++ *nextvaddr = vaddr; ++ return TRUE; ++ } ++ } ++ ++ if (min != (ulong)-1) { ++ *nextvaddr = min; ++ return TRUE; ++ } ++ return FALSE; ++ ++old_module: + for (i = 0; i < st->mods_installed; i++) { + lm = &st->load_modules[i]; + start = lm->mod_base; +diff --git a/symbols.c b/symbols.c +index 7b1d59203b90..f161ee99e90a 100644 +--- a/symbols.c ++++ b/symbols.c +@@ -48,8 +48,8 @@ static int load_module_index(struct syment *); + static void section_header_info(bfd *, asection *, void *); + static void store_section_data(struct load_module *, bfd *, asection *); + static void calculate_load_order_v1(struct load_module *, bfd *); +-static void calculate_load_order_v2(struct load_module *, bfd *, int, +- void *, long, unsigned int); ++static void calculate_load_order_v2(struct load_module *, bfd *, int, void *, long, unsigned int); ++static void calculate_load_order_6_4(struct load_module *, bfd *, int, void *, long, unsigned int); + static void check_insmod_builtin(struct load_module *, int, ulong *); + static int is_insmod_builtin(struct load_module *, struct syment *); + struct load_module; +@@ -104,6 +104,42 @@ static unsigned char is_right_brace(const char *); + static struct struct_elem *find_node(struct struct_elem *, char *); + static void dump_node(struct struct_elem *, char *, unsigned char, unsigned char); + ++static int module_mem_type(ulong, struct load_module *); ++static ulong module_mem_end(ulong, struct load_module *); ++static int in_module_range(ulong, struct load_module *, int, int); ++static struct syment *value_search_module_6_4(ulong, ulong *); ++static struct syment *next_symbol_by_symname(char *); ++static struct syment *prev_symbol_by_symname(char *); ++static struct syment *next_module_symbol_by_value(ulong); ++static struct syment *prev_module_symbol_by_value(ulong); ++static struct syment *next_module_symbol_by_syment(struct syment *); ++static struct syment *prev_module_symbol_by_syment(struct syment *); ++ ++struct module_tag { ++ char *start; ++ char *end; ++ char *start_str; ++ char *end_str; ++}; ++ ++#define MODULE_TAG(type, suffix) ("_MODULE_" #type "_" #suffix "_") ++#define MODULE_STR(type, suffix) ( "MODULE " #type " " #suffix) ++#define MODULE_TAGS(type) { \ ++ .start = MODULE_TAG(type, START), \ ++ .end = MODULE_TAG(type, END), \ ++ .start_str = MODULE_STR(type, START), \ ++ .end_str = MODULE_STR(type, END) \ ++} ++ ++static const struct module_tag module_tag[] = { ++ MODULE_TAGS(TEXT), ++ MODULE_TAGS(DATA), ++ MODULE_TAGS(RODATA), ++ MODULE_TAGS(RO_AFTER_INIT), ++ MODULE_TAGS(INIT_TEXT), ++ MODULE_TAGS(INIT_DATA), ++ MODULE_TAGS(INIT_RODATA), ++}; + + /* + * structure/union printing stuff +@@ -1268,10 +1304,7 @@ symname_hash_search(struct syment *table[], char *name) + * Output for sym -[lL] command. + */ + +-#define MODULE_PSEUDO_SYMBOL(sp) \ +- ((STRNEQ((sp)->name, "_MODULE_START_") || STRNEQ((sp)->name, "_MODULE_END_")) || \ +- (STRNEQ((sp)->name, "_MODULE_INIT_START_") || STRNEQ((sp)->name, "_MODULE_INIT_END_")) || \ +- (STRNEQ((sp)->name, "_MODULE_SECTION_"))) ++#define MODULE_PSEUDO_SYMBOL(sp) (STRNEQ((sp)->name, "_MODULE_")) + + #define MODULE_START(sp) (STRNEQ((sp)->name, "_MODULE_START_")) + #define MODULE_END(sp) (STRNEQ((sp)->name, "_MODULE_END_")) +@@ -1280,6 +1313,76 @@ symname_hash_search(struct syment *table[], char *name) + #define MODULE_SECTION_START(sp) (STRNEQ((sp)->name, "_MODULE_SECTION_START")) + #define MODULE_SECTION_END(sp) (STRNEQ((sp)->name, "_MODULE_SECTION_END")) + ++#define MODULE_MEM_START(sp,t) (STRNEQ((sp)->name, module_tag[t].start)) ++#define MODULE_MEM_END(sp,t) (STRNEQ((sp)->name, module_tag[t].end)) ++ ++/* For 6.4 and later */ ++static void ++module_symbol_dump(char *module) ++{ ++ int i, t; ++ struct syment *sp, *sp_end; ++ struct load_module *lm; ++ const char *p1, *p2; ++ ++ for (i = 0; i < st->mods_installed; i++) { ++ ++ lm = &st->load_modules[i]; ++ if (module && !STREQ(module, lm->mod_name)) ++ continue; ++ ++ if (received_SIGINT() || output_closed()) ++ return; ++ ++ /* ++ * module percpu symbols are within the .data..percpu section, ++ * not in any module memory regions. ++ */ ++ if (MODULE_PERCPU_SYMS_LOADED(lm)) { ++ p1 = "MODULE PERCPU START"; ++ p2 = lm->mod_name; ++ fprintf(fp, "%lx %s: %s\n", lm->mod_percpu, p1, p2); ++ ++ dump_percpu_symbols(lm); ++ ++ p1 = "MODULE PERCPU END"; ++ fprintf(fp, "%lx %s: %s\n", lm->mod_percpu + lm->mod_percpu_size, p1, p2); ++ } ++ ++ for_each_mod_mem_type(t) { ++ if (!lm->symtable[t]) ++ continue; ++ ++ sp = lm->symtable[t]; ++ sp_end = lm->symend[t]; ++ ++ for ( ; sp <= sp_end; sp++) { ++ if (MODULE_PSEUDO_SYMBOL(sp)) { ++ if (MODULE_MEM_START(sp, t)) { ++ p1 = module_tag[t].start_str; ++ p2 = sp->name + strlen(module_tag[t].start); ++ } else if (MODULE_MEM_END(sp, t)) { ++ p1 = module_tag[t].end_str; ++ p2 = sp->name + strlen(module_tag[t].end); ++ } else if (MODULE_SECTION_START(sp)) { ++ p1 = sp->name + strlen("_MODULE_SECTION_START "); ++ p2 = "section start"; ++ } else if (MODULE_SECTION_END(sp)) { ++ p1 = sp->name + strlen("_MODULE_SECTION_END "); ++ p2 = "section end"; ++ } else { ++ p1 = "unknown tag"; ++ p2 = sp->name; ++ } ++ ++ fprintf(fp, "%lx %s: %s\n", sp->value, p1, p2); ++ } else ++ show_symbol(sp, 0, SHOW_RADIX()); ++ } ++ } ++ } ++} ++ + static void + symbol_dump(ulong flags, char *module) + { +@@ -1302,6 +1405,11 @@ symbol_dump(ulong flags, char *module) + if (!(flags & MODULE_SYMS)) + return; + ++ if (MODULE_MEMORY()) { ++ module_symbol_dump(module); ++ return; ++ } ++ + for (i = 0; i < st->mods_installed; i++) { + + lm = &st->load_modules[i]; +@@ -1389,8 +1497,14 @@ dump_percpu_symbols(struct load_module *lm) + struct syment *sp, *sp_end; + + if (MODULE_PERCPU_SYMS_LOADED(lm)) { +- sp = lm->mod_symtable; +- sp_end = lm->mod_symend; ++ if (MODULE_MEMORY()) { ++ /* The lm should have mod_load_symtable. */ ++ sp = lm->mod_load_symtable; ++ sp_end = lm->mod_load_symend; ++ } else { ++ sp = lm->mod_symtable; ++ sp_end = lm->mod_symend; ++ } + for ( ; sp <= sp_end; sp++) { + if (IN_MODULE_PERCPU(sp->value, lm)) + show_symbol(sp, 0, SHOW_RADIX()); +@@ -1425,8 +1539,13 @@ check_for_dups(struct load_module *lm) + { + struct syment *sp, *sp_end; + +- sp = lm->mod_symtable; +- sp_end = lm->mod_symend; ++ if (MODULE_MEMORY()) { ++ sp = lm->mod_load_symtable; ++ sp_end = lm->mod_load_symend; ++ } else { ++ sp = lm->mod_symtable; ++ sp_end = lm->mod_symend; ++ } + + for ( ; sp <= sp_end; sp++) { + if (symbol_name_count(sp->name) > 1) +@@ -1788,6 +1907,362 @@ modsym_value(ulong syms, union kernel_symbol *modsym, int i) + return 0; + } + ++/* ++ * Linux 6.4 introduced module.mem memory layout ++ */ ++void ++store_module_symbols_6_4(ulong total, int mods_installed) ++{ ++ int i, m, t; ++ ulong mod, mod_next; ++ char *mod_name; ++ uint nsyms, ngplsyms; ++ ulong syms, gpl_syms; ++ ulong nksyms; ++ long strbuflen; ++ ulong size; ++ int mcnt, lm_mcnt; ++ union kernel_symbol *modsym; ++ size_t kernel_symbol_size; ++ struct load_module *lm; ++ char buf1[BUFSIZE]; ++ char buf2[BUFSIZE]; ++ char *strbuf = NULL, *modbuf, *modsymbuf; ++ struct syment *sp; ++ ulong first, last; ++ ++ st->mods_installed = mods_installed; ++ ++ if (!st->mods_installed) { ++ st->flags &= ~MODULE_SYMS; ++ return; ++ } ++ ++ /* ++ * If we've been here before, free up everything and start over. ++ */ ++ if (st->flags & MODULE_SYMS) ++ error(FATAL, "re-initialization of module symbols not implemented yet!\n"); ++ ++ kernel_symbol_size = kernel_symbol_type_init(); ++ ++ if ((st->ext_module_symtable = (struct syment *) ++ calloc(total, sizeof(struct syment))) == NULL) ++ error(FATAL, "module syment space malloc (%ld symbols): %s\n", ++ total, strerror(errno)); ++ ++ if (!namespace_ctl(NAMESPACE_INIT, &st->ext_module_namespace, ++ (void *)total, NULL)) ++ error(FATAL, "module namespace malloc: %s\n", strerror(errno)); ++ ++ if ((st->load_modules = (struct load_module *)calloc ++ (st->mods_installed, sizeof(struct load_module))) == NULL) ++ error(FATAL, "load_module array malloc: %s\n", strerror(errno)); ++ ++ modbuf = GETBUF(SIZE(module)); ++ modsymbuf = NULL; ++ m = mcnt = mod_next = 0; ++ ++ for (mod = kt->module_list; mod != kt->kernel_module; mod = mod_next) { ++ ++ readmem(mod, KVADDR, modbuf, SIZE(module), ++ "module buffer", FAULT_ON_ERROR); ++ ++ syms = ULONG(modbuf + OFFSET(module_syms)); ++ gpl_syms = ULONG(modbuf + OFFSET(module_gpl_syms)); ++ nsyms = UINT(modbuf + OFFSET(module_num_syms)); ++ ngplsyms = UINT(modbuf + OFFSET(module_num_gpl_syms)); ++ ++ nksyms = UINT(modbuf + OFFSET(module_num_symtab)); ++ ++ mod_name = modbuf + OFFSET(module_name); ++ ++ lm = &st->load_modules[m++]; ++ BZERO(lm, sizeof(struct load_module)); ++ ++ size = 0; ++ for_each_mod_mem_type(t) { ++ lm->mem[t].base = ULONG(modbuf + OFFSET(module_mem) + ++ SIZE(module_memory) * t + OFFSET(module_memory_base)); ++ lm->mem[t].size = UINT(modbuf + OFFSET(module_mem) + ++ SIZE(module_memory) * t + OFFSET(module_memory_size)); ++ if (t < MOD_INIT_TEXT) ++ size += lm->mem[t].size; ++ } ++ lm->mod_base = lm->mem[MOD_TEXT].base; ++ /* module core size, init not included */ ++ lm->mod_size = size; ++ lm->module_struct = mod; ++ ++ if (strlen(mod_name) < MAX_MOD_NAME) ++ strcpy(lm->mod_name, mod_name); ++ else { ++ error(INFO, "module name greater than MAX_MOD_NAME: %s\n", mod_name); ++ strncpy(lm->mod_name, mod_name, MAX_MOD_NAME-1); ++ } ++ if (CRASHDEBUG(3)) ++ fprintf(fp, "%lx (%lx): %s syms: %d gplsyms: %d ksyms: %ld\n", ++ mod, lm->mod_base, lm->mod_name, nsyms, ngplsyms, nksyms); ++ ++ lm->mod_flags = MOD_EXT_SYMS; ++ lm->mod_ext_symcnt = mcnt; ++ lm->mod_text_start = lm->mod_base; ++ lm->mod_init_module_ptr = lm->mem[MOD_INIT_TEXT].base; ++ lm->mod_init_size = lm->mem[MOD_INIT_TEXT].size; ++ lm->mod_init_text_size = lm->mem[MOD_INIT_TEXT].size; ++ ++ if (VALID_MEMBER(module_percpu)) ++ lm->mod_percpu = ULONG(modbuf + OFFSET(module_percpu)); ++ ++ lm_mcnt = mcnt; ++ for_each_mod_mem_type(t) { ++ if (!lm->mem[t].size) ++ continue; ++ ++ st->ext_module_symtable[mcnt].value = lm->mem[t].base; ++ st->ext_module_symtable[mcnt].type = 'm'; ++ st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL; ++ sprintf(buf2, "%s%s", module_tag[t].start, mod_name); ++ namespace_ctl(NAMESPACE_INSTALL, &st->ext_module_namespace, ++ &st->ext_module_symtable[mcnt], buf2); ++ lm_mcnt = mcnt; ++ mcnt++; ++ ++ if (t >= MOD_INIT_TEXT) ++ lm->mod_flags |= MOD_INIT; ++ } ++ ++ if (nsyms && !IN_MODULE(syms, lm)) { ++ error(WARNING, ++ "[%s] module.syms outside of module " "address space (%lx)\n\n", ++ lm->mod_name, syms); ++ nsyms = 0; ++ } ++ ++ if (nsyms) { ++ modsymbuf = GETBUF(kernel_symbol_size*nsyms); ++ readmem((ulong)syms, KVADDR, modsymbuf, ++ nsyms * kernel_symbol_size, ++ "module symbols", FAULT_ON_ERROR); ++ } ++ ++ for (i = first = last = 0; i < nsyms; i++) { ++ modsym = (union kernel_symbol *) ++ (modsymbuf + (i * kernel_symbol_size)); ++ if (!first ++ || first > modsym_name(syms, modsym, i)) ++ first = modsym_name(syms, modsym, i); ++ if (modsym_name(syms, modsym, i) > last) ++ last = modsym_name(syms, modsym, i); ++ } ++ ++ if (last > first) { ++ /* The buffer should not go over the block. */ ++ ulong end = module_mem_end(first, lm); ++ ++ strbuflen = (last-first) + BUFSIZE; ++ if ((first + strbuflen) >= end) { ++ strbuflen = end - first; ++ ++ } ++ strbuf = GETBUF(strbuflen); ++ ++ if (!readmem(first, KVADDR, strbuf, strbuflen, ++ "module symbol strings", RETURN_ON_ERROR)) { ++ FREEBUF(strbuf); ++ strbuf = NULL; ++ } ++ } ++ ++ ++ for (i = 0; i < nsyms; i++) { ++ modsym = (union kernel_symbol *)(modsymbuf + (i * kernel_symbol_size)); ++ ++ BZERO(buf1, BUFSIZE); ++ ++ if (strbuf) ++ strcpy(buf1, &strbuf[modsym_name(syms, modsym, i) - first]); ++ else ++ read_string(modsym_name(syms, modsym, i), buf1, BUFSIZE-1); ++ ++ if (strlen(buf1)) { ++ st->ext_module_symtable[mcnt].value = ++ modsym_value(syms, modsym, i); ++ st->ext_module_symtable[mcnt].type = '?'; ++ st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL; ++ strip_module_symbol_end(buf1); ++ strip_symbol_end(buf1, NULL); ++ namespace_ctl(NAMESPACE_INSTALL, ++ &st->ext_module_namespace, ++ &st->ext_module_symtable[mcnt], buf1); ++ ++ mcnt++; ++ } ++ } ++ ++ if (modsymbuf) { ++ FREEBUF(modsymbuf); ++ modsymbuf = NULL; ++ } ++ ++ if (strbuf) ++ FREEBUF(strbuf); ++ ++ if (ngplsyms) { ++ modsymbuf = GETBUF(kernel_symbol_size * ngplsyms); ++ readmem((ulong)gpl_syms, KVADDR, modsymbuf, ++ ngplsyms * kernel_symbol_size, ++ "module gpl symbols", FAULT_ON_ERROR); ++ } ++ ++ for (i = first = last = 0; i < ngplsyms; i++) { ++ modsym = (union kernel_symbol *) ++ (modsymbuf + (i * kernel_symbol_size)); ++ if (!first ++ || first > modsym_name(gpl_syms, modsym, i)) ++ first = modsym_name(gpl_syms, modsym, i); ++ if (modsym_name(gpl_syms, modsym, i) > last) ++ last = modsym_name(gpl_syms, modsym, i); ++ } ++ ++ if (last > first) { ++ ulong end = module_mem_end(first, lm); ++ ++ strbuflen = (last-first) + BUFSIZE; ++ if ((first + strbuflen) >= end) { ++ strbuflen = end - first; ++ ++ } ++ strbuf = GETBUF(strbuflen); ++ ++ if (!readmem(first, KVADDR, strbuf, strbuflen, ++ "module gpl symbol strings", RETURN_ON_ERROR)) { ++ FREEBUF(strbuf); ++ strbuf = NULL; ++ } ++ } else ++ strbuf = NULL; ++ ++ for (i = 0; i < ngplsyms; i++) { ++ modsym = (union kernel_symbol *) (modsymbuf + (i * kernel_symbol_size)); ++ ++ BZERO(buf1, BUFSIZE); ++ ++ if (strbuf) ++ strcpy(buf1, &strbuf[modsym_name(gpl_syms, modsym, i) - first]); ++ else ++ read_string(modsym_name(gpl_syms, modsym, i), buf1, BUFSIZE-1); ++ ++ if (strlen(buf1)) { ++ st->ext_module_symtable[mcnt].value = ++ modsym_value(gpl_syms, modsym, i); ++ st->ext_module_symtable[mcnt].type = '?'; ++ st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL; ++ strip_module_symbol_end(buf1); ++ strip_symbol_end(buf1, NULL); ++ namespace_ctl(NAMESPACE_INSTALL, ++ &st->ext_module_namespace, ++ &st->ext_module_symtable[mcnt], buf1); ++ ++ mcnt++; ++ } ++ } ++ ++ if (modsymbuf) { ++ FREEBUF(modsymbuf); ++ modsymbuf = NULL; ++ } ++ ++ if (strbuf) ++ FREEBUF(strbuf); ++ ++ /* ++ * If the module was compiled with kallsyms, add them in. ++ */ ++ switch (kt->flags & (KALLSYMS_V1|KALLSYMS_V2)) ++ { ++ case KALLSYMS_V1: /* impossible, I hope... */ ++ mcnt += store_module_kallsyms_v1(lm, lm_mcnt, mcnt, modbuf); ++ break; ++ case KALLSYMS_V2: ++ mcnt += store_module_kallsyms_v2(lm, lm_mcnt, mcnt, modbuf); ++ break; ++ } ++ ++ for_each_mod_mem_type(t) { ++ if (!lm->mem[t].size) ++ continue; ++ ++ st->ext_module_symtable[mcnt].value = lm->mem[t].base + lm->mem[t].size; ++ st->ext_module_symtable[mcnt].type = 'm'; ++ st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL; ++ sprintf(buf2, "%s%s", module_tag[t].end, mod_name); ++ namespace_ctl(NAMESPACE_INSTALL, ++ &st->ext_module_namespace, ++ &st->ext_module_symtable[mcnt], buf2); ++ mcnt++; ++ } ++ ++ lm->mod_ext_symcnt = mcnt - lm->mod_ext_symcnt; ++ ++ NEXT_MODULE(mod_next, modbuf); ++ } ++ ++ FREEBUF(modbuf); ++ ++ st->ext_module_symcnt = mcnt; ++ st->ext_module_symend = &st->ext_module_symtable[mcnt]; ++ ++ namespace_ctl(NAMESPACE_COMPLETE, &st->ext_module_namespace, ++ st->ext_module_symtable, st->ext_module_symend); ++ ++ qsort(st->ext_module_symtable, mcnt, sizeof(struct syment), ++ compare_syms); ++ ++ /* sort by text base address */ ++ qsort(st->load_modules, m, sizeof(struct load_module), compare_mods); ++ ++ for (m = 0; m < st->mods_installed; m++) { ++ lm = &st->load_modules[m]; ++ ++ for_each_mod_mem_type(t) { ++ if (!lm->mem[t].size) ++ continue; ++ ++ sprintf(buf1, "%s%s", module_tag[t].start, lm->mod_name); ++ sprintf(buf2, "%s%s", module_tag[t].end, lm->mod_name); ++ ++ for (sp = st->ext_module_symtable; sp < st->ext_module_symend; sp++) { ++ if (STREQ(sp->name, buf1)) { ++ lm->ext_symtable[t] = sp; ++ break; ++ } ++ } ++ for ( ; sp < st->ext_module_symend; sp++) { ++ if (STREQ(sp->name, buf2)) { ++ lm->ext_symend[t] = sp; ++ break; ++ } ++ } ++ ++ if (lm->ext_symtable[t] && lm->ext_symend[t]) ++ mod_symtable_hash_install_range(lm->ext_symtable[t], lm->ext_symend[t]); ++ } ++ lm->symtable = lm->ext_symtable; ++ lm->symend = lm->ext_symend; ++ } ++ ++ st->flags |= MODULE_SYMS; ++ ++ if (CRASHDEBUG(2)) { ++ for (sp = st->ext_module_symtable; sp < st->ext_module_symend; sp++) ++ fprintf(fp, "%16lx %s\n", sp->value, sp->name); ++ } ++ ++ if (mcnt > total) ++ error(FATAL, "store_module_symbols_6_4: total: %ld mcnt: %d\n", total, mcnt); ++} ++ + void + store_module_symbols_v2(ulong total, int mods_installed) + { +@@ -2384,6 +2859,7 @@ store_module_kallsyms_v2(struct load_module *lm, int start, int curr, + int mcnt; + int mcnt_idx; + char *module_buf_init = NULL; ++ ulong base, base_init, size, size_init; + + if (!(kt->flags & KALLSYMS_V2)) + return 0; +@@ -2394,9 +2870,22 @@ store_module_kallsyms_v2(struct load_module *lm, int start, int curr, + ns = &st->ext_module_namespace; + ec = &elf_common; + +- module_buf = GETBUF(lm->mod_size); ++ /* kallsyms data looks to be in MOD_DATA region. */ ++ if (MODULE_MEMORY()) { ++ base = lm->mem[MOD_DATA].base; ++ size = lm->mem[MOD_DATA].size; ++ base_init = lm->mem[MOD_INIT_DATA].base; ++ size_init = lm->mem[MOD_INIT_DATA].size; ++ } else { ++ base = lm->mod_base; ++ size = lm->mod_size; ++ base_init = lm->mod_init_module_ptr; ++ size_init = lm->mod_init_size; ++ } + +- if (!readmem(lm->mod_base, KVADDR, module_buf, lm->mod_size, ++ module_buf = GETBUF(size); ++ ++ if (!readmem(base, KVADDR, module_buf, size, + "module (kallsyms)", RETURN_ON_ERROR|QUIET)) { + error(WARNING,"cannot access module kallsyms\n"); + FREEBUF(module_buf); +@@ -2404,10 +2893,10 @@ store_module_kallsyms_v2(struct load_module *lm, int start, int curr, + } + + if (lm->mod_init_size > 0) { +- module_buf_init = GETBUF(lm->mod_init_size); ++ module_buf_init = GETBUF(size_init); + +- if (!readmem(lm->mod_init_module_ptr, KVADDR, module_buf_init, lm->mod_init_size, +- "module init (kallsyms)", RETURN_ON_ERROR|QUIET)) { ++ if (!readmem(base_init, KVADDR, module_buf_init, size_init, ++ "module init (kallsyms)", RETURN_ON_ERROR|QUIET)) { + error(WARNING,"cannot access module init kallsyms\n"); + FREEBUF(module_buf_init); + } +@@ -2429,9 +2918,9 @@ store_module_kallsyms_v2(struct load_module *lm, int start, int curr, + return 0; + } + if (IN_MODULE(ksymtab, lm)) +- locsymtab = module_buf + (ksymtab - lm->mod_base); ++ locsymtab = module_buf + (ksymtab - base); + else +- locsymtab = module_buf_init + (ksymtab - lm->mod_init_module_ptr); ++ locsymtab = module_buf_init + (ksymtab - base_init); + + kstrtab = ULONG(modbuf + OFFSET(module_strtab)); + if (!IN_MODULE(kstrtab, lm) && !IN_MODULE_INIT(kstrtab, lm)) { +@@ -2444,9 +2933,9 @@ store_module_kallsyms_v2(struct load_module *lm, int start, int curr, + return 0; + } + if (IN_MODULE(kstrtab, lm)) +- locstrtab = module_buf + (kstrtab - lm->mod_base); ++ locstrtab = module_buf + (kstrtab - base); + else +- locstrtab = module_buf_init + (kstrtab - lm->mod_init_module_ptr); ++ locstrtab = module_buf_init + (kstrtab - base_init); + + for (i = 1; i < nksyms; i++) { /* ELF starts real symbols at 1 */ + switch (BITS()) +@@ -2461,11 +2950,8 @@ store_module_kallsyms_v2(struct load_module *lm, int start, int curr, + break; + } + +- if (((ec->st_value < lm->mod_base) || +- (ec->st_value > (lm->mod_base + lm->mod_size))) && +- ((ec->st_value < lm->mod_init_module_ptr) || +- (ec->st_value > (lm->mod_init_module_ptr + lm->mod_init_size)))) +- continue; ++ if (!IN_MODULE(ec->st_value, lm) && !IN_MODULE_INIT(ec->st_value, lm)) ++ continue; + + if (ec->st_shndx == SHN_UNDEF) + continue; +@@ -2572,7 +3058,7 @@ strip_module_symbol_end(char *buf) + ulong + lowest_module_address(void) + { +- int i; ++ int i, t; + struct load_module *lm; + ulong low, lowest; + +@@ -2582,9 +3068,20 @@ lowest_module_address(void) + lowest = (ulong)(-1); + for (i = 0; i < st->mods_installed; i++) { + lm = &st->load_modules[i]; +- low = lm->mod_base; +- if (low < lowest) +- lowest = low; ++ if (MODULE_MEMORY()) ++ for_each_mod_mem_type(t) { ++ if (!lm->mem[t].size) ++ continue; ++ ++ low = lm->mem[t].base; ++ if (low < lowest) ++ lowest = low; ++ } ++ else { ++ low = lm->mod_base; ++ if (low < lowest) ++ lowest = low; ++ } + } + + return lowest; +@@ -2593,16 +3090,27 @@ lowest_module_address(void) + ulong + highest_module_address(void) + { +- int i; ++ int i, t; + struct load_module *lm; + ulong high, highest; + + highest = 0; + for (i = 0; i < st->mods_installed; i++) { + lm = &st->load_modules[i]; +- high = lm->mod_base + lm->mod_size; +- if (high > highest) +- highest = high; ++ if (MODULE_MEMORY()) { ++ for_each_mod_mem_type(t) { ++ if (!lm->mem[t].size) ++ continue; ++ ++ high = lm->mem[t].base + lm->mem[t].size; ++ if (high > highest) ++ highest = high; ++ } ++ } else { ++ high = lm->mod_base + lm->mod_size; ++ if (high > highest) ++ highest = high; ++ } + } + + return highest; +@@ -2853,7 +3361,8 @@ compare_syms(const void *v1, const void *v2) + return -1; + if (STRNEQ(s2->name, "__insmod")) + return 1; +- if (STRNEQ(s2->name, "_MODULE_START_")) ++ if (MODULE_MEM_START(s2, MOD_TEXT) || ++ STRNEQ(s2->name, "_MODULE_START_")) + return 1; + /* Get pseudo section name. */ + if (MODULE_SECTION_START(s1)) +@@ -2986,13 +3495,19 @@ is_kernel_text(ulong value) + if (!(lm->mod_section_data[s].flags & SEC_CODE)) + continue; + +- start = lm->mod_base + +- lm->mod_section_data[s].offset; ++ if (MODULE_MEMORY()) ++ start = lm->mod_section_data[s].addr; ++ else ++ start = lm->mod_base + lm->mod_section_data[s].offset; ++ + end = start + lm->mod_section_data[s].size; + + if ((value >= start) && (value < end)) + return TRUE; + } ++ } else if (MODULE_MEMORY()) { ++ if (IN_MODULE_TEXT(value, lm)) ++ return TRUE; + } else { + switch (kt->flags & (KMOD_V1|KMOD_V2)) + { +@@ -3531,22 +4046,42 @@ dump_symbol_table(void) + (ulong)lm->mod_section_data, + lm->mod_section_data ? "" : "(not allocated)"); + ++ if (MODULE_MEMORY()) { ++ int t; ++ for_each_mod_mem_type(t) { ++ fprintf(fp, " mem[%d]: %lx (%x)\n", ++ t, lm->mem[t].base, lm->mem[t].size); ++ } ++ fprintf(fp, " symtable: %lx\n", (ulong)lm->symtable); ++ fprintf(fp, " ext_symtable: %lx\n", (ulong)lm->ext_symtable); ++ for_each_mod_mem_type(t) { ++ fprintf(fp, " ext_symtable[%d]: %lx - %lx\n", ++ t, (ulong)lm->ext_symtable[t], (ulong)lm->ext_symend[t]); ++ } ++ fprintf(fp, " load_symtable: %lx\n", (ulong)lm->load_symtable); ++ for_each_mod_mem_type(t) { ++ fprintf(fp, " load_symtable[%d]: %lx - %lx\n", ++ t, (ulong)lm->load_symtable[t], (ulong)lm->load_symend[t]); ++ } ++ } + + for (s = 0; s < lm->mod_sections; s++) { + fprintf(fp, +- " %12s prio: %x flags: %05x offset: %-8lx size: %lx\n", ++ " %20s prio: %x flags: %08x %s: %-16lx size: %lx\n", + lm->mod_section_data[s].name, + lm->mod_section_data[s].priority, + lm->mod_section_data[s].flags, +- lm->mod_section_data[s].offset, ++ MODULE_MEMORY() ? "addr" : "offset", ++ MODULE_MEMORY() ? lm->mod_section_data[s].addr : ++ lm->mod_section_data[s].offset, + lm->mod_section_data[s].size); + } + + fprintf(fp, " loaded_objfile: %lx\n", (ulong)lm->loaded_objfile); + +- if (CRASHDEBUG(1)) { ++ if (CRASHDEBUG(1) && lm->mod_load_symtable) { + for (sp = lm->mod_load_symtable; +- sp < lm->mod_load_symend; sp++) { ++ sp <= lm->mod_load_symend; sp++) { + fprintf(fp, " %lx %s\n", + sp->value, sp->name); + } +@@ -4458,8 +4993,11 @@ get_section(ulong vaddr, char *buf) + if (module_symbol(vaddr, NULL, &lm, NULL, *gdb_output_radix)) { + if (lm->mod_flags & MOD_LOAD_SYMS) { + for (i = (lm->mod_sections-1); i >= 0; i--) { +- start = lm->mod_base + +- lm->mod_section_data[i].offset; ++ if (MODULE_MEMORY()) ++ start = lm->mod_section_data[i].addr; ++ else ++ start = lm->mod_base + ++ lm->mod_section_data[i].offset; + end = start + lm->mod_section_data[i].size; + + if ((vaddr >= start) && (vaddr < end)) { +@@ -4514,7 +5052,7 @@ get_build_directory(char *buf) + int + symbol_query(char *s, char *print_pad, struct syment **spp) + { +- int i; ++ int i, t; + struct syment *sp, *sp_end; + struct load_module *lm; + int cnt, search_init; +@@ -4534,6 +5072,60 @@ symbol_query(char *s, char *print_pad, struct syment **spp) + } + } + ++ if (!MODULE_MEMORY()) ++ goto old_module; ++ ++ for (i = 0; i < st->mods_installed; i++) { ++ lm = &st->load_modules[i]; ++ ++ if (lm->mod_flags & MOD_LOAD_SYMS) { ++ sp = lm->mod_load_symtable; ++ sp_end = lm->mod_load_symend; ++ ++ for (; sp < sp_end; sp++) { ++ if (MODULE_PSEUDO_SYMBOL(sp)) ++ continue; ++ ++ if (strstr(sp->name, s)) { ++ if (print_pad) { ++ if (strlen(print_pad)) ++ fprintf(fp, "%s", print_pad); ++ show_symbol(sp, 0, SHOW_RADIX()|SHOW_MODULE); ++ } ++ if (spp) ++ *spp = sp; ++ cnt++; ++ } ++ } ++ } else { ++ for_each_mod_mem_type(t) { ++ if (!lm->symtable[t]) ++ continue; ++ ++ sp = lm->symtable[t]; ++ sp_end = lm->symend[t]; ++ ++ for (; sp < sp_end; sp++) { ++ if (MODULE_PSEUDO_SYMBOL(sp)) ++ continue; ++ ++ if (strstr(sp->name, s)) { ++ if (print_pad) { ++ if (strlen(print_pad)) ++ fprintf(fp, "%s", print_pad); ++ show_symbol(sp, 0, SHOW_RADIX()|SHOW_MODULE); ++ } ++ if (spp) ++ *spp = sp; ++ cnt++; ++ } ++ } ++ } ++ } ++ } ++ return cnt; ++ ++old_module: + search_init = FALSE; + + for (i = 0; i < st->mods_installed; i++) { +@@ -4639,7 +5231,7 @@ symbol_search(char *s) + int + symbol_name_count(char *s) + { +- int i; ++ int i, t; + struct syment *sp, *sp_end; + struct load_module *lm; + int count, pseudos, search_init; +@@ -4653,6 +5245,37 @@ symbol_name_count(char *s) + } + } + ++ if (!MODULE_MEMORY()) ++ goto old_module; ++ ++ for (i = 0; i < st->mods_installed; i++) { ++ lm = &st->load_modules[i]; ++ ++ if (lm->mod_flags & MOD_LOAD_SYMS) { ++ sp = lm->mod_load_symtable; ++ sp_end = lm->mod_load_symend; ++ ++ for (; sp < sp_end; sp++) { ++ if (STREQ(s, sp->name)) ++ count++; ++ } ++ } else { ++ for_each_mod_mem_type(t) { ++ if (!lm->symtable[t]) ++ continue; ++ ++ sp = lm->symtable[t]; ++ sp_end = lm->symend[t]; ++ for (; sp < sp_end; sp++) { ++ if (STREQ(s, sp->name)) ++ count++; ++ } ++ } ++ } ++ } ++ return count++; ++ ++old_module: + pseudos = (strstr(s, "_MODULE_START_") || strstr(s, "_MODULE_END_")); + search_init = FALSE; + +@@ -4702,7 +5325,7 @@ symbol_name_count(char *s) + struct syment * + symbol_search_next(char *s, struct syment *spstart) + { +- int i; ++ int i, t; + struct syment *sp, *sp_end; + struct load_module *lm; + int found_start; +@@ -4722,6 +5345,38 @@ symbol_search_next(char *s, struct syment *spstart) + } + } + ++ if (!MODULE_MEMORY()) ++ goto old_module; ++ ++ for (i = 0; i < st->mods_installed; i++) { ++ lm = &st->load_modules[i]; ++ ++ for_each_mod_mem_type(t) { ++ if (!lm->symtable[t]) ++ continue; ++ ++ sp = lm->symtable[t]; ++ sp_end = lm->symend[t]; ++ ++ if (!found_start && (spstart < sp || spstart > sp_end)) ++ continue; ++ ++ for ( ; sp < sp_end; sp++) { ++ if (sp == spstart) { ++ found_start = TRUE; ++ continue; ++ } else if (!found_start) ++ continue; ++ ++ if (STREQ(s, sp->name)) ++ return sp; ++ } ++ } ++ } ++ ++ return NULL; ++ ++old_module: + pseudos = (strstr(s, "_MODULE_START_") || strstr(s, "_MODULE_END_")); + search_init = FALSE; + +@@ -4831,17 +5486,29 @@ module_symbol(ulong value, + for (i = 0; i < st->mods_installed; i++) { + lm = &st->load_modules[i]; + +- if (IN_MODULE(value, lm)) { +- base = lm->mod_base; +- end = lm->mod_base + lm->mod_size; +- } else if (IN_MODULE_INIT(value, lm)) { +- base = lm->mod_init_module_ptr; +- end = lm->mod_init_module_ptr + lm->mod_init_size; +- } else if (IN_MODULE_PERCPU(value, lm)) { +- base = lm->mod_percpu; +- end = lm->mod_percpu + lm->mod_percpu_size; +- } else +- continue; ++ if (MODULE_MEMORY()) { ++ if (IN_MODULE(value, lm) || IN_MODULE_INIT(value, lm)) { ++ int type = module_mem_type(value, lm); ++ base = lm->mem[type].base; ++ end = base + lm->mem[type].size; ++ } else if (IN_MODULE_PERCPU(value, lm)) { ++ base = lm->mod_percpu; ++ end = lm->mod_percpu + lm->mod_percpu_size; ++ } else ++ continue; ++ } else { ++ if (IN_MODULE(value, lm)) { ++ base = lm->mod_base; ++ end = lm->mod_base + lm->mod_size; ++ } else if (IN_MODULE_INIT(value, lm)) { ++ base = lm->mod_init_module_ptr; ++ end = lm->mod_init_module_ptr + lm->mod_init_size; ++ } else if (IN_MODULE_PERCPU(value, lm)) { ++ base = lm->mod_percpu; ++ end = lm->mod_percpu + lm->mod_percpu_size; ++ } else ++ continue; ++ } + + if ((value >= base) && (value < end)) { + if (lmp) +@@ -4877,6 +5544,71 @@ module_symbol(ulong value, + return FALSE; + } + ++static struct syment * ++value_search_module_6_4(ulong value, ulong *offset) ++{ ++ int i, t; ++ struct syment *sp, *sp_end, *spnext, *splast; ++ struct load_module *lm; ++ ++ for (i = 0; i < st->mods_installed; i++) { ++ lm = &st->load_modules[i]; ++ ++ if (!IN_MODULE(value, lm) && !IN_MODULE_INIT(value, lm)) ++ continue; ++ ++ for_each_mod_mem_type(t) { ++ sp = lm->symtable[t]; ++ sp_end = lm->symend[t]; ++ ++ if (value < sp->value) ++ continue; ++ ++ splast = NULL; ++ for ( ; sp <= sp_end; sp++) { ++ if (machine_type("ARM64") && ++ IN_MODULE_PERCPU(sp->value, lm) && ++ !IN_MODULE_PERCPU(value, lm)) ++ continue; ++ ++ if (value == sp->value) { ++ if (MODULE_MEM_END(sp, t)) ++ break; ++ ++ if (MODULE_PSEUDO_SYMBOL(sp)) { ++ spnext = sp + 1; ++ if (MODULE_PSEUDO_SYMBOL(spnext)) ++ continue; ++ if (spnext->value == value) ++ sp = spnext; ++ } ++ if (sp->name[0] == '.') { ++ spnext = sp+1; ++ if (spnext->value == value) ++ sp = spnext; ++ } ++ if (offset) ++ *offset = 0; ++ return sp; ++ } ++ ++ if (sp->value > value) { ++ sp = splast ? splast : sp - 1; ++ if (offset) ++ *offset = value - sp->value; ++ return sp; ++ } ++ ++ if (!MODULE_PSEUDO_SYMBOL(sp)) { ++ splast = sp; ++ } ++ } ++ } ++ } ++ ++ return NULL; ++} ++ + struct syment * + value_search_module(ulong value, ulong *offset) + { +@@ -4885,6 +5617,9 @@ value_search_module(ulong value, ulong *offset) + struct load_module *lm; + int search_init_sections, search_init; + ++ if (MODULE_MEMORY()) ++ return value_search_module_6_4(value, offset); ++ + search_init = FALSE; + search_init_sections = 0; + +@@ -5203,6 +5938,99 @@ closest_symbol_value(ulong value) + return(0); + } + ++/* Only for 6.4 and later */ ++static struct syment * ++next_symbol_by_symname(char *symbol) ++{ ++ struct syment *sp; ++ ++ if ((sp = symbol_search(symbol))) { ++ sp++; ++ if (MODULE_PSEUDO_SYMBOL(sp) && strstr(sp->name, "_END")) ++ return next_module_symbol_by_value(sp->value); ++ ++ return sp; ++ } ++ ++ return NULL; ++} ++ ++/* val_in should be a pseudo module end symbol. */ ++static struct syment * ++next_module_symbol_by_value(ulong val_in) ++{ ++ struct load_module *lm; ++ struct syment *sp, *sp_end; ++ ulong start, min; ++ int i, t; ++ ++retry: ++ sp = sp_end = NULL; ++ min = (ulong)-1; ++ for (i = 0; i < st->mods_installed; i++) { ++ lm = &st->load_modules[i]; ++ ++ /* Search for the next lowest symtable. */ ++ for_each_mod_mem_type(t) { ++ if (!lm->symtable[t]) ++ continue; ++ ++ start = lm->symtable[t]->value; ++ if (start > val_in && start < min) { ++ min = start; ++ sp = lm->symtable[t]; ++ sp_end = lm->symend[t]; ++ } ++ } ++ } ++ ++ if (!sp) ++ return NULL; ++ ++ for ( ; sp < sp_end; sp++) { ++ if (MODULE_PSEUDO_SYMBOL(sp)) ++ continue; ++ if (sp->value > val_in) ++ return sp; ++ } ++ ++ /* Found a table that has only pseudo symbols. */ ++ val_in = sp_end->value; ++ goto retry; ++} ++ ++/* Only for 6.4 and later */ ++static struct syment * ++next_module_symbol_by_syment(struct syment *sp_in) ++{ ++ struct load_module *lm; ++ struct syment *sp; ++ int i, t; ++ ++ for (i = 0; i < st->mods_installed; i++) { ++ lm = &st->load_modules[i]; ++ ++ for_each_mod_mem_type(t) { ++ if (!lm->symtable[t]) ++ continue; ++ ++ if (sp_in < lm->symtable[t] || sp_in > lm->symend[t]) ++ continue; ++ ++ if (sp_in == lm->symend[t]) ++ return next_module_symbol_by_value(sp_in->value); ++ ++ sp = sp_in + 1; ++ if (MODULE_PSEUDO_SYMBOL(sp)) ++ return next_module_symbol_by_value(sp->value); ++ ++ return sp; ++ } ++ } ++ ++ return NULL; ++} ++ + /* + * For a given symbol, return a pointer to the next (higher) symbol's syment. + * Either a symbol name or syment pointer may be passed as an argument. +@@ -5230,6 +6058,9 @@ next_symbol(char *symbol, struct syment *sp_in) + } + } + ++ if (MODULE_MEMORY()) ++ return next_module_symbol_by_syment(sp_in); ++ + search_init = FALSE; + + for (i = 0; i < st->mods_installed; i++) { +@@ -5270,46 +6101,148 @@ next_symbol(char *symbol, struct syment *sp_in) + } + } + +- return NULL; ++ return NULL; ++ } ++ ++ if (MODULE_MEMORY()) ++ return next_symbol_by_symname(symbol); ++ ++ /* ++ * Deal with a few special cases... ++ */ ++ if (strstr(symbol, " module)")) { ++ sprintf(buf, "_MODULE_START_"); ++ strcat(buf, &symbol[1]); ++ p1 = strstr(buf, " module)"); ++ *p1 = NULLCHAR; ++ symbol = buf; ++ } ++ ++ if (STREQ(symbol, "_end")) { ++ if (!st->mods_installed) ++ return NULL; ++ ++ lm = &st->load_modules[0]; ++ ++ return lm->mod_symtable; ++ } ++ ++ if ((sp = symbol_search(symbol))) { ++ sp++; ++ if (MODULE_END(sp)) { ++ sp--; ++ i = load_module_index(sp); ++ if ((i+1) == st->mods_installed) ++ return NULL; ++ ++ lm = &st->load_modules[i+1]; ++ ++ sp = lm->mod_symtable; ++ } ++ return sp; ++ } ++ ++ return NULL; ++} ++ ++/* Only for 6.4 and later */ ++static struct syment * ++prev_symbol_by_symname(char *symbol) ++{ ++ struct syment *sp; ++ ++ if ((sp = symbol_search(symbol))) { ++ if (sp == st->symtable) ++ return NULL; ++ ++ if (module_symbol(sp->value, NULL, NULL, NULL, 0)) { ++ if (MODULE_PSEUDO_SYMBOL(sp) && strstr(sp->name, "_START")) ++ return prev_module_symbol_by_value(sp->value); ++ else ++ sp--; ++ } else ++ sp--; ++ ++ return sp; ++ } ++ ++ return NULL; ++} ++ ++/* val_in should be a pseudo module start symbol. */ ++static struct syment * ++prev_module_symbol_by_value(ulong val_in) ++{ ++ struct load_module *lm; ++ struct syment *sp, *sp_end; ++ ulong end, max; ++ int i, t; ++ ++retry: ++ sp = sp_end = NULL; ++ max = 0; ++ for (i = 0; i < st->mods_installed; i++) { ++ lm = &st->load_modules[i]; ++ ++ /* Search for the previous highest table. */ ++ for_each_mod_mem_type(t) { ++ if (!lm->symtable[t]) ++ continue; ++ ++ end = lm->symend[t]->value; ++ if (end < val_in && end > max) { ++ max = end; ++ sp = lm->symtable[t]; ++ sp_end = lm->symend[t]; ++ } ++ } + } + ++ if (!sp) ++ return NULL; + +- /* +- * Deal with a few special cases... +- */ +- if (strstr(symbol, " module)")) { +- sprintf(buf, "_MODULE_START_"); +- strcat(buf, &symbol[1]); +- p1 = strstr(buf, " module)"); +- *p1 = NULLCHAR; +- symbol = buf; ++ for ( ; sp_end > sp; sp_end--) { ++ if (MODULE_PSEUDO_SYMBOL(sp_end)) ++ continue; ++ if (sp_end->value < val_in) ++ return sp_end; + } + +- if (STREQ(symbol, "_end")) { +- if (!st->mods_installed) +- return NULL; ++ /* Found a table that has only pseudo symbols. */ ++ val_in = sp->value; ++ goto retry; ++} + +- lm = &st->load_modules[0]; ++/* Only for 6.4 and later */ ++static struct syment * ++prev_module_symbol_by_syment(struct syment *sp_in) ++{ ++ struct load_module *lm; ++ struct syment *sp; ++ int i, t; + +- return lm->mod_symtable; +- } ++ for (i = 0; i < st->mods_installed; i++) { ++ lm = &st->load_modules[i]; + +- if ((sp = symbol_search(symbol))) { +- sp++; +- if (MODULE_END(sp)) { +- sp--; +- i = load_module_index(sp); +- if ((i+1) == st->mods_installed) +- return NULL; ++ for_each_mod_mem_type(t) { ++ if (!lm->symtable[t]) ++ continue; + +- lm = &st->load_modules[i+1]; ++ if (sp_in < lm->symtable[t] || sp_in > lm->symend[t]) ++ continue; + +- sp = lm->mod_symtable; ++ if (sp_in == lm->symtable[t]) ++ return prev_module_symbol_by_value(sp_in->value); ++ ++ sp = sp_in - 1; ++ if (MODULE_PSEUDO_SYMBOL(sp)) ++ return prev_module_symbol_by_value(sp->value); ++ ++ return sp; + } +- return sp; + } + +- return NULL; ++ return NULL; + } + + /* +@@ -5335,6 +6268,9 @@ prev_symbol(char *symbol, struct syment *sp_in) + sp_prev = sp; + } + ++ if (MODULE_MEMORY()) ++ return prev_module_symbol_by_syment(sp_in); ++ + search_init = FALSE; + + for (i = 0; i < st->mods_installed; i++) { +@@ -5378,6 +6314,9 @@ prev_symbol(char *symbol, struct syment *sp_in) + return NULL; + } + ++ if (MODULE_MEMORY()) ++ return prev_symbol_by_symname(symbol); ++ + if (strstr(symbol, " module)")) { + sprintf(buf, "_MODULE_START_"); + strcat(buf, &symbol[1]); +@@ -5600,7 +6539,7 @@ kernel_symbol_search(char *symbol) + int + get_syment_array(char *symbol, struct syment **sp_array, int max) + { +- int i, cnt; ++ int i, cnt, t; + struct syment *sp, *sp_end; + struct load_module *lm; + +@@ -5625,6 +6564,31 @@ get_syment_array(char *symbol, struct syment **sp_array, int max) + } + } + ++ if (!MODULE_MEMORY()) ++ goto old_module; ++ ++ for (i = 0; i < st->mods_installed; i++) { ++ lm = &st->load_modules[i]; ++ ++ for_each_mod_mem_type(t) { ++ if (!lm->symtable[t]) ++ continue; ++ ++ sp = lm->symtable[t]; ++ sp_end = lm->symend[t]; ++ for (; sp < sp_end; sp++) { ++ if (STREQ(symbol, sp->name)) { ++ if (max && (cnt < max)) ++ sp_array[cnt] = sp; ++ cnt++; ++ } ++ } ++ } ++ } ++ ++ return cnt; ++ ++old_module: + for (i = 0; i < st->mods_installed; i++) { + lm = &st->load_modules[i]; + sp = lm->mod_symtable; +@@ -9229,6 +10193,9 @@ dump_offset_table(char *spec, ulong makestruct) + OFFSET(module_strtab)); + fprintf(fp, " module_percpu: %ld\n", + OFFSET(module_percpu)); ++ fprintf(fp, " module_mem: %ld\n", OFFSET(module_mem)); ++ fprintf(fp, " module_memory_base: %ld\n", OFFSET(module_memory_base)); ++ fprintf(fp, " module_memory_size: %ld\n", OFFSET(module_memory_size)); + + fprintf(fp, " module_sect_attrs: %ld\n", + OFFSET(module_sect_attrs)); +@@ -10852,6 +11819,7 @@ dump_offset_table(char *spec, ulong makestruct) + SIZE(super_block)); + fprintf(fp, " irqdesc: %ld\n", SIZE(irqdesc)); + fprintf(fp, " module: %ld\n", SIZE(module)); ++ fprintf(fp, " module_memory: %ld\n", SIZE(module_memory)); + fprintf(fp, " module_sect_attr: %ld\n", SIZE(module_sect_attr)); + fprintf(fp, " list_head: %ld\n", SIZE(list_head)); + fprintf(fp, " hlist_head: %ld\n", SIZE(hlist_head)); +@@ -11467,6 +12435,13 @@ store_section_data(struct load_module *lm, bfd *bfd, asection *section) + lm->mod_section_data[i].section = section; + lm->mod_section_data[i].priority = prio; + lm->mod_section_data[i].flags = section->flags & ~SEC_FOUND; ++ lm->mod_section_data[i].size = bfd_section_size(section); ++ lm->mod_section_data[i].offset = 0; ++ lm->mod_section_data[i].addr = 0; ++ if (strlen(name) < MAX_MOD_SEC_NAME) ++ strcpy(lm->mod_section_data[i].name, name); ++ else ++ strncpy(lm->mod_section_data[i].name, name, MAX_MOD_SEC_NAME-1); + /* + * The percpu section isn't included in kallsyms or module_core area. + */ +@@ -11474,13 +12449,8 @@ store_section_data(struct load_module *lm, bfd *bfd, asection *section) + (STREQ(name,".data.percpu") || STREQ(name, ".data..percpu"))) { + lm->mod_percpu_size = bfd_section_size(section); + lm->mod_section_data[i].flags |= SEC_FOUND; ++ lm->mod_section_data[i].addr = lm->mod_percpu; + } +- lm->mod_section_data[i].size = bfd_section_size(section); +- lm->mod_section_data[i].offset = 0; +- if (strlen(name) < MAX_MOD_SEC_NAME) +- strcpy(lm->mod_section_data[i].name, name); +- else +- strncpy(lm->mod_section_data[i].name, name, MAX_MOD_SEC_NAME-1); + lm->mod_sections += 1; + } + +@@ -11722,6 +12692,124 @@ calculate_load_order_v2(struct load_module *lm, bfd *bfd, int dynamic, + } + } + ++/* Linux 6.4 and later */ ++static void ++calculate_load_order_6_4(struct load_module *lm, bfd *bfd, int dynamic, ++ void *minisyms, long symcount, unsigned int size) ++{ ++ struct syment *s1, *s2; ++ ulong sec_start; ++ bfd_byte *from, *fromend; ++ asymbol *store; ++ asymbol *sym; ++ symbol_info syminfo; ++ char *secname; ++ int i, t; ++ ++ if ((store = bfd_make_empty_symbol(bfd)) == NULL) ++ error(FATAL, "bfd_make_empty_symbol() failed\n"); ++ ++ for_each_mod_mem_type(t) { ++ s1 = lm->symtable[t]; ++ s2 = lm->symend[t]; ++ while (s1 < s2) { ++ ++ if (MODULE_PSEUDO_SYMBOL(s1)) { ++ s1++; ++ continue; ++ } ++ ++ /* Skip over symbols whose sections have been identified. */ ++ for (i = 0; i < lm->mod_sections; i++) { ++ if ((lm->mod_section_data[i].flags & SEC_FOUND) == 0) ++ continue; ++ ++ if (s1->value >= lm->mod_section_data[i].addr && ++ s1->value < lm->mod_section_data[i].addr ++ + lm->mod_section_data[i].size) ++ break; ++ } ++ ++ /* Matched one of the sections. Skip symbol. */ ++ if (i < lm->mod_sections) { ++ if (CRASHDEBUG(2)) ++ fprintf(fp, "skip %lx %s %s\n", s1->value, s1->name, ++ lm->mod_section_data[i].name); ++ s1++; ++ continue; ++ } ++ ++ /* Find the symbol in the object file. */ ++ from = (bfd_byte *) minisyms; ++ fromend = from + symcount * size; ++ secname = NULL; ++ for (; from < fromend; from += size) { ++ if (!(sym = bfd_minisymbol_to_symbol(bfd, dynamic, from, store))) ++ error(FATAL, "bfd_minisymbol_to_symbol() failed\n"); ++ ++ bfd_get_symbol_info(bfd, sym, &syminfo); ++ if (CRASHDEBUG(3)) { ++ fprintf(fp,"matching sym %s %lx against bfd %s %lx\n", ++ s1->name, (long) s1->value, syminfo.name, ++ (long) syminfo.value); ++ } ++ if (strcmp(syminfo.name, s1->name) == 0) { ++ secname = (char *)bfd_section_name(sym->section); ++ break; ++ } ++ ++ } ++ if (secname == NULL) { ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "symbol %s not found in module\n", s1->name); ++ s1++; ++ continue; ++ } ++ ++ /* Match the section it came in. */ ++ for (i = 0; i < lm->mod_sections; i++) { ++ if (STREQ(lm->mod_section_data[i].name, secname)) ++ break; ++ } ++ ++ if (i == lm->mod_sections) { ++ fprintf(fp, "?? Section %s not found for symbol %s\n", ++ secname, s1->name); ++ s1++; ++ continue; ++ } ++ ++ if (lm->mod_section_data[i].flags & SEC_FOUND) { ++ s1++; ++ continue; ++ } ++ ++ /* Update the offset information for the section */ ++ sec_start = s1->value - syminfo.value; ++ /* keep the address instead of offset */ ++ lm->mod_section_data[i].addr = sec_start; ++ lm->mod_section_data[i].flags |= SEC_FOUND; ++ ++ if (CRASHDEBUG(2)) ++ fprintf(fp, "update sec offset sym %s @ %lx val %lx section %s\n", ++ s1->name, s1->value, (ulong)syminfo.value, secname); ++ ++ if (strcmp(secname, ".text") == 0) ++ lm->mod_text_start = sec_start; ++ ++ if (strcmp(secname, ".bss") == 0) ++ lm->mod_bss_start = sec_start; ++ ++ if (strcmp(secname, ".data") == 0) ++ lm->mod_data_start = sec_start; ++ ++ if (strcmp(secname, ".rodata") == 0) ++ lm->mod_rodata_start = sec_start; ++ s1++; ++ } ++ } ++} ++ + /* + * Later versons of insmod store basic address information of each + * module in a format that looks like the following example of the +@@ -11927,8 +13015,11 @@ add_symbol_file(struct load_module *lm) + (!STREQ(secname, ".text") && + !STREQ(secname, ".data.percpu") && + !STREQ(secname, ".data..percpu"))) { +- sprintf(buf, " -s %s 0x%lx", secname, +- lm->mod_section_data[i].offset + lm->mod_base); ++ if (MODULE_MEMORY()) ++ sprintf(buf, " -s %s 0x%lx", secname, lm->mod_section_data[i].addr); ++ else ++ sprintf(buf, " -s %s 0x%lx", secname, ++ lm->mod_section_data[i].offset + lm->mod_base); + len += strlen(buf); + } + } +@@ -12269,24 +13360,43 @@ static struct syment * + kallsyms_module_symbol(struct load_module *lm, symbol_info *syminfo) + { + struct syment *sp, *spx; +- int cnt; ++ int cnt, t; + + if (!(lm->mod_flags & MOD_KALLSYMS)) + return NULL; + + sp = NULL; + cnt = 0; +- for (spx = lm->mod_ext_symtable; spx <= lm->mod_ext_symend; spx++) { +- if (!STREQ(spx->name, syminfo->name)) +- continue; +- if (spx->cnt) { +- cnt++; +- continue; +- } ++ if (MODULE_MEMORY()) { ++ for_each_mod_mem_type(t) { ++ if (!lm->ext_symtable[t]) ++ continue; ++ for (spx = lm->ext_symtable[t]; spx <= lm->ext_symend[t]; spx++) { ++ if (!STREQ(spx->name, syminfo->name)) ++ continue; ++ if (spx->cnt) { ++ cnt++; ++ continue; ++ } + +- spx->cnt++; +- sp = spx; +- break; ++ spx->cnt++; ++ sp = spx; ++ break; ++ } ++ } ++ } else { ++ for (spx = lm->mod_ext_symtable; spx <= lm->mod_ext_symend; spx++) { ++ if (!STREQ(spx->name, syminfo->name)) ++ continue; ++ if (spx->cnt) { ++ cnt++; ++ continue; ++ } ++ ++ spx->cnt++; ++ sp = spx; ++ break; ++ } + } + + if (CRASHDEBUG(2)) { +@@ -12312,7 +13422,7 @@ static void + store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms, + long symcount, unsigned int size, ulong base_addr, char *namelist) + { +- int i; ++ int i, t; + asymbol *store; + asymbol *sym; + bfd_byte *from, *fromend; +@@ -12323,7 +13433,7 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms, + char *nameptr, *secname; + long index; + long symalloc; +- int found; ++ int found = FALSE; + + if ((store = bfd_make_empty_symbol(bfd)) == NULL) + error(FATAL, "bfd_make_empty_symbol() failed\n"); +@@ -12380,8 +13490,17 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms, + lm->mod_rodata_start = lm->mod_bss_start = 0; + lm->mod_load_symcnt = 0; + lm->mod_sections = 0; +- for (spx = lm->mod_ext_symtable; spx <= lm->mod_ext_symend; spx++) +- spx->cnt = 0; ++ if (MODULE_MEMORY()) { ++ for_each_mod_mem_type(t) { ++ if (!lm->ext_symtable[t]) ++ continue; ++ for (spx = lm->ext_symtable[t]; spx <= lm->ext_symend[t]; spx++) ++ spx->cnt = 0; ++ } ++ } else { ++ for (spx = lm->mod_ext_symtable; spx <= lm->mod_ext_symend; spx++) ++ spx->cnt = 0; ++ } + sp = lm->mod_load_symtable; + + if (!(lm->mod_section_data = (struct mod_section_data *) +@@ -12392,13 +13511,14 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms, + + bfd_map_over_sections(bfd, section_header_info, MODULE_SECTIONS); + +- if (kt->flags & KMOD_V1) ++ if (MODULE_MEMORY()) ++ calculate_load_order_6_4(lm, bfd, dynamic, minisyms, symcount, size); ++ else if (kt->flags & KMOD_V1) + calculate_load_order_v1(lm, bfd); + else + calculate_load_order_v2(lm, bfd, dynamic, minisyms, + symcount, size); + +- + from = (bfd_byte *) minisyms; + fromend = from + symcount * size; + for (; from < fromend; from += size) +@@ -12501,7 +13621,10 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms, + syminfo.value += lm->mod_percpu; + found = TRUE; + } else { +- syminfo.value += lm->mod_section_data[i].offset + lm->mod_base; ++ if (MODULE_MEMORY()) ++ syminfo.value += lm->mod_section_data[i].addr; ++ else ++ syminfo.value += lm->mod_section_data[i].offset + lm->mod_base; + found = TRUE; + } + } +@@ -12536,6 +13659,53 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms, + * syminfo data types accepted above, plus the two pseudo symbols. + * Note that the new syment name pointers haven't been resolved yet. + */ ++ if (!MODULE_MEMORY()) ++ goto old_module; ++ ++ for_each_mod_mem_type(t) { ++ if (!lm->ext_symtable[t]) ++ continue; ++ for (spx = lm->ext_symtable[t]; spx <= lm->ext_symend[t]; spx++) { ++ found = FALSE; ++ for (sp = lm->mod_load_symtable; sp < lm->mod_load_symend; sp++) { ++ index = (long)sp->name; ++ nameptr = &lm->mod_load_namespace.address[index]; ++ if (STREQ(spx->name, nameptr)) { ++ found = TRUE; ++ if (spx->value == sp->value) { ++ if (CRASHDEBUG(2)) ++ fprintf(fp, "%s: %s matches!\n", ++ lm->mod_name, nameptr); ++ } else { ++ if (CRASHDEBUG(2)) ++ fprintf(fp, ++ "[%s] %s: %lx != extern'd value: %lx\n", ++ lm->mod_name, ++ nameptr, sp->value, ++ spx->value); ++ } ++ break; ++ } ++ } ++ if (!found) { ++ if (CRASHDEBUG(2)) ++ fprintf(fp, "append ext %s (%lx)\n", spx->name, spx->value); ++ /* append it here... */ ++ namespace_ctl(NAMESPACE_INSTALL, ++ &lm->mod_load_namespace, ++ lm->mod_load_symend, spx->name); ++ ++ lm->mod_load_symend->value = spx->value; ++ lm->mod_load_symend->type = spx->type; ++ lm->mod_load_symend->flags |= MODULE_SYMBOL; ++ lm->mod_load_symend++; ++ lm->mod_load_symcnt++; ++ } ++ } ++ } ++ goto append_section_symbols; ++ ++old_module: + for (spx = lm->mod_ext_symtable; spx <= lm->mod_ext_symend; spx++) { + found = FALSE; + for (sp = lm->mod_load_symtable; +@@ -12578,6 +13748,7 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms, + } + } + ++append_section_symbols: + /* + * Append helpful pseudo symbols about found out sections. + * Use 'S' as its type which is never seen in existing symbols. +@@ -12587,8 +13758,11 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms, + if (!(lm->mod_section_data[i].flags & SEC_FOUND)) + continue; + /* Section start */ +- lm->mod_load_symend->value = lm->mod_base + +- lm->mod_section_data[i].offset; ++ if (MODULE_MEMORY()) ++ lm->mod_load_symend->value = lm->mod_section_data[i].addr; ++ else ++ lm->mod_load_symend->value = lm->mod_base + ++ lm->mod_section_data[i].offset; + lm->mod_load_symend->type = 'S'; + lm->mod_load_symend->flags |= MODULE_SYMBOL; + sprintf(name, "_MODULE_SECTION_START [%s]", +@@ -12599,9 +13773,12 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms, + lm->mod_load_symcnt++; + + /* Section end */ +- lm->mod_load_symend->value = lm->mod_base + +- lm->mod_section_data[i].offset + +- lm->mod_section_data[i].size; ++ if (MODULE_MEMORY()) ++ lm->mod_load_symend->value = lm->mod_section_data[i].addr; ++ else ++ lm->mod_load_symend->value = lm->mod_base + ++ lm->mod_section_data[i].offset; ++ lm->mod_load_symend->value += lm->mod_section_data[i].size; + lm->mod_load_symend->type = 'S'; + lm->mod_load_symend->flags |= MODULE_SYMBOL; + sprintf(name, "_MODULE_SECTION_END [%s]", +@@ -12618,16 +13795,57 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms, + qsort(lm->mod_load_symtable, lm->mod_load_symcnt, sizeof(struct syment), + compare_syms); + ++ if (MODULE_MEMORY()) { ++ /* keep load symtable addresses to lm->load_symtable[] */ ++ /* TODO: make more efficient */ ++ for (sp = lm->mod_load_symtable; sp < lm->mod_load_symend; sp++) { ++ char buf1[BUFSIZE], buf2[BUFSIZE]; ++ ++ if (CRASHDEBUG(2)) ++ fprintf(fp, "DEBUG: value %16lx name %s\n", sp->value, sp->name); ++ ++ if (!MODULE_PSEUDO_SYMBOL(sp)) ++ continue; ++ ++ for_each_mod_mem_type(t) { ++ if (!lm->mem[t].size) ++ continue; ++ ++ sprintf(buf1, "%s%s", module_tag[t].start, lm->mod_name); ++ sprintf(buf2, "%s%s", module_tag[t].end, lm->mod_name); ++ ++ if (STREQ(sp->name, buf1)) { ++ lm->load_symtable[t] = sp; ++ break; ++ } else if (STREQ(sp->name, buf2)) { ++ lm->load_symend[t] = sp; ++ break; ++ } ++ } ++ } ++ } ++ + lm->mod_load_symend--; +- if (!MODULE_END(lm->mod_load_symend) && ++ if (!MODULE_MEMORY() && !MODULE_END(lm->mod_load_symend) && + !IN_MODULE_PERCPU(lm->mod_load_symend->value, lm)) + error(INFO, "%s: last symbol: %s is not _MODULE_END_%s?\n", + lm->mod_name, lm->mod_load_symend->name, lm->mod_name); + +- mod_symtable_hash_remove_range(lm->mod_symtable, lm->mod_symend); +- lm->mod_symtable = lm->mod_load_symtable; +- lm->mod_symend = lm->mod_load_symend; +- mod_symtable_hash_install_range(lm->mod_symtable, lm->mod_symend); ++ if (MODULE_MEMORY()) { ++ for_each_mod_mem_type(t) { ++ if (!lm->symtable[t]) ++ continue; ++ mod_symtable_hash_remove_range(lm->symtable[t], lm->symend[t]); ++ } ++ lm->symtable = lm->load_symtable; ++ lm->symend = lm->load_symend; ++ mod_symtable_hash_install_range(lm->mod_load_symtable, lm->mod_load_symend); ++ } else { ++ mod_symtable_hash_remove_range(lm->mod_symtable, lm->mod_symend); ++ lm->mod_symtable = lm->mod_load_symtable; ++ lm->mod_symend = lm->mod_load_symend; ++ mod_symtable_hash_install_range(lm->mod_symtable, lm->mod_symend); ++ } + + lm->mod_flags &= ~MOD_EXT_SYMS; + lm->mod_flags |= MOD_LOAD_SYMS; +@@ -12642,7 +13860,7 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms, + void + delete_load_module(ulong base_addr) + { +- int i; ++ int i, t; + struct load_module *lm; + struct gnu_request request, *req; + +@@ -12657,7 +13875,18 @@ delete_load_module(ulong base_addr) + req->name = lm->mod_namelist; + gdb_interface(req); + } +- mod_symtable_hash_remove_range(lm->mod_symtable, lm->mod_symend); ++ if (MODULE_MEMORY()) { ++ if (lm->mod_load_symtable) { ++ mod_symtable_hash_remove_range(lm->mod_load_symtable, ++ lm->mod_load_symend); ++ for_each_mod_mem_type(t) { ++ lm->load_symtable[t] = NULL; ++ lm->load_symend[t] = NULL; ++ } ++ } ++ } else ++ mod_symtable_hash_remove_range(lm->mod_symtable, lm->mod_symend); ++ + if (lm->mod_load_symtable) { + free(lm->mod_load_symtable); + namespace_ctl(NAMESPACE_FREE, +@@ -12665,9 +13894,23 @@ delete_load_module(ulong base_addr) + } + if (lm->mod_flags & MOD_REMOTE) + unlink_module(lm); +- lm->mod_symtable = lm->mod_ext_symtable; +- lm->mod_symend = lm->mod_ext_symend; +- mod_symtable_hash_install_range(lm->mod_symtable, lm->mod_symend); ++ ++ if (MODULE_MEMORY()) { ++ if (lm->mod_load_symtable) { /* still non-NULL */ ++ lm->symtable = lm->ext_symtable; ++ lm->symend = lm->ext_symend; ++ for_each_mod_mem_type(t) { ++ if (!lm->symtable[t]) ++ continue; ++ mod_symtable_hash_install_range(lm->symtable[t], ++ lm->symend[t]); ++ } ++ } ++ } else { ++ lm->mod_symtable = lm->mod_ext_symtable; ++ lm->mod_symend = lm->mod_ext_symend; ++ mod_symtable_hash_install_range(lm->mod_symtable, lm->mod_symend); ++ } + lm->mod_flags &= ~(MOD_LOAD_SYMS|MOD_REMOTE|MOD_NOPATCH); + lm->mod_flags |= MOD_EXT_SYMS; + lm->mod_load_symtable = NULL; +@@ -12696,7 +13939,18 @@ delete_load_module(ulong base_addr) + req->name = lm->mod_namelist; + gdb_interface(req); + } +- mod_symtable_hash_remove_range(lm->mod_symtable, lm->mod_symend); ++ if (MODULE_MEMORY()) { ++ if (lm->mod_load_symtable) { ++ mod_symtable_hash_remove_range(lm->mod_load_symtable, ++ lm->mod_load_symend); ++ for_each_mod_mem_type(t) { ++ lm->load_symtable[t] = NULL; ++ lm->load_symend[t] = NULL; ++ } ++ } ++ } else ++ mod_symtable_hash_remove_range(lm->mod_symtable, lm->mod_symend); ++ + if (lm->mod_load_symtable) { + free(lm->mod_load_symtable); + namespace_ctl(NAMESPACE_FREE, +@@ -12704,9 +13958,23 @@ delete_load_module(ulong base_addr) + } + if (lm->mod_flags & MOD_REMOTE) + unlink_module(lm); +- lm->mod_symtable = lm->mod_ext_symtable; +- lm->mod_symend = lm->mod_ext_symend; +- mod_symtable_hash_install_range(lm->mod_symtable, lm->mod_symend); ++ ++ if (MODULE_MEMORY()) { ++ if (lm->mod_load_symtable) { ++ lm->symtable = lm->ext_symtable; ++ lm->symend = lm->ext_symend; ++ for_each_mod_mem_type(t) { ++ if (!lm->symtable[t]) ++ continue; ++ mod_symtable_hash_install_range(lm->symtable[t], ++ lm->symend[t]); ++ } ++ } ++ } else { ++ lm->mod_symtable = lm->mod_ext_symtable; ++ lm->mod_symend = lm->mod_ext_symend; ++ mod_symtable_hash_install_range(lm->mod_symtable, lm->mod_symend); ++ } + lm->mod_flags &= ~(MOD_LOAD_SYMS|MOD_REMOTE|MOD_NOPATCH); + lm->mod_flags |= MOD_EXT_SYMS; + lm->mod_load_symtable = NULL; +@@ -13455,7 +14723,7 @@ is_downsized(char *name) + struct syment * + symbol_complete_match(const char *match, struct syment *sp_last) + { +- int i; ++ int i, t; + struct syment *sp, *sp_end, *sp_start; + struct load_module *lm; + int search_init; +@@ -13475,6 +14743,34 @@ symbol_complete_match(const char *match, struct syment *sp_last) + sp_start = NULL; + } + ++ if (!MODULE_MEMORY()) ++ goto old_module; ++ ++ for (i = 0; i < st->mods_installed; i++) { ++ lm = &st->load_modules[i]; ++ ++ for_each_mod_mem_type(t) { ++ sp_end = lm->symend[t]; ++ if (!sp_start) ++ sp_start = lm->symtable[t]; ++ ++ if (sp_start < lm->symtable[t] || sp_start > sp_end) ++ continue; ++ ++ for (sp = sp_start; sp < sp_end; sp++) { ++ if (MODULE_PSEUDO_SYMBOL(sp)) ++ continue; ++ ++ if (STRNEQ(sp->name, match)) ++ return sp; ++ } ++ sp_start = NULL; ++ } ++ } ++ ++ return NULL; ++ ++old_module: + search_init = FALSE; + + for (i = 0; i < st->mods_installed; i++) { +@@ -13521,3 +14817,58 @@ symbol_complete_match(const char *match, struct syment *sp_last) + + return NULL; + } ++ ++/* Returns module memory type if addr is in range, otherwise MOD_INVALID(-1) */ ++static int ++in_module_range(ulong addr, struct load_module *lm, int start, int end) ++{ ++ ulong base = 0, size = 0; ++ int i; ++ ++ if (!MODULE_MEMORY()) ++ goto old_module; ++ ++ for (i = start ; i <= end; i++) { ++ base = lm->mem[i].base; ++ size = lm->mem[i].size; ++ if (!size) ++ continue; ++ if ((addr >= base) && (addr < (base + size))) ++ return i; ++ } ++ return MOD_INVALID; ++ ++old_module: ++ if (start == MOD_TEXT) { ++ base = lm->mod_base; ++ size = lm->mod_size; ++ } else if (start == MOD_INIT_TEXT) { ++ base = lm->mod_init_module_ptr; ++ size = lm->mod_init_size; ++ } else ++ error(FATAL, "invalid module memory type!"); ++ ++ if ((addr >= base) && (addr < (base + size))) ++ return start; ++ ++ return MOD_INVALID; ++} ++ ++/* Returns module memory type, otherwise MOD_INVALID(-1) */ ++static int ++module_mem_type(ulong addr, struct load_module *lm) ++{ ++ return in_module_range(addr, lm, MOD_TEXT, MOD_INIT_RODATA); ++} ++ ++/* Returns the end address of the module memory region. */ ++static ulong ++module_mem_end(ulong addr, struct load_module *lm) ++{ ++ int type = module_mem_type(addr, lm); ++ ++ if (type == MOD_INVALID) ++ return 0; ++ ++ return lm->mem[type].base + lm->mem[type].size; ++} +-- +2.37.1 + diff --git a/crash.spec b/crash.spec index 4f0e4f8..138d7ad 100644 --- a/crash.spec +++ b/crash.spec @@ -4,7 +4,7 @@ Summary: Kernel analysis utility for live systems, netdump, diskdump, kdump, LKCD or mcore dumpfiles Name: crash Version: 8.0.3 -Release: 2%{?dist} +Release: 3%{?dist} License: GPL-3.0-only Source0: https://github.com/crash-utility/crash/archive/crash-%{version}.tar.gz Source1: http://ftp.gnu.org/gnu/gdb/gdb-10.2.tar.gz @@ -19,6 +19,19 @@ Provides: bundled(libiberty) Provides: bundled(gdb) = 10.2 Patch0: lzo_snappy_zstd.patch Patch1: crash-8.0.3_build.patch +Patch2: 0001-Fix-kernel-version-macros-for-revision-numbers-over-.patch +Patch3: 0002-Fix-failure-of-dev-d-D-options-on-Linux-6.4-and-late.patch +Patch4: 0003-Fix-kmem-v-option-displaying-no-regions-on-Linux-6.3.patch +Patch5: 0004-arm64-x86_64-Enhance-vtop-command-to-show-zero_pfn-i.patch +Patch6: 0005-diskdump-netdump-fix-segmentation-fault-caused-by-fa.patch +Patch7: 0006-Fix-segfault-in-arm64_is_kernel_exception_frame-when.patch +Patch8: 0007-Output-prompt-when-stdin-is-not-a-TTY.patch +Patch9: 0008-x86_64-Fix-bt-command-printing-stale-entries-on-Linu.patch +Patch10: 0009-Fix-invalid-structure-size-error-during-crash-startu.patch +Patch11: 0010-Revert-Fix-segfault-in-arm64_is_kernel_exception_fra.patch +Patch12: 0011-arm64-Fix-again-segfault-in-arm64_is_kernel_exceptio.patch +Patch13: 0012-ppc64-Remove-redundant-PTE-checks.patch +Patch14: 0013-Support-module-memory-layout-change-on-Linux-6.4.patch %description The core analysis suite is a self-contained tool that can be used to @@ -40,6 +53,19 @@ offered by Mission Critical Linux, or the LKCD kernel patch. %setup -n %{name}-%{version} -q %patch0 -p1 -b lzo_snappy_zstd.patch %patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 %build @@ -65,6 +91,9 @@ cp -p defs.h %{buildroot}%{_includedir}/crash %{_includedir}/* %changelog +* Tue Jun 27 2023 Lianbo Jiang - 8.0.3-3 +- Support module memory layout change on Linux 6.4 + * Sun Jun 25 2023 Sérgio Basto - 8.0.3-2 - Migrate to SPDX license format