When kASLR is enabled (CONFIG_RANDOMIZED_BASE=y), kernel text mapping base is randomized. The max base offset of such randomization is configured at compile time through CONFIG_RANDOMIZE_MAX_BASE_OFFSET (by default 1G). Currently kexec-tools is using hard code macro X86_64__START_KERNEL_map (0xffffffff80000000) and X86_64_KERNEL_TEXT_SIZE (512M) to determine kernel text mapping from kcore's PT_LOAD. With kASLR, the mapping is changed as the following: ffffffff80000000 - (ffffffff80000000+CONFIG_RANDOMIZE_BASE_MAX_OFFSET) As Vivek suggested, we can get _stext kernel symbol address from /proc/kallsyms, and search for kcore's PT_LOAD which contains _stext, and we can say that this area represents the kernel mapping area. Let's first use this way to find out kernel text mapping. If failed for whatever reason, fall back to use the old way. Suggested-by: Vivek Goyal <vgoyal at redhat.com> Signed-off-by: WANG Chao <chaowang at redhat.com> --- kexec/arch/i386/crashdump-x86.c | 63 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/kexec/arch/i386/crashdump-x86.c b/kexec/arch/i386/crashdump-x86.c index cb19e7d..fb92029 100644 --- a/kexec/arch/i386/crashdump-x86.c +++ b/kexec/arch/i386/crashdump-x86.c @@ -100,6 +100,36 @@ static int get_kernel_paddr(struct kexec_info *UNUSED(info), return -1; } +/* Retrieve kernel _stext symbol virtual address from /proc/kallsyms */ +static unsigned long long get_kernel_stext_sym(void) +{ + const char *kallsyms = "/proc/kallsyms"; + const char *stext = "_stext"; + char sym[128]; + char line[128]; + FILE *fp; + unsigned long long vaddr; + char type; + + fp = fopen(kallsyms, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s\n", kallsyms); + return 0; + } + + while(fgets(line, sizeof(line), fp) != NULL) { + if (sscanf(line, "%Lx %c %s", &vaddr, &type, sym) != 3) + continue; + if (strcmp(sym, stext) == 0) { + dbgprintf("kernel symbol %s vaddr = %16llx\n", stext, vaddr); + return vaddr; + } + } + + fprintf(stderr, "Cannot get kernel %s symbol address\n", stext); + return 0; +} + /* Retrieve info regarding virtual address kernel has been compiled for and * size of the kernel from /proc/kcore. Current /proc/kcore parsing from * from kexec-tools fails because of malformed elf notes. A kernel patch has @@ -118,6 +148,7 @@ static int get_kernel_vaddr_and_size(struct kexec_info *UNUSED(info), int align; off_t size; uint32_t elf_flags = 0; + uint64_t stext_sym; if (elf_info->machine != EM_X86_64) return 0; @@ -145,9 +176,36 @@ static int get_kernel_vaddr_and_size(struct kexec_info *UNUSED(info), return -1; } - /* Traverse through the Elf headers and find the region where - * kernel is mapped. */ end_phdr = &ehdr.e_phdr[ehdr.e_phnum]; + + /* Traverse through the Elf headers and find the region where + * _stext symbol is located in. That's where kernel is mapped */ + stext_sym = get_kernel_stext_sym(); + for(phdr = ehdr.e_phdr; stext_sym && phdr != end_phdr; phdr++) { + if (phdr->p_type == PT_LOAD) { + unsigned long long saddr = phdr->p_vaddr; + unsigned long long eaddr = phdr->p_vaddr + phdr->p_memsz; + unsigned long long size; + + /* Look for kernel text mapping header. */ + if (saddr < stext_sym && eaddr > stext_sym) { + saddr = _ALIGN_DOWN(saddr, X86_64_KERN_VADDR_ALIGN); + elf_info->kern_vaddr_start = saddr; + size = eaddr - saddr; + /* Align size to page size boundary. */ + size = _ALIGN(size, align); + elf_info->kern_size = size; + dbgprintf("kernel vaddr = 0x%llx size = 0x%llx\n", + saddr, size); + return 0; + } + } + } + + /* If failed to retrieve kernel text mapping through + * /proc/kallsyms, Traverse through the Elf headers again and + * find the region where kernel is mapped using hard-coded + * kernel mapping boundries */ for(phdr = ehdr.e_phdr; phdr != end_phdr; phdr++) { if (phdr->p_type == PT_LOAD) { unsigned long long saddr = phdr->p_vaddr; @@ -169,6 +227,7 @@ static int get_kernel_vaddr_and_size(struct kexec_info *UNUSED(info), } } } + fprintf(stderr, "Can't find kernel text map area from kcore\n"); return -1; } -- 1.8.5.3