[PATCH 09/13] vmcore: copy ELF note segments in buffer on 2nd kernel

From: HATAYAMA Daisuke
Date: Thu Feb 14 2013 - 05:12:43 EST


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 <d.hatayama@xxxxxxxxxxxxxx>
---

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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/