The Go Programming Language

Text file src/cmd/6l/obj.c

     1	// Inferno utils/6l/obj.c
     2	// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c
     3	//
     4	//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
     5	//	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
     6	//	Portions Copyright © 1997-1999 Vita Nuova Limited
     7	//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
     8	//	Portions Copyright © 2004,2006 Bruce Ellis
     9	//	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
    10	//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
    11	//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
    12	//
    13	// Permission is hereby granted, free of charge, to any person obtaining a copy
    14	// of this software and associated documentation files (the "Software"), to deal
    15	// in the Software without restriction, including without limitation the rights
    16	// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    17	// copies of the Software, and to permit persons to whom the Software is
    18	// furnished to do so, subject to the following conditions:
    19	//
    20	// The above copyright notice and this permission notice shall be included in
    21	// all copies or substantial portions of the Software.
    22	//
    23	// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    24	// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    25	// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    26	// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    27	// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    28	// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    29	// THE SOFTWARE.
    30	
    31	// Reading object files.
    32	
    33	#define	EXTERN
    34	#include	"l.h"
    35	#include	"../ld/lib.h"
    36	#include	"../ld/elf.h"
    37	#include	"../ld/macho.h"
    38	#include	"../ld/dwarf.h"
    39	#include	"../ld/pe.h"
    40	#include	<ar.h>
    41	
    42	char	*noname		= "<none>";
    43	char*	thestring 	= "amd64";
    44	char*	paramspace	= "FP";
    45	
    46	Header headers[] = {
    47	   "plan9x32", Hplan9x32,
    48	   "plan9", Hplan9x64,
    49	   "elf", Helf,
    50	   "darwin", Hdarwin,
    51	   "linux", Hlinux,
    52	   "freebsd", Hfreebsd,
    53	   "openbsd", Hopenbsd,
    54	   "windows", Hwindows,
    55	   "windowsgui", Hwindows,
    56	   0, 0
    57	};
    58	
    59	/*
    60	 *	-Hplan9x32 -T4136 -R4096	is plan9 64-bit format
    61	 *	-Hplan9 -T4128 -R4096		is plan9 32-bit format
    62	 *	-Helf -T0x80110000 -R4096	is ELF32
    63	 *	-Hdarwin -Tx -Rx		is apple MH-exec
    64	 *	-Hlinux -Tx -Rx			is linux elf-exec
    65	 *	-Hfreebsd -Tx -Rx		is FreeBSD elf-exec
    66	 *	-Hopenbsd -Tx -Rx		is OpenBSD elf-exec
    67	 *	-Hwindows -Tx -Rx		is MS Windows PE32+
    68	 *
    69	 *	options used: 189BLQSWabcjlnpsvz
    70	 */
    71	
    72	void
    73	usage(void)
    74	{
    75		fprint(2, "usage: 6l [-options] [-E entry] [-H head] [-I interpreter] [-L dir] [-T text] [-R rnd] [-r path] [-o out] main.6\n");
    76		exits("usage");
    77	}
    78	
    79	void
    80	main(int argc, char *argv[])
    81	{
    82		int c;
    83	
    84		Binit(&bso, 1, OWRITE);
    85		listinit();
    86		memset(debug, 0, sizeof(debug));
    87		nerrors = 0;
    88		outfile = nil;
    89		HEADTYPE = -1;
    90		INITTEXT = -1;
    91		INITDAT = -1;
    92		INITRND = -1;
    93		INITENTRY = 0;
    94	
    95		ARGBEGIN {
    96		default:
    97			c = ARGC();
    98			if(c == 'l')
    99				usage();
   100	 		if(c >= 0 && c < sizeof(debug))
   101				debug[c]++;
   102			break;
   103		case 'o': /* output to (next arg) */
   104			outfile = EARGF(usage());
   105			break;
   106		case 'E':
   107			INITENTRY = EARGF(usage());
   108			break;
   109		case 'H':
   110			HEADTYPE = headtype(EARGF(usage()));
   111			break;
   112		case 'I':
   113			interpreter = EARGF(usage());
   114			break;
   115		case 'L':
   116			Lflag(EARGF(usage()));
   117			break;
   118		case 'T':
   119			INITTEXT = atolwhex(EARGF(usage()));
   120			break;
   121		case 'D':
   122			INITDAT = atolwhex(EARGF(usage()));
   123			break;
   124		case 'R':
   125			INITRND = atolwhex(EARGF(usage()));
   126			break;
   127		case 'r':
   128			rpath = EARGF(usage());
   129			break;
   130		case 'V':
   131			print("%cl version %s\n", thechar, getgoversion());
   132			errorexit();
   133		} ARGEND
   134	
   135		if(argc != 1)
   136			usage();
   137	
   138		mywhatsys();	// get goos
   139	
   140		if(HEADTYPE == -1)
   141			HEADTYPE = headtype(goos);
   142	
   143		if(outfile == nil) {
   144			if(HEADTYPE == Hwindows)
   145				outfile = "6.out.exe";
   146			else
   147				outfile = "6.out";
   148		}
   149	
   150		libinit();
   151	
   152		switch(HEADTYPE) {
   153		default:
   154			diag("unknown -H option");
   155			errorexit();
   156		case Hplan9x32:	/* plan 9 */
   157			HEADR = 32L+8L;
   158			if(INITTEXT == -1)
   159				INITTEXT = 4096+HEADR;
   160			if(INITDAT == -1)
   161				INITDAT = 0;
   162			if(INITRND == -1)
   163				INITRND = 4096;
   164			break;
   165		case Hplan9x64:	/* plan 9 */
   166			HEADR = 32L;
   167			if(INITTEXT == -1)
   168				INITTEXT = 4096+32;
   169			if(INITDAT == -1)
   170				INITDAT = 0;
   171			if(INITRND == -1)
   172				INITRND = 4096;
   173			break;
   174		case Helf:	/* elf32 executable */
   175			HEADR = rnd(52L+3*32L, 16);
   176			if(INITTEXT == -1)
   177				INITTEXT = 0x80110000L;
   178			if(INITDAT == -1)
   179				INITDAT = 0;
   180			if(INITRND == -1)
   181				INITRND = 4096;
   182			break;
   183		case Hdarwin:	/* apple MACH */
   184			/*
   185			 * OS X system constant - offset from 0(GS) to our TLS.
   186			 * Explained in ../../libcgo/darwin_amd64.c.
   187			 */
   188			tlsoffset = 0x8a0;
   189			machoinit();
   190			HEADR = INITIAL_MACHO_HEADR;
   191			if(INITRND == -1)
   192				INITRND = 4096;
   193			if(INITTEXT == -1)
   194				INITTEXT = 4096+HEADR;
   195			if(INITDAT == -1)
   196				INITDAT = 0;
   197			break;
   198		case Hlinux:	/* elf64 executable */
   199		case Hfreebsd:	/* freebsd */
   200		case Hopenbsd:	/* openbsd */
   201			/*
   202			 * ELF uses TLS offset negative from FS.
   203			 * Translate 0(FS) and 8(FS) into -16(FS) and -8(FS).
   204			 * Also known to ../../pkg/runtime/linux/amd64/sys.s
   205			 * and ../../libcgo/linux_amd64.s.
   206			 */
   207			tlsoffset = -16;
   208			elfinit();
   209			HEADR = ELFRESERVE;
   210			if(INITTEXT == -1)
   211				INITTEXT = (1<<22)+HEADR;
   212			if(INITDAT == -1)
   213				INITDAT = 0;
   214			if(INITRND == -1)
   215				INITRND = 4096;
   216			break;
   217		case Hwindows: /* PE executable */
   218			peinit();
   219			HEADR = PEFILEHEADR;
   220			if(INITTEXT == -1)
   221				INITTEXT = PEBASE+PESECTHEADR;
   222			if(INITDAT == -1)
   223				INITDAT = 0;
   224			if(INITRND == -1)
   225				INITRND = PESECTALIGN;
   226			break;
   227		}
   228		if(INITDAT != 0 && INITRND != 0)
   229			print("warning: -D0x%llux is ignored because of -R0x%ux\n",
   230				INITDAT, INITRND);
   231		if(debug['v'])
   232			Bprint(&bso, "HEADER = -H%d -T0x%llux -D0x%llux -R0x%ux\n",
   233				HEADTYPE, INITTEXT, INITDAT, INITRND);
   234		Bflush(&bso);
   235		instinit();
   236	
   237		zprg.link = P;
   238		zprg.pcond = P;
   239		zprg.back = 2;
   240		zprg.as = AGOK;
   241		zprg.from.type = D_NONE;
   242		zprg.from.index = D_NONE;
   243		zprg.from.scale = 1;
   244		zprg.to = zprg.from;
   245		zprg.mode = 64;
   246	
   247		pcstr = "%.6llux ";
   248		nuxiinit();
   249		histgen = 0;
   250		pc = 0;
   251		dtype = 4;
   252		version = 0;
   253		cbp = buf.cbuf;
   254		cbc = sizeof(buf.cbuf);
   255	
   256		addlibpath("command line", "command line", argv[0], "main");
   257		loadlib();
   258		deadcode();
   259		patch();
   260		follow();
   261		doelf();
   262		if(HEADTYPE == Hdarwin)
   263			domacho();
   264		dostkoff();
   265		dostkcheck();
   266		paramspace = "SP";	/* (FP) now (SP) on output */
   267		if(debug['p'])
   268			if(debug['1'])
   269				doprof1();
   270			else
   271				doprof2();
   272		span();
   273		if(HEADTYPE == Hwindows)
   274			dope();
   275		addexport();
   276		textaddress();
   277		pclntab();
   278		symtab();
   279		dodata();
   280		address();
   281		doweak();
   282		reloc();
   283		asmb();
   284		undef();
   285		if(debug['v']) {
   286			Bprint(&bso, "%5.2f cpu time\n", cputime());
   287			Bprint(&bso, "%d symbols\n", nsymbol);
   288			Bprint(&bso, "%d sizeof adr\n", sizeof(Adr));
   289			Bprint(&bso, "%d sizeof prog\n", sizeof(Prog));
   290		}
   291		Bflush(&bso);
   292	
   293		errorexit();
   294	}
   295	
   296	static Sym*
   297	zsym(char *pn, Biobuf *f, Sym *h[])
   298	{	
   299		int o;
   300		
   301		o = BGETC(f);
   302		if(o < 0 || o >= NSYM || h[o] == nil)
   303			mangle(pn);
   304		return h[o];
   305	}
   306	
   307	static void
   308	zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[])
   309	{
   310		int t;
   311		int32 l;
   312		Sym *s;
   313		Auto *u;
   314	
   315		t = BGETC(f);
   316		a->index = D_NONE;
   317		a->scale = 0;
   318		if(t & T_INDEX) {
   319			a->index = BGETC(f);
   320			a->scale = BGETC(f);
   321		}
   322		a->offset = 0;
   323		if(t & T_OFFSET) {
   324			a->offset = Bget4(f);
   325			if(t & T_64) {
   326				a->offset &= 0xFFFFFFFFULL;
   327				a->offset |= (vlong)Bget4(f) << 32;
   328			}
   329		}
   330		a->sym = S;
   331		if(t & T_SYM)
   332			a->sym = zsym(pn, f, h);
   333		a->type = D_NONE;
   334		if(t & T_FCONST) {
   335			a->ieee.l = Bget4(f);
   336			a->ieee.h = Bget4(f);
   337			a->type = D_FCONST;
   338		} else
   339		if(t & T_SCONST) {
   340			Bread(f, a->scon, NSNAME);
   341			a->type = D_SCONST;
   342		}
   343		if(t & T_TYPE)
   344			a->type = BGETC(f);
   345		if(a->type < 0 || a->type >= D_SIZE)
   346			mangle(pn);
   347		adrgotype = S;
   348		if(t & T_GOTYPE)
   349			adrgotype = zsym(pn, f, h);
   350		s = a->sym;
   351		t = a->type;
   352		if(t == D_INDIR+D_GS)
   353			a->offset += tlsoffset;
   354		if(t != D_AUTO && t != D_PARAM) {
   355			if(s && adrgotype)
   356				s->gotype = adrgotype;
   357			return;
   358		}
   359		l = a->offset;
   360		for(u=curauto; u; u=u->link) {
   361			if(u->asym == s)
   362			if(u->type == t) {
   363				if(u->aoffset > l)
   364					u->aoffset = l;
   365				if(adrgotype)
   366					u->gotype = adrgotype;
   367				return;
   368			}
   369		}
   370		
   371		switch(t) {
   372		case D_FILE:
   373		case D_FILE1:
   374		case D_AUTO:
   375		case D_PARAM:
   376			if(s == S)
   377				mangle(pn);
   378		}
   379	
   380		u = mal(sizeof(*u));
   381		u->link = curauto;
   382		curauto = u;
   383		u->asym = s;
   384		u->aoffset = l;
   385		u->type = t;
   386		u->gotype = adrgotype;
   387	}
   388	
   389	void
   390	nopout(Prog *p)
   391	{
   392		p->as = ANOP;
   393		p->from.type = D_NONE;
   394		p->to.type = D_NONE;
   395	}
   396	
   397	void
   398	ldobj1(Biobuf *f, char *pkg, int64 len, char *pn)
   399	{
   400		vlong ipc;
   401		Prog *p;
   402		int v, o, r, skip, mode;
   403		Sym *h[NSYM], *s;
   404		uint32 sig;
   405		char *name, *x;
   406		int ntext;
   407		vlong eof;
   408		char src[1024];
   409		Prog *lastp;
   410	
   411		lastp = nil;
   412		ntext = 0;
   413		eof = Boffset(f) + len;
   414		src[0] = 0;
   415	
   416	newloop:
   417		memset(h, 0, sizeof(h));
   418		version++;
   419		histfrogp = 0;
   420		ipc = pc;
   421		skip = 0;
   422		mode = 64;
   423	
   424	loop:
   425		if(f->state == Bracteof || Boffset(f) >= eof)
   426			goto eof;
   427		o = BGETC(f);
   428		if(o == Beof)
   429			goto eof;
   430		o |= BGETC(f) << 8;
   431		if(o <= AXXX || o >= ALAST) {
   432			if(o < 0)
   433				goto eof;
   434			diag("%s:#%lld: opcode out of range: %#ux", pn, Boffset(f), o);
   435			print("	probably not a .6 file\n");
   436			errorexit();
   437		}
   438	
   439		if(o == ANAME || o == ASIGNAME) {
   440			sig = 0;
   441			if(o == ASIGNAME)
   442				sig = Bget4(f);
   443			v = BGETC(f);	/* type */
   444			o = BGETC(f);	/* sym */
   445			r = 0;
   446			if(v == D_STATIC)
   447				r = version;
   448			name = Brdline(f, '\0');
   449			if(name == nil) {
   450				if(Blinelen(f) > 0) {
   451					fprint(2, "%s: name too long\n", pn);
   452					errorexit();
   453				}
   454				goto eof;
   455			}
   456			x = expandpkg(name, pkg);
   457			s = lookup(x, r);
   458			if(x != name)
   459				free(x);
   460	
   461			if(debug['S'] && r == 0)
   462				sig = 1729;
   463			if(sig != 0){
   464				if(s->sig != 0 && s->sig != sig)
   465					diag("incompatible type signatures"
   466						"%ux(%s) and %ux(%s) for %s",
   467						s->sig, s->file, sig, pn, s->name);
   468				s->sig = sig;
   469				s->file = pn;
   470			}
   471	
   472			if(debug['W'])
   473				print("	ANAME	%s\n", s->name);
   474			if(o < 0 || o >= nelem(h))
   475				mangle(pn);
   476			h[o] = s;
   477			if((v == D_EXTERN || v == D_STATIC) && s->type == 0)
   478				s->type = SXREF;
   479			if(v == D_FILE) {
   480				if(s->type != SFILE) {
   481					histgen++;
   482					s->type = SFILE;
   483					s->value = histgen;
   484				}
   485				if(histfrogp < MAXHIST) {
   486					histfrog[histfrogp] = s;
   487					histfrogp++;
   488				} else
   489					collapsefrog(s);
   490				dwarfaddfrag(s->value, s->name);
   491			}
   492			goto loop;
   493		}
   494	
   495		p = mal(sizeof(*p));
   496		p->as = o;
   497		p->line = Bget4(f);
   498		p->back = 2;
   499		p->mode = mode;
   500		p->ft = 0;
   501		p->tt = 0;
   502		zaddr(pn, f, &p->from, h);
   503		fromgotype = adrgotype;
   504		zaddr(pn, f, &p->to, h);
   505		
   506		switch(p->as) {
   507		case ATEXT:
   508		case ADATA:
   509		case AGLOBL:
   510			if(p->from.sym == S)
   511				mangle(pn);
   512			break;
   513		}
   514	
   515		if(debug['W'])
   516			print("%P\n", p);
   517	
   518		switch(p->as) {
   519		case AHISTORY:
   520			if(p->to.offset == -1) {
   521				addlib(src, pn);
   522				histfrogp = 0;
   523				goto loop;
   524			}
   525			if(src[0] == '\0')
   526				copyhistfrog(src, sizeof src);
   527			addhist(p->line, D_FILE);		/* 'z' */
   528			if(p->to.offset)
   529				addhist(p->to.offset, D_FILE1);	/* 'Z' */
   530			histfrogp = 0;
   531			goto loop;
   532	
   533		case AEND:
   534			histtoauto();
   535			if(cursym != nil && cursym->text)
   536				cursym->autom = curauto;
   537			curauto = 0;
   538			cursym = nil;
   539			if(Boffset(f) == eof)
   540				return;
   541			goto newloop;
   542	
   543		case AGLOBL:
   544			s = p->from.sym;
   545			if(s->type == 0 || s->type == SXREF) {
   546				s->type = SBSS;
   547				s->size = 0;
   548			}
   549			if(s->type != SBSS && !s->dupok) {
   550				diag("%s: redefinition: %s in %s",
   551					pn, s->name, TNAME);
   552				s->type = SBSS;
   553				s->size = 0;
   554			}
   555			if(p->to.offset > s->size)
   556				s->size = p->to.offset;
   557			if(p->from.scale & DUPOK)
   558				s->dupok = 1;
   559			if(p->from.scale & RODATA)
   560				s->type = SRODATA;
   561			goto loop;
   562	
   563		case ADATA:
   564			// Assume that AGLOBL comes after ADATA.
   565			// If we've seen an AGLOBL that said this sym was DUPOK,
   566			// ignore any more ADATA we see, which must be
   567			// redefinitions.
   568			s = p->from.sym;
   569			if(s->dupok) {
   570	//			if(debug['v'])
   571	//				Bprint(&bso, "skipping %s in %s: dupok\n", s->name, pn);
   572				goto loop;
   573			}
   574			if(s->file == nil)
   575				s->file = pn;
   576			else if(s->file != pn) {
   577				diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn);
   578				errorexit();
   579			}
   580			savedata(s, p, pn);
   581			unmal(p, sizeof *p);
   582			goto loop;
   583	
   584		case AGOK:
   585			diag("%s: GOK opcode in %s", pn, TNAME);
   586			pc++;
   587			goto loop;
   588	
   589		case ATEXT:
   590			s = p->from.sym;
   591			if(s->text != nil) {
   592				diag("%s: %s: redefinition", pn, s->name);
   593				return;
   594			}
   595			if(ntext++ == 0 && s->type != 0 && s->type != SXREF) {
   596				/* redefinition, so file has probably been seen before */
   597				if(debug['v'])
   598					Bprint(&bso, "skipping: %s: redefinition: %s", pn, s->name);
   599				return;
   600			}
   601			if(cursym != nil && cursym->text) {
   602				histtoauto();
   603				cursym->autom = curauto;
   604				curauto = 0;
   605			}
   606			skip = 0;
   607			if(etextp)
   608				etextp->next = s;
   609			else
   610				textp = s;
   611			etextp = s;
   612			s->text = p;
   613			cursym = s;
   614			if(s->type != 0 && s->type != SXREF) {
   615				if(p->from.scale & DUPOK) {
   616					skip = 1;
   617					goto casdef;
   618				}
   619				diag("%s: redefinition: %s\n%P", pn, s->name, p);
   620			}
   621			if(fromgotype) {
   622				if(s->gotype && s->gotype != fromgotype)
   623					diag("%s: type mismatch for %s", pn, s->name);
   624				s->gotype = fromgotype;
   625			}
   626			s->type = STEXT;
   627			s->value = pc;
   628			lastp = p;
   629			p->pc = pc++;
   630			goto loop;
   631	
   632		case AMODE:
   633			if(p->from.type == D_CONST || p->from.type == D_INDIR+D_NONE){
   634				switch((int)p->from.offset){
   635				case 16: case 32: case 64:
   636					mode = p->from.offset;
   637					break;
   638				}
   639			}
   640			goto loop;
   641	
   642		case AFMOVF:
   643		case AFADDF:
   644		case AFSUBF:
   645		case AFSUBRF:
   646		case AFMULF:
   647		case AFDIVF:
   648		case AFDIVRF:
   649		case AFCOMF:
   650		case AFCOMFP:
   651		case AMOVSS:
   652		case AADDSS:
   653		case ASUBSS:
   654		case AMULSS:
   655		case ADIVSS:
   656		case ACOMISS:
   657		case AUCOMISS:
   658			if(skip)
   659				goto casdef;
   660			if(p->from.type == D_FCONST) {
   661				/* size sb 9 max */
   662				sprint(literal, "$%ux", ieeedtof(&p->from.ieee));
   663				s = lookup(literal, 0);
   664				if(s->type == 0) {
   665					s->type = SDATA;
   666					adduint32(s, ieeedtof(&p->from.ieee));
   667					s->reachable = 0;
   668				}
   669				p->from.type = D_EXTERN;
   670				p->from.sym = s;
   671				p->from.offset = 0;
   672			}
   673			goto casdef;
   674	
   675		case AFMOVD:
   676		case AFADDD:
   677		case AFSUBD:
   678		case AFSUBRD:
   679		case AFMULD:
   680		case AFDIVD:
   681		case AFDIVRD:
   682		case AFCOMD:
   683		case AFCOMDP:
   684		case AMOVSD:
   685		case AADDSD:
   686		case ASUBSD:
   687		case AMULSD:
   688		case ADIVSD:
   689		case ACOMISD:
   690		case AUCOMISD:
   691			if(skip)
   692				goto casdef;
   693			if(p->from.type == D_FCONST) {
   694				/* size sb 18 max */
   695				sprint(literal, "$%ux.%ux",
   696					p->from.ieee.l, p->from.ieee.h);
   697				s = lookup(literal, 0);
   698				if(s->type == 0) {
   699					s->type = SDATA;
   700					adduint32(s, p->from.ieee.l);
   701					adduint32(s, p->from.ieee.h);
   702					s->reachable = 0;
   703				}
   704				p->from.type = D_EXTERN;
   705				p->from.sym = s;
   706				p->from.offset = 0;
   707			}
   708			goto casdef;
   709	
   710		casdef:
   711		default:
   712			if(skip)
   713				nopout(p);
   714			p->pc = pc;
   715			pc++;
   716	
   717			if(p->to.type == D_BRANCH)
   718				p->to.offset += ipc;
   719			if(lastp == nil) {
   720				if(p->as != ANOP)
   721					diag("unexpected instruction: %P", p);
   722				goto loop;
   723			}
   724			lastp->link = p;
   725			lastp = p;
   726			goto loop;
   727		}
   728	
   729	eof:
   730		diag("truncated object file: %s", pn);
   731	}
   732	
   733	Prog*
   734	prg(void)
   735	{
   736		Prog *p;
   737	
   738		p = mal(sizeof(*p));
   739	
   740		*p = zprg;
   741		return p;
   742	}
   743	
   744	Prog*
   745	copyp(Prog *q)
   746	{
   747		Prog *p;
   748	
   749		p = prg();
   750		*p = *q;
   751		return p;
   752	}
   753	
   754	Prog*
   755	appendp(Prog *q)
   756	{
   757		Prog *p;
   758	
   759		p = prg();
   760		p->link = q->link;
   761		q->link = p;
   762		p->line = q->line;
   763		p->mode = q->mode;
   764		return p;
   765	}

release.r60.3. Except as noted, this content is licensed under a Creative Commons Attribution 3.0 License.