jdb@mordor.UUCP (John Bruner) (11/29/84)
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@mordor.ARPA [jdb@s1-c] (415) 422-0758 UUCP: ...!ucbvax!dual!mordor!jdb ...!decvax!decwrl!mordor!jdb