[PATCH v3 2/8] kexec: generalize and rename get_kernel_stext_sym()

Pratyush Anand panand at redhat.com
Thu Oct 6 23:41:40 PDT 2016


Hi Dave,

On Fri, Oct 7, 2016 at 9:48 AM, Dave Young <dyoung at redhat.com> wrote:
>> >     /* 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();
>> > +   stext_sym = get_kernel_sym("stext");
>>
>>
>> I think this should be get_kernel_sym("_stext");
>>
>> Apart from that as Simon already mentioned, due to commit
>> 9f62cbd ("kexec/arch/i386: Add support for KASLR memory randomization")
>> this patch does not apply cleanly.
>
> Pratyush, I had a cleanup patch below, but I did not get time to test it
> so it is not ready to send out.
>
> The basic thought is to move the page_offset_base code to
> get_kernel_page_offset(), there is also another issue is for old kernel
> or kernel without kaslr built-in there will be an unnecessary error
> message but I have not get any idea how to fix it.
>

Are you talking about following error message:

"Cannot get kernel %s symbol address\n"

May be it can be changes as dbgprintf() rather than fprintf(stderr)

> Would you like to take below cleanup patch along with your next version?

I think, this patch series is for ARM64 support along with any generic
modifications needed by arm64 patches. Below modification is specific
to x86. So, better to send them separately.

> It is also fine to leave it as a future improvement to me.

OK.

>
> Thanks
> Dave
>
> ---
>  kexec/arch/i386/crashdump-x86.c |  158 ++++++++++++++++++++--------------------
>  1 file changed, 82 insertions(+), 76 deletions(-)
>
> --- kexec-tools.orig/kexec/arch/i386/crashdump-x86.c
> +++ kexec-tools/kexec/arch/i386/crashdump-x86.c
> @@ -54,10 +54,59 @@
>
>  extern struct arch_options_t arch_options;
>
> +/* Retrieve kernel symbol virtual address from /proc/kallsyms */
> +static unsigned long long get_kernel_sym(const char *symbol)
> +{
> +       const char *kallsyms = "/proc/kallsyms";
> +       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, symbol) == 0) {
> +                       dbgprintf("kernel symbol %s vaddr = %16llx\n", symbol, vaddr);
> +                       return vaddr;
> +               }
> +       }
> +
> +       fprintf(stderr, "Cannot get kernel %s symbol address\n", symbol);
> +
> +       return 0;
> +}
> +
>  static int get_kernel_page_offset(struct kexec_info *UNUSED(info),
> -                                 struct crash_elf_info *elf_info)
> +                                 struct crash_elf_info *elf_info,
> +                                 struct mem_ehdr *ehdr)
>  {
>         int kv;
> +       struct mem_phdr *phdr, *end_phdr;
> +       const unsigned long long pud_mask = ~((1 << 30) - 1);
> +       unsigned long long vaddr, lowest_vaddr = 0;
> +
> +       end_phdr = &ehdr->e_phdr[ehdr->e_phnum];
> +       /* Search for the real PAGE_OFFSET when KASLR memory randomization
> +        * is enabled */
> +       if (get_kernel_sym("page_offset_base") != 0) {
> +               for(phdr = ehdr->e_phdr; phdr != end_phdr; phdr++) {
> +                       if (phdr->p_type == PT_LOAD) {
> +                               vaddr = phdr->p_vaddr & pud_mask;
> +                               if (lowest_vaddr == 0 || lowest_vaddr > vaddr)

Although, it is just the copy of original code from one function to
another, but I think logic can be improved a bit here. Initialize
lowest_vaddr with ULONG_MAX and we can get rid of one check of
(lowest_vaddr == 0).

> +                                       lowest_vaddr = vaddr;
> +                       }
> +               }
> +               if (lowest_vaddr != 0)

And then here it should be (lowest_vaddr != ULONG_MAX)

> +                       elf_info->page_offset = lowest_vaddr;
> +       }
>
>         if (elf_info->machine == EM_X86_64) {
>                 kv = kernel_version();
> @@ -102,35 +151,6 @@ static int get_kernel_paddr(struct kexec
>         return -1;
>  }
>
> -/* Retrieve kernel symbol virtual address from /proc/kallsyms */
> -static unsigned long long get_kernel_sym(const char *symbol)
> -{
> -       const char *kallsyms = "/proc/kallsyms";
> -       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, symbol) == 0) {
> -                       dbgprintf("kernel symbol %s vaddr = %16llx\n", symbol, vaddr);
> -                       return vaddr;
> -               }
> -       }
> -
> -       fprintf(stderr, "Cannot get kernel %s symbol address\n", symbol);
> -       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
> @@ -139,19 +159,12 @@ static unsigned long long get_kernel_sym
>   * we should get rid of backward compatible code. */
>
>  static int get_kernel_vaddr_and_size(struct kexec_info *UNUSED(info),
> -                                    struct crash_elf_info *elf_info)
> +                                    struct crash_elf_info *elf_info,
> +                                    struct mem_ehdr *ehdr)
>  {
> -       int result;
> -       const char kcore[] = "/proc/kcore";
> -       char *buf;
> -       struct mem_ehdr ehdr;
>         struct mem_phdr *phdr, *end_phdr;
>         int align;
> -       off_t size;
> -       uint32_t elf_flags = 0;
>         uint64_t stext_sym;
> -       const unsigned long long pud_mask = ~((1 << 30) - 1);
> -       unsigned long long vaddr, lowest_vaddr = 0;
>
>         if (elf_info->machine != EM_X86_64)
>                 return 0;
> @@ -160,45 +173,13 @@ static int get_kernel_vaddr_and_size(str
>                 return 0;
>
>         align = getpagesize();
> -       buf = slurp_file_len(kcore, KCORE_ELF_HEADERS_SIZE, &size);
> -       if (!buf) {
> -               fprintf(stderr, "Cannot read %s: %s\n", kcore, strerror(errno));
> -               return -1;
> -       }
>
> -       /* Don't perform checks to make sure stated phdrs and shdrs are
> -        * actually present in the core file. It is not practical
> -        * to read the GB size file into a user space buffer, Given the
> -        * fact that we don't use any info from that.
> -        */
> -       elf_flags |= ELF_SKIP_FILESZ_CHECK;
> -       result = build_elf_core_info(buf, size, &ehdr, elf_flags);
> -       if (result < 0) {
> -               /* Perhaps KCORE_ELF_HEADERS_SIZE is too small? */
> -               fprintf(stderr, "ELF core (kcore) parse failed\n");
> -               return -1;
> -       }
> -
> -       end_phdr = &ehdr.e_phdr[ehdr.e_phnum];
> -
> -       /* Search for the real PAGE_OFFSET when KASLR memory randomization
> -        * is enabled */
> -       if (get_kernel_sym("page_offset_base") != 0) {
> -               for(phdr = ehdr.e_phdr; phdr != end_phdr; phdr++) {
> -                       if (phdr->p_type == PT_LOAD) {
> -                               vaddr = phdr->p_vaddr & pud_mask;
> -                               if (lowest_vaddr == 0 || lowest_vaddr > vaddr)
> -                                       lowest_vaddr = vaddr;
> -                       }
> -               }
> -               if (lowest_vaddr != 0)
> -                       elf_info->page_offset = lowest_vaddr;
> -       }
> +       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_sym("_stext");
> -       for(phdr = ehdr.e_phdr; stext_sym && phdr != end_phdr; phdr++) {
> +       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;
> @@ -223,7 +204,7 @@ static int get_kernel_vaddr_and_size(str
>          * /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++) {
> +       for(phdr = ehdr->e_phdr; 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;
> @@ -887,6 +868,12 @@ int load_crashdump_segments(struct kexec
>         struct memory_range *mem_range, *memmap_p;
>         struct crash_elf_info elf_info;
>         unsigned kexec_arch;
> +       char *buf;
> +       off_t size;
> +       struct mem_ehdr ehdr;
> +       uint32_t elf_flags = 0;
> +       const char kcore[] = "/proc/kcore";
> +       int result;
>
>         memset(&elf_info, 0x0, sizeof(elf_info));
>
> @@ -943,13 +930,32 @@ int load_crashdump_segments(struct kexec
>                 elf_info.class = ELFCLASS64;
>         }
>
> -       if (get_kernel_page_offset(info, &elf_info))
> +       buf = slurp_file_len(kcore, KCORE_ELF_HEADERS_SIZE, &size);
> +       if (!buf) {
> +               fprintf(stderr, "Cannot read %s: %s\n", kcore, strerror(errno));
> +               return -1;
> +       }
> +
> +       /* Don't perform checks to make sure stated phdrs and shdrs are
> +        * actually present in the core file. It is not practical
> +        * to read the GB size file into a user space buffer, Given the
> +        * fact that we don't use any info from that.
> +        */
> +       elf_flags |= ELF_SKIP_FILESZ_CHECK;
> +       result = build_elf_core_info(buf, size, &ehdr, elf_flags);
> +       if (result < 0) {
> +               /* Perhaps KCORE_ELF_HEADERS_SIZE is too small? */
> +               fprintf(stderr, "ELF core (kcore) parse failed\n");
> +               return -1;
> +       }
> +
> +       if (get_kernel_page_offset(info, &elf_info, &ehdr))
>                 return -1;
>
>         if (get_kernel_paddr(info, &elf_info))
>                 return -1;
>
> -       if (get_kernel_vaddr_and_size(info, &elf_info))
> +       if (get_kernel_vaddr_and_size(info, &elf_info, &ehdr))
>                 return -1;
>
>         /* Memory regions which panic kernel can safely use to boot into */

~Pratyush



More information about the kexec mailing list