Z0MAGIC: a.out without a valid page 0

John Bruner jdb at mordor.UUCP
Fri Nov 30 04:14:52 AEST 1984


Several months ago I posted an inquiry about UNIX systems which do
not map virtual address 0 to a valid physical address.  I was
interested in the number of library routines which would have to be
"fixed" because they depended upon being able to dereference a NULL
pointer (a zero-valued pointer on a non-tagged machine like the VAX).
The general consensus was that most library routines would work OK
without changes, especially since other implementations of UNIX on
non-DEC machines did not map 0 to a valid address.

I implemented an OPTIONAL new object format on our 4.2BSD 750's last
October.  We have been using it for locally-written programs since
then and have had no problems.  It has been helpful in finding
errors in some of our programs that were running "correctly" before.

The new object format is referred to as "-Z" format or Z0MAGIC format,
where Z0MAGIC is 0420.  A Z0MAGIC executable file differs from a
corresponding ZMAGIC executable file in that the first page of the
text segment is always filled with zeros and the start address
("a_entry" in the a.out header) is 1024 rather than zero.  The format
of the file is otherwise identical to a ZMAGIC file.  This similarity
has two advantages.  First, it is trivial to modify the debuggers and
other user-level programs to understand Z0MAGIC format, and second,
it is possible to produce a ZMAGIC binary from a Z0MAGIC binary by
simply patching the magic number.  (This may be useful for transporting
a binary from one machine to another.)

At the user level, the following things were changed:

a.out.h	new definitions added.

 libc.a	nlist.o must be recompiled (no source changes necessary)

  ld	The flag "-Z" is now recognized.  "ld" inserts 1024 bytes of
	zeros at the start of the text segment.  [Note: the default
	object format is still ZMAGIC or "-z" format.]

  cc	Because the leading 1024 bytes of zeros affect the addresses
	assigned to symbols in the text segments, it is necessary for
	the "-Z" flag to preceed all input file specifications in the
	"ld" argument list.  However, "cc" likes to call "ld":

	    ld -X /lib/crt0.o [ld flags] [files] [libraries] -lc

	The "cc" command was changed to recognize "-Z" and call the
	loader with "-Z" preceeding "/lib/crt0.o" in the arglist.

  file	The new magic number was recognized so that an appropriate
	identification message could be output.

  adb	The debugger was informed that Z0MAGIC is a valid a.out
	magic number.

  dbx	The debugger was informed that Z0MAGIC is a valid a.out
	magic number.  [Caveat: we do not use "dbx" here.]

  sdb	The debugger was informed that Z0MAGIC is a valid a.out
	magic number.  [We still use "sdb" locally.]

Also, several programs which use the macros in <a.out.h> should be
recompiled so that they understand Z0MAGIC files.  No sources
changes are necessary.  Here is a list of (at least most of) the
major ones:

  diff gprof make nm prof ranlib size strings strip symorder


At the kernel level, a new flag was added to the x_flag field in the
(shared) text structure: XPZIV (Page Zero InValid).  This flag is set
when the magic number is 0420 [why does the kernel source use literal
numeric constants for the magic numbers instead of #define-d names?].

xalloc() calls settprot() to make all pages in the text segment
readonly.  A second argument was added to settprot() which specifies
whether page 0 is to be made inaccessible.  (This argument is nonzero
if XPZIV is set.)  If it is nonzero, the access mode for the first
CLSIZE 512-byte pages of the text segment is set to 0 (no access to
anyone).  If the process attempts to reference any location in the
inaccessible area it will receive a SIGBUS signal.

When a traced child process is performing an I-space write on behalf
of its parent, procxmt() temporarily converts the text page to write
access, writes the word, and converts it back to readonly access.
A check was added to forbid writes to non-readable text pages (hence,
forbidding a write to an invalid page zero).

[BTW, I believe that the changes to a PDP-11 kernel would be similar,
although I no longer have access to an 11 to try them on.  The new object
format could be based upon NMAGIC with the first 64 bytes of the first
segmentation register left invalid.]

-------------------------------------------------------------------------


++ h/text.h
*** /tmp/,RCSt1002982	Fri Oct  5 17:07:20 1984
--- text.h	Thu Sep 20 10:39:07 1984
***************
*** 35,37
  #define	XLOCK	010		/* Being swapped in or out */
  #define	XWANT	020		/* Wanted for swapping */
  #define	XPAGI	040		/* Page in on demand from inode */

--- 35,38 -----
  #define	XLOCK	010		/* Being swapped in or out */
  #define	XWANT	020		/* Wanted for swapping */
  #define	XPAGI	040		/* Page in on demand from inode */
+ #define	XPZIV	0100		/* Page 0 is not valid (420-format files) */

-------------------------------------------------------------------------

++ sys/kern_exec.c
*** /tmp/,RCSt1002988	Fri Oct  5 17:07:28 1984
--- kern_exec.c	Thu Sep 20 10:51:28 1984
***************
*** 83,88
  	 *	407 = plain executable
  	 *	410 = RO text
  	 *	413 = demand paged RO text
  	 * Also an ASCII line beginning with #! is
  	 * the file name of a ``shell'' and arguments may be prepended
  	 * to the argument list if given here.

--- 83,89 -----
  	 *	407 = plain executable
  	 *	410 = RO text
  	 *	413 = demand paged RO text
+ 	 *	420 = demand paged R0 text, page 0 invalid
  	 * Also an ASCII line beginning with #! is
  	 * the file name of a ``shell'' and arguments may be prepended
  	 * to the argument list if given here.
***************
*** 112,117
  		u.u_exdata.ux_tsize = 0;
  		break;
  
  	case 0413:
  	case 0410:
  		if (u.u_exdata.ux_tsize == 0) {

--- 113,119 -----
  		u.u_exdata.ux_tsize = 0;
  		break;
  
+ 	case 0420:
  	case 0413:
  	case 0410:
  		if (u.u_exdata.ux_tsize == 0) {
***************
*** 300,306
  	int nargc, uid, gid;
  {
  	register size_t ts, ds, ss;
! 	int pagi;
  
  	if (u.u_exdata.ux_mag == 0413)
  		pagi = SPAGI;

--- 302,308 -----
  	int nargc, uid, gid;
  {
  	register size_t ts, ds, ss;
! 	int pagi, pziv;
  
  	if (u.u_exdata.ux_mag == 0420) {
  		pagi = SPAGI;
***************
*** 302,308
  	register size_t ts, ds, ss;
  	int pagi;
  
! 	if (u.u_exdata.ux_mag == 0413)
  		pagi = SPAGI;
  	else
  		pagi = 0;

--- 304,310 -----
  	register size_t ts, ds, ss;
  	int pagi, pziv;
  
! 	if (u.u_exdata.ux_mag == 0420) {
  		pagi = SPAGI;
  		pziv = XPZIV;
  	} else if (u.u_exdata.ux_mag == 0413) {
***************
*** 304,310
  
  	if (u.u_exdata.ux_mag == 0413)
  		pagi = SPAGI;
! 	else
  		pagi = 0;
  	if (u.u_exdata.ux_tsize!=0 && (ip->i_flag&ITEXT)==0 &&
  	    ip->i_count!=1) {

--- 306,316 -----
  
  	if (u.u_exdata.ux_mag == 0420) {
  		pagi = SPAGI;
! 		pziv = XPZIV;
! 	} else if (u.u_exdata.ux_mag == 0413) {
! 		pagi = SPAGI;
! 		pziv = 0;
! 	} else {
  		pagi = 0;
  		pziv = 0;
  	}
***************
*** 306,311
  		pagi = SPAGI;
  	else
  		pagi = 0;
  	if (u.u_exdata.ux_tsize!=0 && (ip->i_flag&ITEXT)==0 &&
  	    ip->i_count!=1) {
  		register struct file *fp;

--- 312,319 -----
  		pziv = 0;
  	} else {
  		pagi = 0;
+ 		pziv = 0;
+ 	}
  	if (u.u_exdata.ux_tsize!=0 && (ip->i_flag&ITEXT)==0 &&
  	    ip->i_count!=1) {
  		register struct file *fp;
***************
*** 369,375
  			(int)u.u_exdata.ux_dsize,
  			(int)(sizeof(u.u_exdata)+u.u_exdata.ux_tsize),
  			0, (int *)0);
! 	xalloc(ip, pagi);
  	if (pagi && u.u_procp->p_textp)
  		vinifod((struct fpte *)dptopte(u.u_procp, 0),
  		    PG_FTEXT, u.u_procp->p_textp->x_iptr,

--- 377,383 -----
  			(int)u.u_exdata.ux_dsize,
  			(int)(sizeof(u.u_exdata)+u.u_exdata.ux_tsize),
  			0, (int *)0);
! 	xalloc(ip, pagi, pziv);
  	if (pagi && u.u_procp->p_textp)
  		vinifod((struct fpte *)dptopte(u.u_procp, 0),
  		    PG_FTEXT, u.u_procp->p_textp->x_iptr,

-------------------------------------------------------------------------

++ sys/vm_text.c
*** /tmp/,RCSt1002994	Fri Oct  5 17:07:42 1984
--- vm_text.c	Fri Sep 21 09:01:32 1984
***************
*** 58,64
   * If it is being used, but is not currently in core,
   * a swap has to be done to get it back.
   */
! xalloc(ip, pagi)
  register struct inode *ip;
  {
  	register struct text *xp;

--- 58,64 -----
   * If it is being used, but is not currently in core,
   * a swap has to be done to get it back.
   */
! xalloc(ip, pagi, pziv)
  register struct inode *ip;
  {
  	register struct text *xp;
***************
*** 94,100
  		return;
  	}
  	xp->x_flag = XLOAD|XLOCK;
! 	if (pagi)
  		xp->x_flag |= XPAGI;
  	ts = clrnd(btoc(u.u_exdata.ux_tsize));
  	xp->x_size = ts;

--- 94,100 -----
  		return;
  	}
  	xp->x_flag = XLOAD|XLOCK;
! 	if (pagi) {
  		xp->x_flag |= XPAGI;
  		if (pziv)
  			xp->x_flag |= XPZIV;
***************
*** 96,101
  	xp->x_flag = XLOAD|XLOCK;
  	if (pagi)
  		xp->x_flag |= XPAGI;
  	ts = clrnd(btoc(u.u_exdata.ux_tsize));
  	xp->x_size = ts;
  	if (vsxalloc(xp) == NULL) {

--- 96,104 -----
  	xp->x_flag = XLOAD|XLOCK;
  	if (pagi) {
  		xp->x_flag |= XPAGI;
+ 		if (pziv)
+ 			xp->x_flag |= XPZIV;
+ 	}
  	ts = clrnd(btoc(u.u_exdata.ux_tsize));
  	xp->x_size = ts;
  	if (vsxalloc(xp) == NULL) {
***************
*** 111,117
  	u.u_procp->p_textp = xp;
  	xlink(u.u_procp);
  	if (pagi == 0) {
! 		settprot(RW);
  		u.u_procp->p_flag |= SKEEP;
  		(void) rdwri(UIO_READ, ip,
  			(caddr_t)ctob(tptov(u.u_procp, 0)),

--- 114,120 -----
  	u.u_procp->p_textp = xp;
  	xlink(u.u_procp);
  	if (pagi == 0) {
! 		settprot(RW, 0);
  		u.u_procp->p_flag |= SKEEP;
  		(void) rdwri(UIO_READ, ip,
  			(caddr_t)ctob(tptov(u.u_procp, 0)),
***************
*** 119,125
  			2, (int *)0);
  		u.u_procp->p_flag &= ~SKEEP;
  	}
! 	settprot(RO);
  	u.u_segflg = 0;
  	xp->x_flag |= XWRIT;
  	xp->x_flag &= ~XLOAD;

--- 122,128 -----
  			2, (int *)0);
  		u.u_procp->p_flag &= ~SKEEP;
  	}
! 	settprot(RO, pziv);
  	u.u_segflg = 0;
  	xp->x_flag |= XWRIT;
  	xp->x_flag &= ~XLOAD;

-------------------------------------------------------------------------

++ sys/sys_process.c
*** /tmp/,RCSt1003000	Fri Oct  5 17:07:51 1984
--- sys_process.c	Fri Sep 21 09:22:04 1984
***************
*** 139,144
  				goto error;
  			xp->x_iptr->i_flag &= ~ITEXT;
  		}
  		i = -1;
  		if ((i = suiword((caddr_t)ipc.ip_addr, ipc.ip_data)) < 0) {
  			if (chgprot((caddr_t)ipc.ip_addr, RW) &&

--- 139,150 -----
  				goto error;
  			xp->x_iptr->i_flag &= ~ITEXT;
  		}
+ 		/*
+ 		 * Location must be accessible for read (could be invalid
+ 		 * if in page 0 and XPZIV is set.
+ 		 */
+ 		if (!useracc((caddr_t)ipc.ip_addr, 4, B_READ))
+ 			goto error;
  		i = -1;
  		if ((i = suiword((caddr_t)ipc.ip_addr, ipc.ip_data)) < 0) {
  			if (chgprot((caddr_t)ipc.ip_addr, RW) &&

-------------------------------------------------------------------------

++ vax/vm_machdep.c
*** /tmp/,RCSt1003006	Fri Oct  5 17:07:58 1984
--- vm_machdep.c	Fri Sep 21 09:04:55 1984
***************
*** 153,159
  	return (1);
  }
  
! settprot(tprot)
  	long tprot;
  {
  	register int *ptaddr, i;

--- 153,159 -----
  	return (1);
  }
  
! settprot(tprot, pziv)
  	long tprot;
  {
  	register int *ptaddr, i;
***************
*** 161,167
  	ptaddr = (int *)mfpr(P0BR);
  	for (i = 0; i < u.u_tsize; i++) {
  		ptaddr[i] &= ~PG_PROT;
! 		ptaddr[i] |= tprot;
  	}
  	mtpr(TBIA, 0);
  }

--- 161,168 -----
  	ptaddr = (int *)mfpr(P0BR);
  	for (i = 0; i < u.u_tsize; i++) {
  		ptaddr[i] &= ~PG_PROT;
! 		if (i >= CLSIZE || !pziv)
! 			ptaddr[i] |= tprot;
  	}
  	mtpr(TBIA, 0);
  }

-------------------------------------------------------------------------


++ ld.c
*** /tmp/,RCSt1009223	Thu Nov 29 09:51:56 1984
--- /tmp/,RCSt2009223	Thu Nov 29 09:52:15 1984
***************
*** 230,235
  int	nflag;		/* pure procedure */
  int	dflag;		/* define common even with rflag */
  int	zflag;		/* demand paged  */
  long	hsize;		/* size of hole at beginning of data to be squashed */
  int	Aflag;		/* doing incremental load */
  int	Nflag;		/* want impure a.out */

--- 230,236 -----
  int	nflag;		/* pure procedure */
  int	dflag;		/* define common even with rflag */
  int	zflag;		/* demand paged  */
+ int	Zflag;		/* demand paged, page 0 not mapped (invalid) */
  long	hsize;		/* size of hole at beginning of data to be squashed */
  int	Aflag;		/* doing incremental load */
  int	Nflag;		/* want impure a.out */
***************
*** 434,440
  			continue;
  		case 'n':
  			nflag++;
! 			Nflag = zflag = 0;
  			continue;
  		case 'N':
  			Nflag++;

--- 435,441 -----
  			continue;
  		case 'n':
  			nflag++;
! 			Nflag = zflag = Zflag = 0;
  			continue;
  		case 'N':
  			Nflag++;
***************
*** 438,444
  			continue;
  		case 'N':
  			Nflag++;
! 			nflag = zflag = 0;
  			continue;
  		case 'd':
  			dflag++;

--- 439,445 -----
  			continue;
  		case 'N':
  			Nflag++;
! 			nflag = zflag = Zflag = 0;
  			continue;
  		case 'd':
  			dflag++;
***************
*** 461,466
  			goto next;
  		case 'z':
  			zflag++;
  			Nflag = nflag = 0;
  			continue;
  		default:

--- 462,477 -----
  			goto next;
  		case 'z':
  			zflag++;
+ 			Nflag = nflag = Zflag = 0;
+ 			continue;
+ 		case 'Z':
+ 			if (tsize != 0)
+ 				error(1, "-Z: too late, some text already loaded (-z assumed)");
+ 			else {
+ 				Zflag++;
+ 				tsize = pagesize;
+ 			}
+ 			zflag++;	/* -Z implies -z */
  			Nflag = nflag = 0;
  			continue;
  		default:
***************
*** 541,546
  	filname = 0;
  	middle();
  	setupout();
  	p = argv+1;
  	for (c=1; c<argc; c++) {
  		ap = *p++;

--- 552,559 -----
  	filname = 0;
  	middle();
  	setupout();
+ 	if (Zflag)
+ 		torigin += pagesize;
  	p = argv+1;
  	for (c=1; c<argc; c++) {
  		ap = *p++;
***************
*** 1019,1025
  	}
  	tout = &toutb;
  	bopen(tout, 0);
! 	filhdr.a_magic = nflag ? NMAGIC : (zflag ? ZMAGIC : OMAGIC);
  	filhdr.a_text = nflag ? tsize :
  	    round(tsize, zflag ? pagesize : sizeof (long));
  	filhdr.a_data = zflag ? round(dsize, pagesize) : dsize;

--- 1032,1038 -----
  	}
  	tout = &toutb;
  	bopen(tout, 0);
! 	filhdr.a_magic = nflag ? NMAGIC : (zflag ? (Zflag ? Z0MAGIC : ZMAGIC) : OMAGIC);
  	filhdr.a_text = nflag ? tsize :
  	    round(tsize, zflag ? pagesize : sizeof (long));
  	filhdr.a_data = zflag ? round(dsize, pagesize) : dsize;
***************
*** 1036,1042
  		else
  			filhdr.a_entry = entrypt->n_value;
  	} else
! 		filhdr.a_entry = 0;
  	filhdr.a_trsize = (rflag ? trsize:0);
  	filhdr.a_drsize = (rflag ? drsize:0);
  	bwrite((char *)&filhdr, sizeof (filhdr), tout);

--- 1049,1055 -----
  		else
  			filhdr.a_entry = entrypt->n_value;
  	} else
! 		filhdr.a_entry = Zflag ? pagesize : 0;
  	filhdr.a_trsize = (rflag ? trsize:0);
  	filhdr.a_drsize = (rflag ? drsize:0);
  	bwrite((char *)&filhdr, sizeof (filhdr), tout);
***************
*** 1043,1049
  	if (zflag) {
  		bflush1(tout);
  		biobufs = 0;
! 		bopen(tout, pagesize);
  	}
  	wroff = N_TXTOFF(filhdr) + filhdr.a_text;
  	outb(&dout, filhdr.a_data);

--- 1056,1062 -----
  	if (zflag) {
  		bflush1(tout);
  		biobufs = 0;
! 		bopen(tout, Zflag ? (2*pagesize) : pagesize);
  	}
  	wroff = N_TXTOFF(filhdr) + filhdr.a_text;
  	outb(&dout, filhdr.a_data);

-------------------------------------------------------------------------


++ cc.c
*** /tmp/,RCSt1009253	Thu Nov 29 09:53:59 1984
--- /tmp/,RCSt2009253	Thu Nov 29 09:54:01 1984
***************
*** 22,28
  int	idexit();
  char	**av, **clist, **llist, **plist;
  int	cflag, eflag, oflag, pflag, sflag, wflag, Rflag, exflag, proflag;
! int	gflag, Gflag;
  char	*dflag;
  int	exfail;
  char	*chpass;

--- 22,28 -----
  int	idexit();
  char	**av, **clist, **llist, **plist;
  int	cflag, eflag, oflag, pflag, sflag, wflag, Rflag, exflag, proflag;
! int	gflag, Gflag, Zflag;
  char	*dflag;
  int	exfail;
  char	*chpass;
***************
*** 120,125
  		case 'd':
  			dflag = argv[i];
  			continue;
  		}
  		t = argv[i];
  		c = getsuf(t);

--- 120,128 -----
  		case 'd':
  			dflag = argv[i];
  			continue;
+ 		case 'Z':
+ 			Zflag++;
+ 			continue;
  		}
  		t = argv[i];
  		c = getsuf(t);
***************
*** 244,250
  nocom:
  	if (cflag==0 && nl!=0) {
  		i = 0;
! 		av[0] = "ld"; av[1] = "-X"; av[2] = crt0; na = 3;
  		if (outfile) {
  			av[na++] = "-o";
  			av[na++] = outfile;

--- 247,256 -----
  nocom:
  	if (cflag==0 && nl!=0) {
  		i = 0;
! 		av[0] = "ld"; av[1] = "-X"; na = 2;
! 		if (Zflag)
! 			av[na++] = "-Z";
! 		av[na++] = crt0;
  		if (outfile) {
  			av[na++] = "-o";
  			av[na++] = outfile;

-- 
  John Bruner (S-1 Project, Lawrence Livermore National Laboratory)
  MILNET: jdb at mordor.ARPA [jdb at s1-c]	(415) 422-0758
  UUCP: ...!ucbvax!dual!mordor!jdb 	...!decvax!decwrl!mordor!jdb



More information about the Comp.unix.wizards mailing list