Newsgroups: comp.os.linux
From: eric@tantalus.nrl.navy.mil (Eric Youngdale)
Subject: core files and gdb for linux.
Reply-To: eric@tantalus.nrl.navy.mil (Eric Youngdale)
Organization: The Internet
Date: Wed, 10 Jun 1992 19:37:47 GMT


	I have patched linux to generate proper core files, and I have
patched gdb to read these core files.  I have uploaded a new gdb to
banjo.concert.net in the Incoming directory (gdb.Z), and I have
uploaded a second file (gdb-4.5-src.tar.Z) which contains all of the
patches (both to gdb and the kernel).

	Linus has the kernel patches, and I imagine that they will
appear in the next release.  Until then, you can start with a 0.96a,
patchlevel 2 kernel, and apply the kernel patch that I have enclosed
at the end of this message.  A new file, user.h is also enclosed.
This should be installed in /usr/src/linux/include/sys and
/usr/include/sys (provided they are not symbolically linked together).
This patch essentially tells linux what the contents of the core file
are supposed to be.  You will obviously need to obtain the new gdb
executable before you will be able to use core files.

-Eric

*** /usr/src/linux/fs/exec.c	Wed Jun 10 14:22:46 1992
--- /usr/src/linux/fs/exec.c	Wed Jun 10 14:26:38 1992
***************
*** 30,35 ****
--- 30,36 ----
  #include < linux/kernel.h>
  #include < linux/mm.h>
  #include < asm/segment.h>
+ #include < sys/user.h>

  extern int sys_exit(int exit_code);
  extern int sys_close(int fd);
***************
*** 69,78 ****
--- 70,85 ----
  	struct file file;
  	unsigned short fs;
  	int has_dumped = 0;
+ 	register int dump_start, dump_size;
+ 	struct user dump;

  	if (!current->dumpable)
  		return 0;
  	current->dumpable = 0;
+
+ /* See if we have enough room to write the upage.  */
+ 	if(current->rlim[RLIMIT_CORE].rlim_cur < PAGE_SIZE/1024) return 0;
+
  	__asm__("mov %%fs,%0":"=r" (fs));
  	__asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
  	if (open_namei("core",O_CREAT | O_WRONLY | O_TRUNC,0600,&inode))
***************
*** 96,117 ****
  	has_dumped = 1;
  /* write and seek example: from kernel space */
  	__asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
! 	DUMP_WRITE("core-dump, regs=\n",17);
! 	DUMP_SEEK(64);
! 	DUMP_WRITE(regs,sizeof(*regs));
! 	if (current->used_math) {
! 		if (last_task_used_math == current)
! 			__asm__("clts ; fnsave %0"::"m" (current->tss.i387));
! 		DUMP_SEEK(1024);
! 		DUMP_WRITE("floating-point regs=\n",21);
! 		DUMP_SEEK(1088);
! 		DUMP_WRITE(&current->tss.i387,sizeof(current->tss.i387));
! 	}
  /* now we start writing out the user space info */
  	__asm__("mov %0,%%fs"::"r" ((unsigned short) 0x17));
! /* the dummy dump-file contains the first block of user space... */
! 	DUMP_SEEK(2048);
! 	DUMP_WRITE(0,1024);
  close_coredump:
  	if (file.f_op->release)
  		file.f_op->release(inode,&file);
--- 103,155 ----
  	has_dumped = 1;
  /* write and seek example: from kernel space */
  	__asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
! 	dump.u_tsize = current->end_code / PAGE_SIZE;
! 	dump.u_dsize = (current->brk - current->end_code) / PAGE_SIZE;
! 	dump.u_ssize =((current->start_stack + 0xfff) / PAGE_SIZE) -
! 	  (regs->esp/ PAGE_SIZE);
! /* If the size of the dump file exceeds the rlimit, then see what would happen
!    if we wrote the stack, but not the data area.  */
! 	if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE/1024 >
! 	    current->rlim[RLIMIT_CORE].rlim_cur)
! 	  dump.u_dsize = 0;
! /* Make sure we have enough room to write the stack and data areas. */
! 	if ((dump.u_ssize+1) * PAGE_SIZE / 1024 >
! 	    current->rlim[RLIMIT_CORE].rlim_cur)
! 	  dump.u_ssize = 0;
!
!        	dump.u_comm = 0;
! 	dump.u_ar0 = (struct pt_regs *)(((int)(&dump.regs)) -((int)(&dump)));
! 	dump.signal = signr;
! 	dump.regs = *regs;
! 	dump.start_code = 0;
! 	dump.start_stack = regs->esp & ~(PAGE_SIZE - 1);
! /* Flag indicating the math stuff is valid. */
!    	if (dump.u_fpvalid = current->used_math) {
!    		if (last_task_used_math == current)
!    			__asm__("clts ; fnsave %0"::"m" (dump.i387));
!    		else
!    			dump.i387 = current->tss.i387;
!    	};
! 	DUMP_WRITE(&dump,sizeof(dump));
! 	DUMP_SEEK(sizeof(dump));
!  /* Dump the task struct.  Not be used by gdb, but could be useful */
! 	DUMP_WRITE(current,sizeof(*current));
! /* Now dump all of the user data.  Include malloced stuff as well */
! 	DUMP_SEEK(4096);
  /* now we start writing out the user space info */
  	__asm__("mov %0,%%fs"::"r" ((unsigned short) 0x17));
! /* Dump the data area */
! 	if (dump.u_dsize != 0) {
! 	  dump_start = current->end_code;
! 	  dump_size = current->brk - current->end_code;
! 	  DUMP_WRITE(dump_start,dump_size);
! 	};
! /* Now prepare to dump the stack area */
! 	if (dump.u_ssize != 0) {
! 	  dump_start = regs->esp & ~(PAGE_SIZE - 1);
! 	  dump_size = dump.u_ssize * PAGE_SIZE;
! 	  DUMP_WRITE(dump_start,dump_size);
! 	};
  close_coredump:
  	if (file.f_op->release)
  		file.f_op->release(inode,&file);
*** /dev/null	Sun May 17 01:10:15 1992
--- /usr/include/sys/user.h	Mon Jun  8 22:51:30 1992
***************
*** 0 ****
--- 1,84 ----
+ #include < sys/ptrace.h>
+
+ /* Core file format: The core file is written in such a way that gdb
+    can understand it and provide useful information to the user (under
+    linux we use the 'trad-core' bfd).  There are quite a number of
+    obstacles to being able to view the contents of the floating point
+    registers, and until these are solved you will not be able to view the
+    contents of them.  Actually, you can read in the core file and look at
+    the contents of the user struct to find out what the floating point
+    registers contain.
+
+    The actual file contents are as follows:
+
+    UPAGE: 1 page consisting of a user struct that tells gdb what is present
+    in the file.  Directly after this is a copy of the task_struct, which
+    is currently not used by gdb, but it may come in useful at some point.
+    All of the registers are stored as part of the upage.  The upage should
+    always be only one page.
+
+    DATA: The data area is stored.  We use current->end_text to
+    current->brk to pick up all of the user variables, plus any memory
+    that may have been malloced.  No attempt is made to determine if a page
+    is demand-zero or if a page is totally unused, we just cover the entire
+    range.  All of the addresses are rounded in such a way that an integral
+    number of pages is written.
+
+    STACK: We need the stack information in order to get a meaningful
+    backtrace.  We need to write the data from (esp) to
+    current->start_stack, so we round each of these off in order to be able
+    to write an integer number of pages.
+
+    The minimum core file size is 3 pages, or 12288 bytes.
+ */
+
+ struct user_i387_struct {
+ 	long	cwd;
+ 	long	swd;
+ 	long	twd;
+ 	long	fip;
+ 	long	fcs;
+ 	long	foo;
+ 	long	fos;
+ 	long	st_space[20];	/* 8*10 bytes for each FP-reg = 80 bytes */
+ };
+
+ /* When the kernel dumps core, it starts by dumping the user struct -
+    this will be used by gdb to figure out where the data and stack segments
+    are within the file, and what virtual addresses to use. */
+
+ struct user{
+ /* We start with the registers, to mimic the way that "memory" is returned
+    from the ptrace(3,...) function.  */
+
+   struct pt_regs regs;		/* Where the registers are actually stored */
+
+ /* ptrace does not yet supply these.  Someday.... */
+
+   int u_fpvalid;		/* True if math co-processor being used. */
+                                 /* for this mess. Not yet used. */
+   struct user_i387_struct i387;	/* Math Co-processor registers. */
+
+ /* The rest of this junk is to help gdb figure out what goes where */
+
+   unsigned long int u_tsize;	/* Text segment size (pages). */
+   unsigned long int u_dsize;	/* Data segment size (pages). */
+   unsigned long int u_ssize;	/* Stack segment size (pages). */
+   unsigned long start_code;     /* Starting virtual address of text. */
+   unsigned long start_stack;	/* Starting virtual address of stack area.
+ 				   This is actually the bottom of the stack,
+ 				   the top of the stack is always found in the
+ 				   esp register.  */
+   long int signal;     		/* Signal that caused the core dump. */
+   char * u_comm;		/* User command that was responsible */
+   struct pt_regs * u_ar0;	/* Used by gdb to help find the values for */
+ 				/* the registers. */
+   struct user_i387_struct* u_fpstate;	/* Math Co-processor pointer. */
+ };
+
+
+ #define NBPG 4096
+ #define UPAGES 1
+ #define HOST_TEXT_START_ADDR (u.start_code)
+ #define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG)
+