Objects exported from ELF note segments are in fact located apart from
each other on old memory. But on /proc/vmcore they are exported as a
single ELF note segment. To satisfy mmap()'s page-size boundary
requirement, copy them in a page-size aligned buffer allocated by
__get_free_pages() on 2nd kernel and remap the buffer to user-space.

The buffer for ELF note segments is added to vmcore_list as the object
of VMCORE_2ND_KERNEL type.

Copy of ELF note segments is done in two pass: first pass tries to
calculate real total size of ELF note segments, and then 2nd pass
copies the segment data into the buffer of the real total size.

Signed-off-by: HATAYAMA Daisuke <[email protected]>
---

 fs/proc/vmcore.c |   78 +++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 59 insertions(+), 19 deletions(-)

diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c
index 3aedb52..ccf0dc5 100644
--- a/fs/proc/vmcore.c
+++ b/fs/proc/vmcore.c
@@ -230,27 +230,25 @@ static u64 __init get_vmcore_size_elf32(char *elfptr)
        return size;
 }
 
-/* Merges all the PT_NOTE headers into one. */
-static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz,
-                                               struct list_head *vc_list)
+static int __init parse_note_segments_elf64(char *elfptr, int *nr_ptnote,
+                                           u64 *phdr_sz, char *notebuf)
 {
-       int i, nr_ptnote=0, rc=0;
-       char *tmp;
+       int i, rc=0;
+       loff_t notebuf_off = 0;
        Elf64_Ehdr *ehdr_ptr;
-       Elf64_Phdr phdr, *phdr_ptr;
        Elf64_Nhdr *nhdr_ptr;
-       u64 phdr_sz = 0, note_off;
+       Elf64_Phdr *phdr_ptr;
 
        ehdr_ptr = (Elf64_Ehdr *)elfptr;
        phdr_ptr = (Elf64_Phdr*)(elfptr + sizeof(Elf64_Ehdr));
        for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
                int j;
                void *notes_section;
-               struct vmcore *new;
                u64 offset, max_sz, sz, real_sz = 0;
                if (phdr_ptr->p_type != PT_NOTE)
                        continue;
-               nr_ptnote++;
+               if (nr_ptnote)
+                       *nr_ptnote = *nr_ptnote + 1;
                max_sz = phdr_ptr->p_memsz;
                offset = phdr_ptr->p_offset;
                notes_section = kmalloc(max_sz, GFP_KERNEL);
@@ -271,20 +269,51 @@ static int __init merge_note_headers_elf64(char *elfptr, 
size_t *elfsz,
                        real_sz += sz;
                        nhdr_ptr = (Elf64_Nhdr*)((char*)nhdr_ptr + sz);
                }
-
-               /* Add this contiguous chunk of notes section to vmcore list.*/
-               new = get_new_element();
-               if (!new) {
-                       kfree(notes_section);
-                       return -ENOMEM;
+               if (phdr_sz)
+                       *phdr_sz += real_sz;
+               if (notebuf) {
+                       memcpy(notebuf + notebuf_off, notes_section, real_sz);
+                       notebuf_off += real_sz;
                }
-               new->paddr = phdr_ptr->p_offset;
-               new->size = real_sz;
-               list_add_tail(&new->list, vc_list);
-               phdr_sz += real_sz;
                kfree(notes_section);
        }
 
+       return 0;
+}
+
+/* Merges all the PT_NOTE headers into one. */
+static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz,
+                                               struct list_head *vc_list)
+{
+       int i, nr_ptnote, rc=0;
+       char *tmp, *notebuf;
+       Elf64_Ehdr *ehdr_ptr;
+       Elf64_Phdr phdr;
+       u64 phdr_sz, note_off, notebuf_sz;
+       struct vmcore *new;
+
+       ehdr_ptr = (Elf64_Ehdr *)elfptr;
+
+       /* The 1st pass calculates real size of ELF note segments. */
+       nr_ptnote = 0;
+       phdr_sz = 0;
+       rc = parse_note_segments_elf64(elfptr, &nr_ptnote, &phdr_sz, NULL);
+       if (rc < 0)
+               return rc;
+
+       /* The 2nd pass copies the ELF note segments into the buffer
+        * of the exact size. */
+       notebuf_sz = roundup(phdr_sz, PAGE_SIZE);
+       notebuf = (char *) __get_free_pages(GFP_KERNEL | __GFP_ZERO,
+                                           get_order(notebuf_sz));
+       if (!notebuf)
+               return -ENOMEM;
+       rc = parse_note_segments_elf64(elfptr, NULL, NULL, notebuf);
+       if (rc < 0) {
+               free_pages((unsigned long)notebuf, get_order(notebuf_sz));
+               return rc;
+       }
+
        /* Prepare merged PT_NOTE program header. */
        phdr.p_type    = PT_NOTE;
        phdr.p_flags   = 0;
@@ -315,6 +344,17 @@ static int __init merge_note_headers_elf64(char *elfptr, 
size_t *elfsz,
 
        *elfsz = roundup(*elfsz, PAGE_SIZE);
 
+       /* Add the merged unique ELF note segments in vmcore_list. */
+       new = get_new_element();
+       if (!new) {
+               free_pages((unsigned long)notebuf, get_order(notebuf_sz));
+               return -ENOMEM;
+       }
+       new->type = VMCORE_2ND_KERNEL;
+       new->buf = notebuf;
+       new->size = notebuf_sz;
+       list_add_tail(&new->list, vc_list);
+
        return 0;
 }
 

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to