The Go Programming Language

Text file src/libmach/executable.c

     1	// Inferno libmach/executable.c
     2	// http://code.google.com/p/inferno-os/source/browse/utils/libmach/executable.c
     3	//
     4	//	Copyright © 1994-1999 Lucent Technologies Inc.
     5	//	Power PC support Copyright © 1995-2004 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	//	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
     9	//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
    10	//
    11	// Permission is hereby granted, free of charge, to any person obtaining a copy
    12	// of this software and associated documentation files (the "Software"), to deal
    13	// in the Software without restriction, including without limitation the rights
    14	// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    15	// copies of the Software, and to permit persons to whom the Software is
    16	// furnished to do so, subject to the following conditions:
    17	//
    18	// The above copyright notice and this permission notice shall be included in
    19	// all copies or substantial portions of the Software.
    20	//
    21	// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    22	// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    23	// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    24	// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    25	// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    26	// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    27	// THE SOFTWARE.
    28	
    29	#include	<u.h>
    30	#include	<libc.h>
    31	#include	<bio.h>
    32	#include	<bootexec.h>
    33	#include	<mach.h>
    34	#include	"elf.h"
    35	#include	"macho.h"
    36	
    37	/*
    38	 *	All a.out header types.  The dummy entry allows canonical
    39	 *	processing of the union as a sequence of int32s
    40	 */
    41	
    42	typedef struct {
    43		union{
    44			/*struct { */
    45				Exec exechdr;		/* a.out.h */
    46			/*	uvlong hdr[1];*/
    47			/*};*/
    48			Ehdr32 elfhdr32;			/* elf.h */
    49			Ehdr64 elfhdr64;			/* elf.h */
    50			struct mipsexec mips;	/* bootexec.h */
    51			struct mips4kexec mipsk4;	/* bootexec.h */
    52			struct sparcexec sparc;	/* bootexec.h */
    53			struct nextexec next;	/* bootexec.h */
    54			Machhdr machhdr;	/* macho.h */
    55		} e;
    56		int32 dummy;			/* padding to ensure extra int32 */
    57	} ExecHdr;
    58	
    59	static	int	nextboot(int, Fhdr*, ExecHdr*);
    60	static	int	sparcboot(int, Fhdr*, ExecHdr*);
    61	static	int	mipsboot(int, Fhdr*, ExecHdr*);
    62	static	int	mips4kboot(int, Fhdr*, ExecHdr*);
    63	static	int	common(int, Fhdr*, ExecHdr*);
    64	static	int	commonllp64(int, Fhdr*, ExecHdr*);
    65	static	int	adotout(int, Fhdr*, ExecHdr*);
    66	static	int	elfdotout(int, Fhdr*, ExecHdr*);
    67	static	int	machdotout(int, Fhdr*, ExecHdr*);
    68	static	int	armdotout(int, Fhdr*, ExecHdr*);
    69	static	void	setsym(Fhdr*, vlong, int32, vlong, int32, vlong, int32);
    70	static	void	setdata(Fhdr*, uvlong, int32, vlong, int32);
    71	static	void	settext(Fhdr*, uvlong, uvlong, int32, vlong);
    72	static	void	hswal(void*, int, uint32(*)(uint32));
    73	static	uvlong	_round(uvlong, uint32);
    74	
    75	/*
    76	 *	definition of per-executable file type structures
    77	 */
    78	
    79	typedef struct Exectable{
    80		int32	magic;			/* big-endian magic number of file */
    81		char	*name;			/* executable identifier */
    82		char	*dlmname;		/* dynamically loadable module identifier */
    83		uchar	type;			/* Internal code */
    84		uchar	_magic;			/* _MAGIC() magic */
    85		Mach	*mach;			/* Per-machine data */
    86		int32	hsize;			/* header size */
    87		uint32	(*swal)(uint32);		/* beswal or leswal */
    88		int	(*hparse)(int, Fhdr*, ExecHdr*);
    89	} ExecTable;
    90	
    91	extern	Mach	mmips;
    92	extern	Mach	mmips2le;
    93	extern	Mach	mmips2be;
    94	extern	Mach	msparc;
    95	extern	Mach	msparc64;
    96	extern	Mach	m68020;
    97	extern	Mach	mi386;
    98	extern	Mach	mamd64;
    99	extern	Mach	marm;
   100	extern	Mach	mpower;
   101	extern	Mach	mpower64;
   102	extern	Mach	malpha;
   103	
   104	/* BUG: FIX THESE WHEN NEEDED */
   105	Mach	mmips;
   106	Mach	mmips2le;
   107	Mach	mmips2be;
   108	Mach	msparc;
   109	Mach	msparc64;
   110	Mach	m68020;
   111	Mach	mpower;
   112	Mach	mpower64;
   113	Mach	malpha;
   114	
   115	ExecTable exectab[] =
   116	{
   117		{ V_MAGIC,			/* Mips v.out */
   118			"mips plan 9 executable BE",
   119			"mips plan 9 dlm BE",
   120			FMIPS,
   121			1,
   122			&mmips,
   123			sizeof(Exec),
   124			beswal,
   125			adotout },
   126		{ P_MAGIC,			/* Mips 0.out (r3k le) */
   127			"mips plan 9 executable LE",
   128			"mips plan 9 dlm LE",
   129			FMIPSLE,
   130			1,
   131			&mmips,
   132			sizeof(Exec),
   133			beswal,
   134			adotout },
   135		{ M_MAGIC,			/* Mips 4.out */
   136			"mips 4k plan 9 executable BE",
   137			"mips 4k plan 9 dlm BE",
   138			FMIPS2BE,
   139			1,
   140			&mmips2be,
   141			sizeof(Exec),
   142			beswal,
   143			adotout },
   144		{ N_MAGIC,			/* Mips 0.out */
   145			"mips 4k plan 9 executable LE",
   146			"mips 4k plan 9 dlm LE",
   147			FMIPS2LE,
   148			1,
   149			&mmips2le,
   150			sizeof(Exec),
   151			beswal,
   152			adotout },
   153		{ 0x160<<16,			/* Mips boot image */
   154			"mips plan 9 boot image",
   155			nil,
   156			FMIPSB,
   157			0,
   158			&mmips,
   159			sizeof(struct mipsexec),
   160			beswal,
   161			mipsboot },
   162		{ (0x160<<16)|3,		/* Mips boot image */
   163			"mips 4k plan 9 boot image",
   164			nil,
   165			FMIPSB,
   166			0,
   167			&mmips2be,
   168			sizeof(struct mips4kexec),
   169			beswal,
   170			mips4kboot },
   171		{ K_MAGIC,			/* Sparc k.out */
   172			"sparc plan 9 executable",
   173			"sparc plan 9 dlm",
   174			FSPARC,
   175			1,
   176			&msparc,
   177			sizeof(Exec),
   178			beswal,
   179			adotout },
   180		{ 0x01030107, 			/* Sparc boot image */
   181			"sparc plan 9 boot image",
   182			nil,
   183			FSPARCB,
   184			0,
   185			&msparc,
   186			sizeof(struct sparcexec),
   187			beswal,
   188			sparcboot },
   189		{ U_MAGIC,			/* Sparc64 u.out */
   190			"sparc64 plan 9 executable",
   191			"sparc64 plan 9 dlm",
   192			FSPARC64,
   193			1,
   194			&msparc64,
   195			sizeof(Exec),
   196			beswal,
   197			adotout },
   198		{ A_MAGIC,			/* 68020 2.out & boot image */
   199			"68020 plan 9 executable",
   200			"68020 plan 9 dlm",
   201			F68020,
   202			1,
   203			&m68020,
   204			sizeof(Exec),
   205			beswal,
   206			common },
   207		{ 0xFEEDFACE,			/* Next boot image */
   208			"next plan 9 boot image",
   209			nil,
   210			FNEXTB,
   211			0,
   212			&m68020,
   213			sizeof(struct nextexec),
   214			beswal,
   215			nextboot },
   216		{ I_MAGIC,			/* I386 8.out & boot image */
   217			"386 plan 9 executable",
   218			"386 plan 9 dlm",
   219			FI386,
   220			1,
   221			&mi386,
   222			sizeof(Exec),
   223			beswal,
   224			common },
   225		{ S_MAGIC,			/* amd64 6.out & boot image */
   226			"amd64 plan 9 executable",
   227			"amd64 plan 9 dlm",
   228			FAMD64,
   229			1,
   230			&mamd64,
   231			sizeof(Exec)+8,
   232			nil,
   233			commonllp64 },
   234		{ Q_MAGIC,			/* PowerPC q.out & boot image */
   235			"power plan 9 executable",
   236			"power plan 9 dlm",
   237			FPOWER,
   238			1,
   239			&mpower,
   240			sizeof(Exec),
   241			beswal,
   242			common },
   243		{ T_MAGIC,			/* power64 9.out & boot image */
   244			"power64 plan 9 executable",
   245			"power64 plan 9 dlm",
   246			FPOWER64,
   247			1,
   248			&mpower64,
   249			sizeof(Exec)+8,
   250			nil,
   251			commonllp64 },
   252		{ ELF_MAG,			/* any elf32 or elf64 */
   253			"elf executable",
   254			nil,
   255			FNONE,
   256			0,
   257			&mi386,
   258			sizeof(Ehdr64),
   259			nil,
   260			elfdotout },
   261		{ MACH64_MAG,			/* 64-bit MACH (apple mac) */
   262			"mach executable",
   263			nil,
   264			FAMD64,
   265			0,
   266			&mamd64,
   267			sizeof(Machhdr),
   268			nil,
   269			machdotout },
   270		{ MACH32_MAG,			/* 32-bit MACH (apple mac) */
   271			"mach executable",
   272			nil,
   273			FI386,
   274			0,
   275			&mi386,
   276			sizeof(Machhdr),
   277			nil,
   278			machdotout },
   279		{ E_MAGIC,			/* Arm 5.out and boot image */
   280			"arm plan 9 executable",
   281			"arm plan 9 dlm",
   282			FARM,
   283			1,
   284			&marm,
   285			sizeof(Exec),
   286			beswal,
   287			common },
   288		{ (143<<16)|0413,		/* (Free|Net)BSD Arm */
   289			"arm *bsd executable",
   290			nil,
   291			FARM,
   292			0,
   293			&marm,
   294			sizeof(Exec),
   295			leswal,
   296			armdotout },
   297		{ L_MAGIC,			/* alpha 7.out */
   298			"alpha plan 9 executable",
   299			"alpha plan 9 dlm",
   300			FALPHA,
   301			1,
   302			&malpha,
   303			sizeof(Exec),
   304			beswal,
   305			common },
   306		{ 0x0700e0c3,			/* alpha boot image */
   307			"alpha plan 9 boot image",
   308			nil,
   309			FALPHA,
   310			0,
   311			&malpha,
   312			sizeof(Exec),
   313			beswal,
   314			common },
   315		{ 0 },
   316	};
   317	
   318	Mach	*mach = &mi386;			/* Global current machine table */
   319	
   320	static ExecTable*
   321	couldbe4k(ExecTable *mp)
   322	{
   323		Dir *d;
   324		ExecTable *f;
   325	
   326		if((d=dirstat("/proc/1/regs")) == nil)
   327			return mp;
   328		if(d->length < 32*8){		/* R3000 */
   329			free(d);
   330			return mp;
   331		}
   332		free(d);
   333		for (f = exectab; f->magic; f++)
   334			if(f->magic == M_MAGIC) {
   335				f->name = "mips plan 9 executable on mips2 kernel";
   336				return f;
   337			}
   338		return mp;
   339	}
   340	
   341	int
   342	crackhdr(int fd, Fhdr *fp)
   343	{
   344		ExecTable *mp;
   345		ExecHdr d;
   346		int nb, ret;
   347		uint32 magic;
   348	
   349		fp->type = FNONE;
   350		nb = read(fd, (char *)&d.e, sizeof(d.e));
   351		if (nb <= 0)
   352			return 0;
   353	
   354		ret = 0;
   355		magic = beswal(d.e.exechdr.magic);		/* big-endian */
   356		for (mp = exectab; mp->magic; mp++) {
   357			if (nb < mp->hsize)
   358				continue;
   359	
   360			/*
   361			 * The magic number has morphed into something
   362			 * with fields (the straw was DYN_MAGIC) so now
   363			 * a flag is needed in Fhdr to distinguish _MAGIC()
   364			 * magic numbers from foreign magic numbers.
   365			 *
   366			 * This code is creaking a bit and if it has to
   367			 * be modified/extended much more it's probably
   368			 * time to step back and redo it all.
   369			 */
   370			if(mp->_magic){
   371				if(mp->magic != (magic & ~DYN_MAGIC))
   372					continue;
   373	
   374				if(mp->magic == V_MAGIC)
   375					mp = couldbe4k(mp);
   376	
   377				if ((magic & DYN_MAGIC) && mp->dlmname != nil)
   378					fp->name = mp->dlmname;
   379				else
   380					fp->name = mp->name;
   381			}
   382			else{
   383				if(mp->magic != magic)
   384					continue;
   385				fp->name = mp->name;
   386			}
   387			fp->type = mp->type;
   388			fp->hdrsz = mp->hsize;		/* will be zero on bootables */
   389			fp->_magic = mp->_magic;
   390			fp->magic = magic;
   391	
   392			mach = mp->mach;
   393			if(mp->swal != nil)
   394				hswal(&d, sizeof(d.e)/sizeof(uint32), mp->swal);
   395			ret = mp->hparse(fd, fp, &d);
   396			seek(fd, mp->hsize, 0);		/* seek to end of header */
   397			break;
   398		}
   399		if(mp->magic == 0)
   400			werrstr("unknown header type");
   401		return ret;
   402	}
   403	
   404	/*
   405	 * Convert header to canonical form
   406	 */
   407	static void
   408	hswal(void *v, int n, uint32 (*swap)(uint32))
   409	{
   410		uint32 *ulp;
   411	
   412		for(ulp = v; n--; ulp++)
   413			*ulp = (*swap)(*ulp);
   414	}
   415	
   416	/*
   417	 *	Crack a normal a.out-type header
   418	 */
   419	static int
   420	adotout(int fd, Fhdr *fp, ExecHdr *hp)
   421	{
   422		int32 pgsize;
   423	
   424		USED(fd);
   425		pgsize = mach->pgsize;
   426		settext(fp, hp->e.exechdr.entry, pgsize+sizeof(Exec),
   427				hp->e.exechdr.text, sizeof(Exec));
   428		setdata(fp, _round(pgsize+fp->txtsz+sizeof(Exec), pgsize),
   429			hp->e.exechdr.data, fp->txtsz+sizeof(Exec), hp->e.exechdr.bss);
   430		setsym(fp, fp->datoff+fp->datsz, hp->e.exechdr.syms, 0, hp->e.exechdr.spsz, 0, hp->e.exechdr.pcsz);
   431		return 1;
   432	}
   433	
   434	static void
   435	commonboot(Fhdr *fp)
   436	{
   437		if (!(fp->entry & mach->ktmask))
   438			return;
   439	
   440		switch(fp->type) {				/* boot image */
   441		case F68020:
   442			fp->type = F68020B;
   443			fp->name = "68020 plan 9 boot image";
   444			break;
   445		case FI386:
   446			fp->type = FI386B;
   447			fp->txtaddr = (u32int)fp->entry;
   448			fp->name = "386 plan 9 boot image";
   449			fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
   450			break;
   451		case FARM:
   452			fp->type = FARMB;
   453			fp->txtaddr = (u32int)fp->entry;
   454			fp->name = "ARM plan 9 boot image";
   455			fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
   456			return;
   457		case FALPHA:
   458			fp->type = FALPHAB;
   459			fp->txtaddr = (u32int)fp->entry;
   460			fp->name = "alpha plan 9 boot image";
   461			fp->dataddr = fp->txtaddr+fp->txtsz;
   462			break;
   463		case FPOWER:
   464			fp->type = FPOWERB;
   465			fp->txtaddr = (u32int)fp->entry;
   466			fp->name = "power plan 9 boot image";
   467			fp->dataddr = fp->txtaddr+fp->txtsz;
   468			break;
   469		case FAMD64:
   470			fp->type = FAMD64B;
   471			fp->txtaddr = fp->entry;
   472			fp->name = "amd64 plan 9 boot image";
   473			fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
   474			break;
   475		default:
   476			return;
   477		}
   478		fp->hdrsz = 0;			/* header stripped */
   479	}
   480	
   481	/*
   482	 *	_MAGIC() style headers and
   483	 *	alpha plan9-style bootable images for axp "headerless" boot
   484	 *
   485	 */
   486	static int
   487	common(int fd, Fhdr *fp, ExecHdr *hp)
   488	{
   489		adotout(fd, fp, hp);
   490		if(hp->e.exechdr.magic & DYN_MAGIC) {
   491			fp->txtaddr = 0;
   492			fp->dataddr = fp->txtsz;
   493			return 1;
   494		}
   495		commonboot(fp);
   496		return 1;
   497	}
   498	
   499	static int
   500	commonllp64(int unused, Fhdr *fp, ExecHdr *hp)
   501	{
   502		int32 pgsize;
   503		uvlong entry;
   504	
   505		hswal(&hp->e, sizeof(Exec)/sizeof(int32), beswal);
   506		if(!(hp->e.exechdr.magic & HDR_MAGIC))
   507			return 0;
   508	
   509		/*
   510		 * There can be more magic here if the
   511		 * header ever needs more expansion.
   512		 * For now just catch use of any of the
   513		 * unused bits.
   514		 */
   515		if((hp->e.exechdr.magic & ~DYN_MAGIC)>>16)
   516			return 0;
   517		union {
   518			char *p;
   519			uvlong *v;
   520		} u;
   521		u.p = (char*)&hp->e.exechdr;
   522		entry = beswav(*u.v);
   523	
   524		pgsize = mach->pgsize;
   525		settext(fp, entry, pgsize+fp->hdrsz, hp->e.exechdr.text, fp->hdrsz);
   526		setdata(fp, _round(pgsize+fp->txtsz+fp->hdrsz, pgsize),
   527			hp->e.exechdr.data, fp->txtsz+fp->hdrsz, hp->e.exechdr.bss);
   528		setsym(fp, fp->datoff+fp->datsz, hp->e.exechdr.syms, 0, hp->e.exechdr.spsz, 0, hp->e.exechdr.pcsz);
   529	
   530		if(hp->e.exechdr.magic & DYN_MAGIC) {
   531			fp->txtaddr = 0;
   532			fp->dataddr = fp->txtsz;
   533			return 1;
   534		}
   535		commonboot(fp);
   536		return 1;
   537	}
   538	
   539	/*
   540	 *	mips bootable image.
   541	 */
   542	static int
   543	mipsboot(int fd, Fhdr *fp, ExecHdr *hp)
   544	{
   545	abort();
   546	#ifdef unused
   547		USED(fd);
   548		fp->type = FMIPSB;
   549		switch(hp->e.exechdr.amagic) {
   550		default:
   551		case 0407:	/* some kind of mips */
   552			settext(fp, (u32int)hp->e.mentry, (u32int)hp->e.text_start,
   553				hp->e.tsize, sizeof(struct mipsexec)+4);
   554			setdata(fp, (u32int)hp->e.data_start, hp->e.dsize,
   555				fp->txtoff+hp->e.tsize, hp->e.bsize);
   556			break;
   557		case 0413:	/* some kind of mips */
   558			settext(fp, (u32int)hp->e.mentry, (u32int)hp->e.text_start,
   559				hp->e.tsize, 0);
   560			setdata(fp, (u32int)hp->e.data_start, hp->e.dsize,
   561				hp->e.tsize, hp->e.bsize);
   562			break;
   563		}
   564		setsym(fp, hp->e.nsyms, 0, hp->e.pcsize, hp->e.symptr);
   565		fp->hdrsz = 0;			/* header stripped */
   566	#endif
   567		return 1;
   568	}
   569	
   570	/*
   571	 *	mips4k bootable image.
   572	 */
   573	static int
   574	mips4kboot(int fd, Fhdr *fp, ExecHdr *hp)
   575	{
   576	abort();
   577	#ifdef unused
   578		USED(fd);
   579		fp->type = FMIPSB;
   580		switch(hp->e.h.amagic) {
   581		default:
   582		case 0407:	/* some kind of mips */
   583			settext(fp, (u32int)hp->e.h.mentry, (u32int)hp->e.h.text_start,
   584				hp->e.h.tsize, sizeof(struct mips4kexec));
   585			setdata(fp, (u32int)hp->e.h.data_start, hp->e.h.dsize,
   586				fp->txtoff+hp->e.h.tsize, hp->e.h.bsize);
   587			break;
   588		case 0413:	/* some kind of mips */
   589			settext(fp, (u32int)hp->e.h.mentry, (u32int)hp->e.h.text_start,
   590				hp->e.h.tsize, 0);
   591			setdata(fp, (u32int)hp->e.h.data_start, hp->e.h.dsize,
   592				hp->e.h.tsize, hp->e.h.bsize);
   593			break;
   594		}
   595		setsym(fp, hp->e.h.nsyms, 0, hp->e.h.pcsize, hp->e.h.symptr);
   596		fp->hdrsz = 0;			/* header stripped */
   597	#endif
   598		return 1;
   599	}
   600	
   601	/*
   602	 *	sparc bootable image
   603	 */
   604	static int
   605	sparcboot(int fd, Fhdr *fp, ExecHdr *hp)
   606	{
   607	abort();
   608	#ifdef unused
   609		USED(fd);
   610		fp->type = FSPARCB;
   611		settext(fp, hp->e.sentry, hp->e.sentry, hp->e.stext,
   612			sizeof(struct sparcexec));
   613		setdata(fp, hp->e.sentry+hp->e.stext, hp->e.sdata,
   614			fp->txtoff+hp->e.stext, hp->e.sbss);
   615		setsym(fp, hp->e.ssyms, 0, hp->e.sdrsize, fp->datoff+hp->e.sdata);
   616		fp->hdrsz = 0;			/* header stripped */
   617	#endif
   618		return 1;
   619	}
   620	
   621	/*
   622	 *	next bootable image
   623	 */
   624	static int
   625	nextboot(int fd, Fhdr *fp, ExecHdr *hp)
   626	{
   627	abort();
   628	#ifdef unused
   629		USED(fd);
   630		fp->type = FNEXTB;
   631		settext(fp, hp->e.textc.vmaddr, hp->e.textc.vmaddr,
   632			hp->e.texts.size, hp->e.texts.offset);
   633		setdata(fp, hp->e.datac.vmaddr, hp->e.datas.size,
   634			hp->e.datas.offset, hp->e.bsss.size);
   635		setsym(fp, hp->e.symc.nsyms, hp->e.symc.spoff, hp->e.symc.pcoff,
   636			hp->e.symc.symoff);
   637		fp->hdrsz = 0;			/* header stripped */
   638	#endif
   639		return 1;
   640	}
   641	
   642	/*
   643	 * Elf32 and Elf64 binaries.
   644	 */
   645	static int
   646	elf64dotout(int fd, Fhdr *fp, ExecHdr *hp)
   647	{
   648	
   649		uvlong (*swav)(uvlong);
   650		uint32 (*swal)(uint32);
   651		ushort (*swab)(ushort);
   652		Ehdr64 *ep;
   653		Phdr64 *ph;
   654		Shdr64 *sh;
   655		int i, it, id, is, phsz, shsz;
   656	
   657		/* bitswap the header according to the DATA format */
   658		ep = &hp->e.elfhdr64;
   659		if(ep->ident[CLASS] != ELFCLASS64) {
   660			werrstr("bad ELF class - not 32 bit or 64 bit");
   661			return 0;
   662		}
   663		if(ep->ident[DATA] == ELFDATA2LSB) {
   664			swab = leswab;
   665			swal = leswal;
   666			swav = leswav;
   667		} else if(ep->ident[DATA] == ELFDATA2MSB) {
   668			swab = beswab;
   669			swal = beswal;
   670			swav = beswav;
   671		} else {
   672			werrstr("bad ELF encoding - not big or little endian");
   673			return 0;
   674		}
   675	
   676		ep->type = swab(ep->type);
   677		ep->machine = swab(ep->machine);
   678		ep->version = swal(ep->version);
   679		ep->elfentry = swal(ep->elfentry);
   680		ep->phoff = swav(ep->phoff);
   681		ep->shoff = swav(ep->shoff);
   682		ep->flags = swav(ep->flags);
   683		ep->ehsize = swab(ep->ehsize);
   684		ep->phentsize = swab(ep->phentsize);
   685		ep->phnum = swab(ep->phnum);
   686		ep->shentsize = swab(ep->shentsize);
   687		ep->shnum = swab(ep->shnum);
   688		ep->shstrndx = swab(ep->shstrndx);
   689		if(ep->type != EXEC || ep->version != CURRENT)
   690			return 0;
   691	
   692		/* we could definitely support a lot more machines here */
   693		fp->magic = ELF_MAG;
   694		fp->hdrsz = (ep->ehsize+ep->phnum*ep->phentsize+16)&~15;
   695		switch(ep->machine) {
   696		case AMD64:
   697			mach = &mamd64;
   698			fp->type = FAMD64;
   699			break;
   700		default:
   701			return 0;
   702		}
   703	
   704		if(ep->phentsize != sizeof(Phdr64)) {
   705			werrstr("bad ELF header size");
   706			return 0;
   707		}
   708		phsz = sizeof(Phdr64)*ep->phnum;
   709		ph = malloc(phsz);
   710		if(!ph)
   711			return 0;
   712		seek(fd, ep->phoff, 0);
   713		if(read(fd, ph, phsz) < 0) {
   714			free(ph);
   715			return 0;
   716		}
   717		hswal(ph, phsz/sizeof(uint32), swal);
   718	
   719		shsz = sizeof(Shdr64)*ep->shnum;
   720		sh = malloc(shsz);
   721		if(sh) {
   722			seek(fd, ep->shoff, 0);
   723			if(read(fd, sh, shsz) < 0) {
   724				free(sh);
   725				sh = 0;
   726			} else
   727				hswal(sh, shsz/sizeof(uint32), swal);
   728		}
   729	
   730		/* find text, data and symbols and install them */
   731		it = id = is = -1;
   732		for(i = 0; i < ep->phnum; i++) {
   733			if(ph[i].type == LOAD
   734			&& (ph[i].flags & (R|X)) == (R|X) && it == -1)
   735				it = i;
   736			else if(ph[i].type == LOAD
   737			&& (ph[i].flags & (R|W)) == (R|W) && id == -1)
   738				id = i;
   739			else if(ph[i].type == NOPTYPE && is == -1)
   740				is = i;
   741		}
   742		if(it == -1 || id == -1) {
   743			/*
   744			 * The SPARC64 boot image is something of an ELF hack.
   745			 * Text+Data+BSS are represented by ph[0].  Symbols
   746			 * are represented by ph[1]:
   747			 *
   748			 *		filesz, memsz, vaddr, paddr, off
   749			 * ph[0] : txtsz+datsz, txtsz+datsz+bsssz, txtaddr-KZERO, datasize, txtoff
   750			 * ph[1] : symsz, lcsz, 0, 0, symoff
   751			 */
   752			if(ep->machine == SPARC64 && ep->phnum == 2) {
   753				uint32 txtaddr, txtsz, dataddr, bsssz;
   754	
   755				txtaddr = ph[0].vaddr | 0x80000000;
   756				txtsz = ph[0].filesz - ph[0].paddr;
   757				dataddr = txtaddr + txtsz;
   758				bsssz = ph[0].memsz - ph[0].filesz;
   759				settext(fp, ep->elfentry | 0x80000000, txtaddr, txtsz, ph[0].offset);
   760				setdata(fp, dataddr, ph[0].paddr, ph[0].offset + txtsz, bsssz);
   761				setsym(fp, ph[1].offset, ph[1].filesz, 0, 0, 0, ph[1].memsz);
   762				free(ph);
   763				return 1;
   764			}
   765	
   766			werrstr("No TEXT or DATA sections");
   767			free(ph);
   768			free(sh);
   769			return 0;
   770		}
   771	
   772		settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset);
   773		setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, ph[id].memsz - ph[id].filesz);
   774		if(is != -1)
   775			setsym(fp, ph[is].offset, ph[is].filesz, 0, 0, 0, ph[is].memsz);
   776		else if(sh != 0){
   777			char *buf;
   778			uvlong symsize = 0;
   779			uvlong symoff = 0;
   780			uvlong pclnsz = 0;
   781			uvlong pclnoff = 0;
   782	
   783			/* load shstrtab names */
   784			buf = malloc(sh[ep->shstrndx].size);
   785			if (buf == 0)
   786				goto done;
   787			memset(buf, 0, sizeof buf);
   788			seek(fd, sh[ep->shstrndx].offset, 0);
   789			i = read(fd, buf, sh[ep->shstrndx].size);
   790			USED(i);	// shut up ubuntu gcc
   791	
   792			for(i = 0; i < ep->shnum; i++) {
   793				if (strcmp(&buf[sh[i].name], ".gosymtab") == 0) {
   794					symsize = sh[i].size;
   795					symoff = sh[i].offset;
   796				}
   797				if (strcmp(&buf[sh[i].name], ".gopclntab") == 0) {
   798					pclnsz = sh[i].size;
   799					pclnoff = sh[i].offset;
   800				}
   801			}
   802			setsym(fp, symoff, symsize, 0, 0, pclnoff, pclnsz);
   803			free(buf);
   804		}
   805	done:
   806		free(ph);
   807		free(sh);
   808		return 1;
   809	}
   810	
   811	static int
   812	elfdotout(int fd, Fhdr *fp, ExecHdr *hp)
   813	{
   814	
   815		uint32 (*swal)(uint32);
   816		ushort (*swab)(ushort);
   817		Ehdr32 *ep;
   818		Phdr32 *ph;
   819		int i, it, id, is, phsz, shsz;
   820		Shdr32 *sh;
   821	
   822		/* bitswap the header according to the DATA format */
   823		ep = &hp->e.elfhdr32;
   824		if(ep->ident[CLASS] != ELFCLASS32) {
   825			return elf64dotout(fd, fp, hp);
   826		}
   827		if(ep->ident[DATA] == ELFDATA2LSB) {
   828			swab = leswab;
   829			swal = leswal;
   830		} else if(ep->ident[DATA] == ELFDATA2MSB) {
   831			swab = beswab;
   832			swal = beswal;
   833		} else {
   834			werrstr("bad ELF encoding - not big or little endian");
   835			return 0;
   836		}
   837	
   838		ep->type = swab(ep->type);
   839		ep->machine = swab(ep->machine);
   840		ep->version = swal(ep->version);
   841		ep->elfentry = swal(ep->elfentry);
   842		ep->phoff = swal(ep->phoff);
   843		ep->shoff = swal(ep->shoff);
   844		ep->flags = swal(ep->flags);
   845		ep->ehsize = swab(ep->ehsize);
   846		ep->phentsize = swab(ep->phentsize);
   847		ep->phnum = swab(ep->phnum);
   848		ep->shentsize = swab(ep->shentsize);
   849		ep->shnum = swab(ep->shnum);
   850		ep->shstrndx = swab(ep->shstrndx);
   851		if(ep->type != EXEC || ep->version != CURRENT)
   852			return 0;
   853	
   854		/* we could definitely support a lot more machines here */
   855		fp->magic = ELF_MAG;
   856		fp->hdrsz = (ep->ehsize+ep->phnum*ep->phentsize+16)&~15;
   857		switch(ep->machine) {
   858		case I386:
   859			mach = &mi386;
   860			fp->type = FI386;
   861			break;
   862		case MIPS:
   863			mach = &mmips;
   864			fp->type = FMIPS;
   865			break;
   866		case SPARC64:
   867			mach = &msparc64;
   868			fp->type = FSPARC64;
   869			break;
   870		case POWER:
   871			mach = &mpower;
   872			fp->type = FPOWER;
   873			break;
   874		case ARM:
   875			mach = &marm;
   876			fp->type = FARM;
   877			break;
   878		default:
   879			return 0;
   880		}
   881	
   882		if(ep->phentsize != sizeof(Phdr32)) {
   883			werrstr("bad ELF header size");
   884			return 0;
   885		}
   886		phsz = sizeof(Phdr32)*ep->phnum;
   887		ph = malloc(phsz);
   888		if(!ph)
   889			return 0;
   890		seek(fd, ep->phoff, 0);
   891		if(read(fd, ph, phsz) < 0) {
   892			free(ph);
   893			return 0;
   894		}
   895		hswal(ph, phsz/sizeof(uint32), swal);
   896	
   897		shsz = sizeof(Shdr32)*ep->shnum;
   898		sh = malloc(shsz);
   899		if(sh) {
   900			seek(fd, ep->shoff, 0);
   901			if(read(fd, sh, shsz) < 0) {
   902				free(sh);
   903				sh = 0;
   904			} else
   905				hswal(sh, shsz/sizeof(uint32), swal);
   906		}
   907	
   908		/* find text, data and symbols and install them */
   909		it = id = is = -1;
   910		for(i = 0; i < ep->phnum; i++) {
   911			if(ph[i].type == LOAD
   912			&& (ph[i].flags & (R|X)) == (R|X) && it == -1)
   913				it = i;
   914			else if(ph[i].type == LOAD
   915			&& (ph[i].flags & (R|W)) == (R|W) && id == -1)
   916				id = i;
   917			else if(ph[i].type == NOPTYPE && is == -1)
   918				is = i;
   919		}
   920		if(it == -1 || id == -1) {
   921			/*
   922			 * The SPARC64 boot image is something of an ELF hack.
   923			 * Text+Data+BSS are represented by ph[0].  Symbols
   924			 * are represented by ph[1]:
   925			 *
   926			 *		filesz, memsz, vaddr, paddr, off
   927			 * ph[0] : txtsz+datsz, txtsz+datsz+bsssz, txtaddr-KZERO, datasize, txtoff
   928			 * ph[1] : symsz, lcsz, 0, 0, symoff
   929			 */
   930			if(ep->machine == SPARC64 && ep->phnum == 2) {
   931				uint32 txtaddr, txtsz, dataddr, bsssz;
   932	
   933				txtaddr = ph[0].vaddr | 0x80000000;
   934				txtsz = ph[0].filesz - ph[0].paddr;
   935				dataddr = txtaddr + txtsz;
   936				bsssz = ph[0].memsz - ph[0].filesz;
   937				settext(fp, ep->elfentry | 0x80000000, txtaddr, txtsz, ph[0].offset);
   938				setdata(fp, dataddr, ph[0].paddr, ph[0].offset + txtsz, bsssz);
   939				setsym(fp, ph[1].offset, ph[1].filesz, 0, 0, 0, ph[1].memsz);
   940				free(ph);
   941				return 1;
   942			}
   943	
   944			werrstr("No TEXT or DATA sections");
   945			free(sh);
   946			free(ph);
   947			return 0;
   948		}
   949	
   950		settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset);
   951		setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, ph[id].memsz - ph[id].filesz);
   952		if(is != -1)
   953			setsym(fp, ph[is].offset, ph[is].filesz, 0, 0, 0, ph[is].memsz);
   954		else if(sh != 0){
   955			char *buf;
   956			uvlong symsize = 0;
   957			uvlong symoff = 0;
   958			uvlong pclnsize = 0;
   959			uvlong pclnoff = 0;
   960	
   961			/* load shstrtab names */
   962			buf = malloc(sh[ep->shstrndx].size);
   963			if (buf == 0)
   964				goto done;
   965			memset(buf, 0, sizeof buf);
   966			seek(fd, sh[ep->shstrndx].offset, 0);
   967			i = read(fd, buf, sh[ep->shstrndx].size);
   968			USED(i);	// shut up ubuntu gcc
   969	
   970			for(i = 0; i < ep->shnum; i++) {
   971				if (strcmp(&buf[sh[i].name], ".gosymtab") == 0) {
   972					symsize = sh[i].size;
   973					symoff = sh[i].offset;
   974				}
   975				if (strcmp(&buf[sh[i].name], ".gopclntab") == 0) {
   976					pclnsize = sh[i].size;
   977					pclnoff = sh[i].offset;
   978				}
   979			}
   980			setsym(fp, symoff, symsize, 0, 0, pclnoff, pclnsize);
   981			free(buf);
   982		}
   983	done:
   984		free(sh);
   985		free(ph);
   986		return 1;
   987	}
   988	
   989	static int
   990	machdotout(int fd, Fhdr *fp, ExecHdr *hp)
   991	{
   992		uvlong (*swav)(uvlong);
   993		uint32 (*swal)(uint32);
   994		Machhdr *mp;
   995		MachCmd **cmd;
   996		MachSymSeg *symtab;
   997		MachSymSeg *pclntab;
   998		MachSeg64 *seg;
   999		MachSect64 *sect;
  1000		MachSeg32 *seg32;
  1001		MachSect32 *sect32;
  1002		uvlong textsize, datasize, bsssize;
  1003		uchar *cmdbuf;
  1004		uchar *cmdp;
  1005		int i, hdrsize;
  1006		uint32 textva, textoff, datava, dataoff;
  1007	
  1008		mp = &hp->e.machhdr;
  1009		if (leswal(mp->filetype) != MACH_EXECUTABLE_TYPE) {
  1010			werrstr("bad MACH executable type %#ux", leswal(mp->filetype));
  1011			return 0;
  1012		}
  1013	
  1014		swal = leswal;
  1015		swav = leswav;
  1016	
  1017		mp->magic = swal(mp->magic);
  1018		mp->cputype = swal(mp->cputype);
  1019		mp->cpusubtype = swal(mp->cpusubtype);
  1020		mp->filetype = swal(mp->filetype);
  1021		mp->ncmds = swal(mp->ncmds);
  1022		mp->sizeofcmds = swal(mp->sizeofcmds);
  1023		mp->flags = swal(mp->flags);
  1024		mp->reserved = swal(mp->reserved);
  1025		hdrsize = 0;
  1026	
  1027		switch(mp->magic) {
  1028		case 0xFEEDFACE:	// 32-bit mach
  1029			if (mp->cputype != MACH_CPU_TYPE_X86) {
  1030				werrstr("bad MACH cpu type - not 386");
  1031				return 0;
  1032			}
  1033			if (mp->cpusubtype != MACH_CPU_SUBTYPE_X86) {
  1034				werrstr("bad MACH cpu subtype - not 386");
  1035				return 0;
  1036			}
  1037			if (mp->filetype != MACH_EXECUTABLE_TYPE) {
  1038				werrstr("bad MACH executable type");
  1039				return 0;
  1040			}
  1041			mach = &mi386;
  1042			fp->type = FI386;
  1043			hdrsize = 28;
  1044			break;
  1045	
  1046		case 0xFEEDFACF:	// 64-bit mach
  1047			if (mp->cputype != MACH_CPU_TYPE_X86_64) {
  1048				werrstr("bad MACH cpu type - not amd64");
  1049				return 0;
  1050			}
  1051	
  1052			if (mp->cpusubtype != MACH_CPU_SUBTYPE_X86) {
  1053				werrstr("bad MACH cpu subtype - not amd64");
  1054				return 0;
  1055			}
  1056			mach = &mamd64;
  1057			fp->type = FAMD64;
  1058			hdrsize = 32;
  1059			break;
  1060	
  1061		default:
  1062			werrstr("not mach %#ux", mp->magic);
  1063			return 0;
  1064		}
  1065	
  1066		cmdbuf = malloc(mp->sizeofcmds);
  1067		seek(fd, hdrsize, 0);
  1068		if(read(fd, cmdbuf, mp->sizeofcmds) != mp->sizeofcmds) {
  1069			free(cmdbuf);
  1070			return 0;
  1071		}
  1072		cmd = malloc(mp->ncmds * sizeof(MachCmd*));
  1073		cmdp = cmdbuf;
  1074		textva = 0;
  1075		textoff = 0;
  1076		dataoff = 0;
  1077		datava = 0;
  1078		symtab = 0;
  1079		pclntab = 0;
  1080		textsize = datasize = bsssize = 0;
  1081		for (i = 0; i < mp->ncmds; i++) {
  1082			MachCmd *c;
  1083	
  1084			cmd[i] = (MachCmd*)cmdp;
  1085			c = cmd[i];
  1086			c->type = swal(c->type);
  1087			c->size = swal(c->size);
  1088			switch(c->type) {
  1089			case MACH_SEGMENT_32:
  1090				if(mp->magic != 0xFEEDFACE) {
  1091					werrstr("segment 32 in mach 64");
  1092					goto bad;
  1093				}
  1094				seg32 = (MachSeg32*)c;
  1095				seg32->vmaddr = swav(seg32->vmaddr);
  1096				seg32->vmsize = swav(seg32->vmsize);
  1097				seg32->fileoff = swav(seg32->fileoff);
  1098				seg32->filesize = swav(seg32->filesize);
  1099				seg32->maxprot = swal(seg32->maxprot);
  1100				seg32->initprot = swal(seg32->initprot);
  1101				seg32->nsects = swal(seg32->nsects);
  1102				seg32->flags = swal(seg32->flags);
  1103				if (strcmp(seg32->segname, "__TEXT") == 0) {
  1104					textva = seg32->vmaddr;
  1105					textoff = seg32->fileoff;
  1106					sect32 = (MachSect32*)(cmdp + sizeof(MachSeg32));
  1107					if (strcmp(sect32->sectname, "__text") == 0) {
  1108						textsize = swal(sect32->size);
  1109					} else {
  1110						werrstr("no text section");
  1111						goto bad;
  1112					}
  1113				}
  1114				if (strcmp(seg32->segname, "__DATA") == 0) {
  1115					datava = seg32->vmaddr;
  1116					dataoff = seg32->fileoff;
  1117					sect32 = (MachSect32*)(cmdp + sizeof(MachSeg32));
  1118					if (strcmp(sect32->sectname, "__data") == 0) {
  1119						datasize = swal(sect32->size);
  1120					} else {
  1121						werrstr("no data section");
  1122						goto bad;
  1123					}
  1124					sect32++;
  1125					if (strcmp(sect32->sectname, "__nl_symbol_ptr") == 0)
  1126						sect32++;
  1127					if (strcmp(sect32->sectname, "__bss") == 0) {
  1128						bsssize = swal(sect32->size);
  1129					} else {
  1130						werrstr("no bss section");
  1131						goto bad;
  1132					}
  1133				}
  1134				break;
  1135	
  1136			case MACH_SEGMENT_64:
  1137				if(mp->magic != 0xFEEDFACF) {
  1138					werrstr("segment 32 in mach 64");
  1139					goto bad;
  1140				}
  1141				seg = (MachSeg64*)c;
  1142				seg->vmaddr = swav(seg->vmaddr);
  1143				seg->vmsize = swav(seg->vmsize);
  1144				seg->fileoff = swav(seg->fileoff);
  1145				seg->filesize = swav(seg->filesize);
  1146				seg->maxprot = swal(seg->maxprot);
  1147				seg->initprot = swal(seg->initprot);
  1148				seg->nsects = swal(seg->nsects);
  1149				seg->flags = swal(seg->flags);
  1150				if (strcmp(seg->segname, "__TEXT") == 0) {
  1151					textva = seg->vmaddr;
  1152					textoff = seg->fileoff;
  1153					sect = (MachSect64*)(cmdp + sizeof(MachSeg64));
  1154					if (strcmp(sect->sectname, "__text") == 0) {
  1155						textsize = swav(sect->size);
  1156					} else {
  1157						werrstr("no text section");
  1158						goto bad;
  1159					}
  1160				}
  1161				if (strcmp(seg->segname, "__DATA") == 0) {
  1162					datava = seg->vmaddr;
  1163					dataoff = seg->fileoff;
  1164					sect = (MachSect64*)(cmdp + sizeof(MachSeg64));
  1165					if (strcmp(sect->sectname, "__data") == 0) {
  1166						datasize = swav(sect->size);
  1167					} else {
  1168						werrstr("no data section");
  1169						goto bad;
  1170					}
  1171					sect++;
  1172					if (strcmp(sect->sectname, "__nl_symbol_ptr") == 0)
  1173						sect++;
  1174					if (strcmp(sect->sectname, "__bss") == 0) {
  1175						bsssize = swav(sect->size);
  1176					} else {
  1177						werrstr("no bss section");
  1178						goto bad;
  1179					}
  1180				}
  1181				break;
  1182			case MACH_UNIXTHREAD:
  1183				break;
  1184			case MACH_SYMSEG:
  1185				if (symtab == 0)
  1186					symtab = (MachSymSeg*)c;
  1187				else if (pclntab == 0)
  1188					pclntab = (MachSymSeg*)c;
  1189				break;
  1190			}
  1191			cmdp += c->size;
  1192		}
  1193		if (textva == 0 || datava == 0) {
  1194			free(cmd);
  1195			free(cmdbuf);
  1196			return 0;
  1197		}
  1198		/* compute entry by taking address after header - weird - BUG? */
  1199		settext(fp, textva+sizeof(Machhdr) + mp->sizeofcmds, textva, textsize, textoff);
  1200		setdata(fp, datava, datasize, dataoff, bsssize);
  1201		if(symtab != 0)
  1202			setsym(fp, symtab->fileoff, symtab->filesize, 0, 0, 0, pclntab? pclntab->filesize : 0);
  1203		free(cmd);
  1204		free(cmdbuf);
  1205		return 1;
  1206	bad:
  1207		free(cmd);
  1208		free(cmdbuf);
  1209		return 0;
  1210	}
  1211	
  1212	/*
  1213	 * (Free|Net)BSD ARM header.
  1214	 */
  1215	static int
  1216	armdotout(int fd, Fhdr *fp, ExecHdr *hp)
  1217	{
  1218		uvlong kbase;
  1219	
  1220		USED(fd);
  1221		settext(fp, hp->e.exechdr.entry, sizeof(Exec), hp->e.exechdr.text, sizeof(Exec));
  1222		setdata(fp, fp->txtsz, hp->e.exechdr.data, fp->txtsz, hp->e.exechdr.bss);
  1223		setsym(fp, fp->datoff+fp->datsz, hp->e.exechdr.syms, 0, hp->e.exechdr.spsz, 0, hp->e.exechdr.pcsz);
  1224	
  1225		kbase = 0xF0000000;
  1226		if ((fp->entry & kbase) == kbase) {		/* Boot image */
  1227			fp->txtaddr = kbase+sizeof(Exec);
  1228			fp->name = "ARM *BSD boot image";
  1229			fp->hdrsz = 0;		/* header stripped */
  1230			fp->dataddr = kbase+fp->txtsz;
  1231		}
  1232		return 1;
  1233	}
  1234	
  1235	static void
  1236	settext(Fhdr *fp, uvlong e, uvlong a, int32 s, vlong off)
  1237	{
  1238		fp->txtaddr = a;
  1239		fp->entry = e;
  1240		fp->txtsz = s;
  1241		fp->txtoff = off;
  1242	}
  1243	
  1244	static void
  1245	setdata(Fhdr *fp, uvlong a, int32 s, vlong off, int32 bss)
  1246	{
  1247		fp->dataddr = a;
  1248		fp->datsz = s;
  1249		fp->datoff = off;
  1250		fp->bsssz = bss;
  1251	}
  1252	
  1253	static void
  1254	setsym(Fhdr *fp, vlong symoff, int32 symsz, vlong sppcoff, int32 sppcsz, vlong lnpcoff, int32 lnpcsz)
  1255	{
  1256		fp->symoff = symoff;
  1257		fp->symsz = symsz;
  1258		
  1259		if(sppcoff == 0)
  1260			sppcoff = symoff+symsz;
  1261		fp->sppcoff = symoff;
  1262		fp->sppcsz = sppcsz;
  1263	
  1264		if(lnpcoff == 0)
  1265			lnpcoff = sppcoff + sppcsz;
  1266		fp->lnpcoff = lnpcoff;
  1267		fp->lnpcsz = lnpcsz;
  1268	}
  1269	
  1270	
  1271	static uvlong
  1272	_round(uvlong a, uint32 b)
  1273	{
  1274		uvlong w;
  1275	
  1276		w = (a/b)*b;
  1277		if (a!=w)
  1278			w += b;
  1279		return(w);
  1280	}

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