Patch to make /proc/kcore ELF

Kohtala Marko (Marko.Kohtala@ntc.nokia.com)
Wed, 5 Nov 1997 22:39:49 +0200


I had some trouble getting "gdb vmlinuz /proc/kcore" to work. It seems
to work a bit better if /proc/kcore is in ELF instead of a.out. a.out
could not tell from what offset the kcore was made, so gdb read the
wrong data.

There still is the problem, that gdb 4.16 seems to read the data once
and then it uses the old value. I hoped mmap support would have fixed
that, but no, now looking at gdb with strace shows does not use mmap
and buffers the data it has read from kcore last. Also, it would help
if gdb accepted core files without process status and info, there is
little to give for a kernel.

Anyway, the patch against 2.1.62 is this. Please try it out and tell
me if it should be improved or works and should be sent to Linus.

--- 2.1-linux/fs/proc/array.c Wed Nov 5 21:22:25 1997
+++ aspoca-linux/fs/proc/array.c Wed Nov 5 21:21:33 1997
@@ -30,6 +30,7 @@
* <Yves.Arrouye@marin.fdn.fr>
*/

+#include <linux/config.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/sched.h>
@@ -37,12 +38,15 @@
#include <linux/kernel_stat.h>
#include <linux/tty.h>
#include <linux/user.h>
+#ifdef CONFIG_BINFMT_ELF
+#include <linux/elf.h>
+#include <linux/elfcore.h>
+#endif
#include <linux/a.out.h>
#include <linux/string.h>
#include <linux/mman.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
-#include <linux/config.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/swap.h>
@@ -60,20 +64,119 @@
int get_malloc(char * buffer);
#endif

+#if defined (__i386__) || defined (__mc68000__)
+# define FIRST_MAPPED PAGE_SIZE /* we don't have page 0 mapped on x86.. */
+#else
+# define FIRST_MAPPED 0
+#endif
+
+#ifdef USE_ELF_CORE_DUMP
+#define noteoffset (sizeof(struct elfhdr) + 2*sizeof(struct elf_phdr))
+#define notesize (2*sizeof(struct elf_note) + 2*4 \
+ + sizeof(struct elf_prpsinfo) + sizeof(struct elf_prstatus))
+#define codeoffset (noteoffset+notesize)
+#endif

static ssize_t read_core(struct file * file, char * buf,
size_t count, loff_t *ppos)
{
+#ifdef USE_ELF_CORE_DUMP
+ unsigned long p = *ppos, memsize;
+ ssize_t read;
+ static const struct
+ {
+ struct elfhdr hdr;
+ struct elf_phdr phdr[1];
+ } file_hdr =
+ { { { 0x7F, 'E', 'L', 'F', ELF_CLASS, ELF_DATA, EV_CURRENT },
+ ET_CORE, ELF_ARCH, EV_CURRENT, 0, sizeof(struct elfhdr),
+ 0, 0, sizeof(struct elfhdr), sizeof(struct elf_phdr),
+ 2, 0, 0, 0 },
+ { { PT_NOTE, noteoffset, 0, 0, notesize, 0, 0, 0 }
+ }
+ };
+ static const struct elf_phdr code_phdr = {
+ PT_LOAD, codeoffset, PAGE_OFFSET+FIRST_MAPPED, 0,
+ -PAGE_OFFSET, 0, 0 };
+
+ memsize = (max_mapnr + 1) << PAGE_SHIFT;
+ if (p >= memsize)
+ return 0;
+ if (count > memsize - p)
+ count = memsize - p;
+ read = 0;
+
+ if (p < sizeof(file_hdr) && count > 0) {
+ size_t count1 = count;
+ if (p + count1 > sizeof(file_hdr))
+ count1 = sizeof(file_hdr)-p;
+ copy_to_user(buf, (char *)&file_hdr + p, count1);
+ buf += count1;
+ p += count1;
+ count -= count1;
+ read += count1;
+ }
+ if (p >= sizeof(file_hdr) &&
+ p < sizeof(file_hdr)+sizeof(code_phdr) &&
+ count > 0) {
+ size_t count1 = count;
+ struct elf_phdr phdr = code_phdr;
+ phdr.p_filesz = memsize;
+ phdr.p_memsz = memsize;
+ if (p + count1 > sizeof(file_hdr)+sizeof(code_phdr))
+ count1 = sizeof(file_hdr)+sizeof(code_phdr)-p;
+ copy_to_user(buf, (char *)&phdr + p - sizeof(file_hdr), count1);
+ buf += count1;
+ p += count1;
+ count -= count1;
+ read += count1;
+ }
+ if (p >= noteoffset && p < noteoffset + notesize && count > 0) {
+ /* The notes section */
+ struct {
+ struct elf_note note1;
+ char name1[4];
+ struct elf_prstatus prstatus;
+ struct elf_note note2;
+ char name2[4];
+ struct elf_prpsinfo prpsinfo;
+ } note;
+ unsigned long count1 = count;
+ memset(&note, 0, sizeof(note));
+ note.note1.n_namesz = 4;
+ note.note1.n_descsz = sizeof(struct elf_prstatus);
+ note.note1.n_type = NT_PRSTATUS;
+ strcpy(note.prpsinfo.pr_fname, "vmlinux");
+ memcpy(note.name1, "CORE", 4);
+ note.note2.n_namesz = 4;
+ note.note2.n_descsz = sizeof(struct elf_prpsinfo);
+ note.note2.n_type = NT_PRPSINFO;
+ memcpy(note.name2, "CORE", 4);
+ if (p + count1 > noteoffset + notesize)
+ count1 = noteoffset + notesize-p;
+ copy_to_user(buf, (char *)&note + p - noteoffset, count1);
+ buf += count1;
+ p += count1;
+ count -= count1;
+ read += count1;
+ }
+
+ if (p >= codeoffset && p < codeoffset+memsize && count > 0) {
+ size_t count1 = count;
+ if (p + count1 > memsize)
+ count1 = memsize - p;
+ copy_to_user(buf, (void*)(PAGE_OFFSET+FIRST_MAPPED+p-codeoffset), count1);
+ read += count1;
+ }
+ *ppos += read;
+ return read;
+
+#else
unsigned long p = *ppos, memsize;
ssize_t read;
ssize_t count1;
char * pnt;
struct user dump;
-#if defined (__i386__) || defined (__mc68000__)
-# define FIRST_MAPPED PAGE_SIZE /* we don't have page 0 mapped on x86.. */
-#else
-# define FIRST_MAPPED 0
-#endif

memset(&dump, 0, sizeof(struct user));
dump.magic = CMAGIC;
@@ -117,11 +220,35 @@
}
*ppos += read;
return read;
+#endif
}

+#ifdef USE_ELF_CORE_DUMP
+static int mmap_kcore(struct file * file, struct vm_area_struct * vma)
+{
+ unsigned long offset = vma->vm_offset;
+
+ if (offset < codeoffset || offset % PAGE_SIZE != codeoffset % PAGE_SIZE)
+ return -ENXIO;
+
+ offset = __pa(offset - codeoffset + PAGE_SIZE + FIRST_MAPPED);
+ if (remap_page_range(vma->vm_start, offset, vma->vm_end - vma->vm_start, vma->vm_page_prot))
+ return -EAGAIN;
+ vma->vm_dentry = dget(file->f_dentry);
+ return 0;
+}
+#endif
+
static struct file_operations proc_kcore_operations = {
NULL, /* lseek */
read_core,
+#ifdef USE_ELF_CORE_DUMP
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ NULL, /* ioctl */
+ mmap_kcore, /* mmap */
+#endif
};

struct inode_operations proc_kcore_inode_operations = {

-- 
---
Marko Kohtala - Marko.Kohtala@ntc.nokia.com, Marko.Kohtala@hut.fi