This causes clang to give fewer warnings of implicit declarations of functions. In mach/pdp/cv/cv.c, rename wr_int2() to cv_int2() because it conflicts with wr_int2() in <object.h>. In util/ack, rename F_OK to F_TRANSFORM because it conflicts with F_OK for access() in <unistd.h>.
		
			
				
	
	
		
			666 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			666 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 | 
						|
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * cvmach.c - convert ack.out to Mach-o
 | 
						|
 *
 | 
						|
 * Mostly pinched from aelflod (util/amisc/aelflod.c), which pinched
 | 
						|
 * from the ARM cv (mach/arm/cv/cv.c), which pinched from the m68k2 cv
 | 
						|
 * (mach/m68k2/cv/cv.c).  The code to read ack.out format using
 | 
						|
 * libobject is pinched from the Xenix i386 cv (mach/i386/cv/cv.c).
 | 
						|
 */
 | 
						|
 | 
						|
#include <fcntl.h>
 | 
						|
#include <stdarg.h>
 | 
						|
#include <stdint.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
#include <out.h>
 | 
						|
#include <object.h>
 | 
						|
 | 
						|
/* Header and section table of ack.out */
 | 
						|
struct outhead	outhead;
 | 
						|
struct outsect	outsect[S_MAX];
 | 
						|
uint32_t ack_off_char;		/* Offset of string table in ack.out */
 | 
						|
 | 
						|
int bigendian;			/* Emit big-endian Mach-o? */
 | 
						|
int cpu_type;
 | 
						|
uint32_t entry;			/* Virtual address of entry point */
 | 
						|
uint32_t sz_thread_command;
 | 
						|
 | 
						|
char *outputfile = NULL;	/* Name of output file, or NULL */
 | 
						|
char *program;			/* Name of current program: argv[0] */
 | 
						|
FILE *output;			/* Output stream */
 | 
						|
#define writef(a, b, c)	fwrite((a), (b), (c), output)
 | 
						|
 | 
						|
/* Segment numbers in ack.out */
 | 
						|
enum {
 | 
						|
	TEXT = 0,
 | 
						|
	ROM,
 | 
						|
	DATA,
 | 
						|
	BSS,
 | 
						|
	NUM_SEGMENTS
 | 
						|
};
 | 
						|
 | 
						|
/* Constants from Mach headers */
 | 
						|
#define MH_MAGIC			0xfeedface
 | 
						|
#define MH_EXECUTE			2
 | 
						|
#define LC_SEGMENT			1
 | 
						|
#define LC_SYMTAB			2
 | 
						|
#define LC_UNIXTHREAD			5
 | 
						|
 | 
						|
#define CPU_TYPE_X86			7
 | 
						|
#define CPU_SUBTYPE_X86_ALL		3
 | 
						|
#define x86_THREAD_STATE32		1
 | 
						|
#define x86_THREAD_STATE32_COUNT	16
 | 
						|
 | 
						|
#define CPU_TYPE_POWERPC		18
 | 
						|
#define CPU_SUBTYPE_POWERPC_ALL		0
 | 
						|
#define PPC_THREAD_STATE		1
 | 
						|
#define PPC_THREAD_STATE_COUNT		40
 | 
						|
 | 
						|
#define VM_PROT_NONE			0x0
 | 
						|
#define VM_PROT_READ			0x1
 | 
						|
#define VM_PROT_WRITE			0x2
 | 
						|
#define VM_PROT_EXECUTE			0x4
 | 
						|
 | 
						|
/* sizes of Mach structs */
 | 
						|
#define SZ_MACH_HEADER			28
 | 
						|
#define SZ_SEGMENT_COMMAND		56
 | 
						|
#define SZ_SECTION_HEADER		68
 | 
						|
#define SZ_SYMTAB_COMMAND		24
 | 
						|
#define SZ_THREAD_COMMAND_BF_STATE	16
 | 
						|
#define SZ_NLIST			12
 | 
						|
 | 
						|
/* the page size for x86 and PowerPC */
 | 
						|
#define CV_PGSZ				4096
 | 
						|
/* u modulo page size */
 | 
						|
#define pg_mod(u) ((u) & (CV_PGSZ - 1))
 | 
						|
/* u rounded down to whole pages */
 | 
						|
#define pg_trunc(u) ((u) & ~(CV_PGSZ - 1))
 | 
						|
/* u rounded up to whole pages */
 | 
						|
#define pg_round(u) pg_trunc((u) + (CV_PGSZ - 1))
 | 
						|
 | 
						|
const char zero_pg[CV_PGSZ] = { 0 };
 | 
						|
 | 
						|
/*
 | 
						|
 * machseg[0]: __PAGEZERO with address 0, size CV_PGSZ
 | 
						|
 * machseg[1]: __TEXT for ack TEXT, ROM
 | 
						|
 * machseg[2]: __DATA for ack DATA, BSS
 | 
						|
 */
 | 
						|
struct {
 | 
						|
	const char	*ms_name;
 | 
						|
	uint32_t	 ms_vmaddr;
 | 
						|
	uint32_t	 ms_vmsize;
 | 
						|
	uint32_t	 ms_fileoff;
 | 
						|
	uint32_t	 ms_filesize;
 | 
						|
	uint32_t	 ms_prot;
 | 
						|
	uint32_t	 ms_nsects;
 | 
						|
} machseg[3] = {
 | 
						|
	"__PAGEZERO", 0, CV_PGSZ, 0, 0, VM_PROT_NONE, 0,
 | 
						|
	"__TEXT", 0, 0, 0, 0, VM_PROT_READ | VM_PROT_EXECUTE, 2,
 | 
						|
	"__DATA", 0, 0, 0, 0, VM_PROT_READ | VM_PROT_WRITE, 2,
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
usage(void)
 | 
						|
{
 | 
						|
	fprintf(stderr, "Usage: %s -m<num> <inputfile> <outputfile>\n",
 | 
						|
	    program);
 | 
						|
	exit(1);
 | 
						|
}
 | 
						|
 | 
						|
/* Produce an error message and exit. */
 | 
						|
static void
 | 
						|
fatal(const char* s, ...)
 | 
						|
{
 | 
						|
	va_list ap;
 | 
						|
 | 
						|
	fprintf(stderr, "%s: ",program) ;
 | 
						|
 | 
						|
	va_start(ap, s);
 | 
						|
	vfprintf(stderr, s, ap);
 | 
						|
	va_end(ap);
 | 
						|
 | 
						|
	fprintf(stderr, "\n");
 | 
						|
 | 
						|
	if (outputfile)
 | 
						|
		unlink(outputfile);
 | 
						|
	exit(1);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
rd_fatal(void)
 | 
						|
{
 | 
						|
	fatal("read error");
 | 
						|
}
 | 
						|
 | 
						|
/* Returns n such that 2**n == a. */
 | 
						|
static uint32_t
 | 
						|
log2u(uint32_t a)
 | 
						|
{
 | 
						|
	uint32_t n = 0;
 | 
						|
	while (a) {
 | 
						|
		a >>= 1;
 | 
						|
		n++;
 | 
						|
	}
 | 
						|
	return n - 1;
 | 
						|
}
 | 
						|
 | 
						|
/* Writes a byte. */
 | 
						|
static void
 | 
						|
emit8(uint8_t value)
 | 
						|
{
 | 
						|
	writef(&value, 1, 1);
 | 
						|
}
 | 
						|
 | 
						|
/* Writes out a 16-bit value in the appropriate endianness. */
 | 
						|
static void
 | 
						|
emit16(uint16_t value)
 | 
						|
{
 | 
						|
	unsigned char buffer[2];
 | 
						|
 | 
						|
	if (bigendian)
 | 
						|
	{
 | 
						|
		buffer[0] = (value >> 8) & 0xFF;
 | 
						|
		buffer[1] = (value >> 0) & 0xFF;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		buffer[1] = (value >> 8) & 0xFF;
 | 
						|
		buffer[0] = (value >> 0) & 0xFF;
 | 
						|
	}
 | 
						|
 | 
						|
	writef(buffer, 1, sizeof(buffer));
 | 
						|
}
 | 
						|
 | 
						|
/* Writes out a 32-bit value in the appropriate endianness. */
 | 
						|
static void
 | 
						|
emit32(uint32_t value)
 | 
						|
{
 | 
						|
	unsigned char buffer[4];
 | 
						|
 | 
						|
	if (bigendian)
 | 
						|
	{
 | 
						|
		buffer[0] = (value >> 24) & 0xFF;
 | 
						|
		buffer[1] = (value >> 16) & 0xFF;
 | 
						|
		buffer[2] = (value >>  8) & 0xFF;
 | 
						|
		buffer[3] = (value >>  0) & 0xFF;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		buffer[3] = (value >> 24) & 0xFF;
 | 
						|
		buffer[2] = (value >> 16) & 0xFF;
 | 
						|
		buffer[1] = (value >>  8) & 0xFF;
 | 
						|
		buffer[0] = (value >>  0) & 0xFF;
 | 
						|
	}
 | 
						|
 | 
						|
	writef(buffer, 1, sizeof(buffer));
 | 
						|
}
 | 
						|
 | 
						|
/* Copies the contents of a section from the input stream
 | 
						|
 * to the output stream. */
 | 
						|
static void
 | 
						|
emit_section(int section_nr)
 | 
						|
{
 | 
						|
	struct outsect *section = &outsect[section_nr];
 | 
						|
	size_t blocksize;
 | 
						|
	uint32_t n = section->os_flen;
 | 
						|
	char buffer[BUFSIZ];
 | 
						|
 | 
						|
	rd_outsect(section_nr);
 | 
						|
	while (n > 0)
 | 
						|
	{
 | 
						|
		blocksize = (n > BUFSIZ) ? BUFSIZ : n;
 | 
						|
		rd_emit(buffer, (long)blocksize);
 | 
						|
		writef(buffer, 1, blocksize);
 | 
						|
		n -= blocksize;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Zero fill any remaining space. */
 | 
						|
	n = section->os_size - section->os_flen;
 | 
						|
	while (n > 0)
 | 
						|
	{
 | 
						|
		blocksize = (n > sizeof(zero_pg)) ? sizeof(zero_pg) : n;
 | 
						|
		writef(zero_pg, 1, blocksize);
 | 
						|
		n -= blocksize;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
emit_lc_segment(int i)
 | 
						|
{
 | 
						|
	uint32_t sz;
 | 
						|
	int flags, maxprot;
 | 
						|
	char namebuf[16];
 | 
						|
 | 
						|
	if (i == 0) {
 | 
						|
		/* special values for __PAGEZERO */
 | 
						|
		maxprot = VM_PROT_NONE;
 | 
						|
		flags = 4; /* SG_NORELOC */
 | 
						|
	} else {
 | 
						|
		maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
 | 
						|
		flags = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * The size of this command includes the size of its section
 | 
						|
	 * headers, see emit_section_header().
 | 
						|
	 */
 | 
						|
	sz = SZ_SEGMENT_COMMAND + machseg[i].ms_nsects * SZ_SECTION_HEADER;
 | 
						|
 | 
						|
	/* Use strncpy() to pad namebuf with '\0' bytes. */
 | 
						|
	strncpy(namebuf, machseg[i].ms_name, sizeof(namebuf));
 | 
						|
 | 
						|
	emit32(LC_SEGMENT);		/* command */
 | 
						|
	emit32(sz);			/* size of command */
 | 
						|
	writef(namebuf, 1, sizeof(namebuf));
 | 
						|
	emit32(machseg[i].ms_vmaddr);	/* vm address */
 | 
						|
	emit32(machseg[i].ms_vmsize);	/* vm size */
 | 
						|
	emit32(machseg[i].ms_fileoff);	/* file offset */
 | 
						|
	emit32(machseg[i].ms_filesize);	/* file size */
 | 
						|
	emit32(maxprot);		/* max protection */
 | 
						|
	emit32(machseg[i].ms_prot);	/* initial protection */
 | 
						|
	emit32(machseg[i].ms_nsects);	/* number of Mach sections */
 | 
						|
	emit32(flags);			/* flags */
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
emit_section_header(int ms, const char *name, int os)
 | 
						|
{
 | 
						|
	uint32_t fileoff, flags;
 | 
						|
	char namebuf[16];
 | 
						|
 | 
						|
	switch (os) {
 | 
						|
	case TEXT:
 | 
						|
		/* S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS */
 | 
						|
		flags = 0x80000400;
 | 
						|
		break;
 | 
						|
	case BSS:
 | 
						|
		flags = 0x1; /* S_ZEROFILL */
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		flags = 0x0; /* S_REGULAR */
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	if (os == BSS)
 | 
						|
		fileoff = 0;
 | 
						|
	else
 | 
						|
		fileoff = machseg[ms].ms_fileoff +
 | 
						|
		    (outsect[os].os_base - machseg[ms].ms_vmaddr);
 | 
						|
 | 
						|
	/* name of Mach section */
 | 
						|
	strncpy(namebuf, name, sizeof(namebuf));
 | 
						|
	writef(namebuf, 1, sizeof(namebuf));
 | 
						|
	/* name of Mach segment */
 | 
						|
	strncpy(namebuf, machseg[ms].ms_name, sizeof(namebuf));
 | 
						|
	writef(namebuf, 1, sizeof(namebuf));
 | 
						|
	emit32(outsect[os].os_base);	/* vm address */
 | 
						|
	emit32(outsect[os].os_size);	/* vm size */
 | 
						|
	emit32(fileoff);		/* file offset */
 | 
						|
	emit32(log2u(outsect[os].os_lign)); /* alignment */
 | 
						|
	emit32(0);			/* offset of relocations */
 | 
						|
	emit32(0);			/* number of relocations */
 | 
						|
	emit32(flags);			/* flags */
 | 
						|
	emit32(0);			/* reserved */
 | 
						|
	emit32(0);			/* reserved */
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
emit_lc_symtab(void)
 | 
						|
{
 | 
						|
	uint32_t off1, off2;
 | 
						|
 | 
						|
	/* Symbol table will be at next page after machseg[2]. */
 | 
						|
	off1 = pg_round(machseg[2].ms_fileoff + machseg[2].ms_filesize);
 | 
						|
	/* String table will be after symbol table. */
 | 
						|
	off2 = off1 + 12 * outhead.oh_nname;
 | 
						|
 | 
						|
	emit32(LC_SYMTAB);		/* command */
 | 
						|
	emit32(SZ_SYMTAB_COMMAND);	/* size of command */
 | 
						|
	emit32(off1);			/* offset of symbol table */
 | 
						|
	emit32(outhead.oh_nname);	/* number of symbols */
 | 
						|
	emit32(off2);			/* offset of string table */
 | 
						|
	emit32(1 + outhead.oh_nchar);	/* size of string table */
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
emit_lc_unixthread(void)
 | 
						|
{
 | 
						|
	int i, ireg, ts, ts_count;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * The thread state has ts_count registers.  The ireg'th
 | 
						|
	 * register holds the entry point.  We can set other registers
 | 
						|
	 * to zero.  At execution time, the kernel will allocate a
 | 
						|
	 * stack and set the stack pointer.
 | 
						|
	 */
 | 
						|
	switch (cpu_type) {
 | 
						|
	case CPU_TYPE_X86:
 | 
						|
		ireg = 10;	/* eip */
 | 
						|
		ts = x86_THREAD_STATE32;
 | 
						|
		ts_count = x86_THREAD_STATE32_COUNT;
 | 
						|
		break;
 | 
						|
	case CPU_TYPE_POWERPC:
 | 
						|
		ireg = 0;	/* srr0 */
 | 
						|
		ts = PPC_THREAD_STATE;
 | 
						|
		ts_count = PPC_THREAD_STATE_COUNT;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	emit32(LC_UNIXTHREAD);		/* command */
 | 
						|
	emit32(sz_thread_command);	/* size of command */
 | 
						|
	emit32(ts);			/* thread state */
 | 
						|
	emit32(ts_count);		/* thread state count */
 | 
						|
	for (i = 0; i < ts_count; i++) {
 | 
						|
		if (i == ireg)
 | 
						|
			emit32(entry);
 | 
						|
		else
 | 
						|
			emit32(0);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
emit_symbol(struct outname *np)
 | 
						|
{
 | 
						|
	uint32_t soff;
 | 
						|
	uint8_t type;
 | 
						|
	uint8_t sect;
 | 
						|
	uint16_t desc;
 | 
						|
 | 
						|
	if (np->on_type & S_STB) {
 | 
						|
		/* stab for debugger */
 | 
						|
		type = np->on_type >> 8;
 | 
						|
		desc = np->on_desc;
 | 
						|
	} else {
 | 
						|
		desc = 0;
 | 
						|
 | 
						|
		switch (np->on_type & S_TYP) {
 | 
						|
		case S_UND:
 | 
						|
			type = 0x0; /* N_UNDF */
 | 
						|
			break;
 | 
						|
		case S_ABS:
 | 
						|
			type = 0x2; /* N_ABS */
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			type = 0xe; /* N_SECT */
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		if (np->on_type & S_EXT)
 | 
						|
			type |= 0x1; /* N_EXT */
 | 
						|
	}
 | 
						|
 | 
						|
	switch (np->on_type & S_TYP) {
 | 
						|
	case S_MIN + TEXT:
 | 
						|
		sect = 1;
 | 
						|
		break;
 | 
						|
	case S_MIN + ROM:
 | 
						|
		sect = 2;
 | 
						|
		break;
 | 
						|
	case S_MIN + DATA:
 | 
						|
		sect = 3;
 | 
						|
		break;
 | 
						|
	case S_MIN + BSS:
 | 
						|
	case S_MIN + NUM_SEGMENTS:
 | 
						|
		sect = 4;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		sect = 0; /* NO_SECT */
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * To find the symbol's name, ack.out uses an offset from the
 | 
						|
	 * beginning of the file, but Mach-o uses an offset into the
 | 
						|
	 * string table.  Both formats use offset 0 for a symbol with
 | 
						|
	 * no name.  We will prepend a '\0' at offset 0, so every
 | 
						|
	 * named symbol needs + 1.
 | 
						|
	 */
 | 
						|
	if (np->on_foff)
 | 
						|
		soff = np->on_foff - ack_off_char + 1;
 | 
						|
	else
 | 
						|
		soff = 0;
 | 
						|
 | 
						|
	emit32(soff);
 | 
						|
	emit8(type);
 | 
						|
	emit8(sect);
 | 
						|
	emit16(desc);
 | 
						|
	emit32(np->on_valu);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
emit_symtab(void)
 | 
						|
{
 | 
						|
	struct outname *names, *np;
 | 
						|
	int i;
 | 
						|
	char *chars;
 | 
						|
 | 
						|
	/* Using calloc(a, b) to check if a * b would overflow. */
 | 
						|
	names = calloc(outhead.oh_nname, sizeof(struct outname));
 | 
						|
	if (!names)
 | 
						|
		fatal("out of memory");
 | 
						|
	chars = malloc(outhead.oh_nchar);
 | 
						|
	if (!names || !chars)
 | 
						|
		fatal("out of memory");
 | 
						|
	rd_name(names, outhead.oh_nname);
 | 
						|
	rd_string(chars, outhead.oh_nchar);
 | 
						|
 | 
						|
	ack_off_char = OFF_CHAR(outhead);
 | 
						|
 | 
						|
	/* Emit each symbol entry. */
 | 
						|
	for (i = 0, np = names; i < outhead.oh_nname; i++, np++)
 | 
						|
		emit_symbol(np);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Emit the string table.  The first character of a Mach-o
 | 
						|
	 * string table must be '\0', so we prepend a '\0'.
 | 
						|
	 */
 | 
						|
	emit8(0);
 | 
						|
	writef(chars, 1, outhead.oh_nchar);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
main(int argc, char *argv[])
 | 
						|
{
 | 
						|
	uint32_t end, pad[3], sz, sz_load_cmds;
 | 
						|
	int cpu_subtype, fd, mflag = 0;
 | 
						|
 | 
						|
	/* General housecleaning and setup. */
 | 
						|
	output = stdout;
 | 
						|
	program = argv[0];
 | 
						|
 | 
						|
	/* Read in and process any flags. */
 | 
						|
	while ((argc > 1) && (argv[1][0] == '-')) {
 | 
						|
		switch (argv[1][1]) {
 | 
						|
		case 'm': /* machine cpu type */
 | 
						|
			mflag = 1;
 | 
						|
			cpu_type = atoi(&argv[1][2]);
 | 
						|
			break;
 | 
						|
		case 'h': /* help */
 | 
						|
		default:
 | 
						|
			usage();
 | 
						|
		}
 | 
						|
 | 
						|
		argv++;
 | 
						|
		argc--;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!mflag)
 | 
						|
		usage();
 | 
						|
 | 
						|
	/* Check cpu type. */
 | 
						|
	switch (cpu_type) {
 | 
						|
	case CPU_TYPE_X86:
 | 
						|
		bigendian = 0;
 | 
						|
		cpu_subtype = CPU_SUBTYPE_X86_ALL;
 | 
						|
		sz_thread_command = 4 * x86_THREAD_STATE32_COUNT;
 | 
						|
		break;
 | 
						|
	case CPU_TYPE_POWERPC:
 | 
						|
		bigendian = 1;
 | 
						|
		cpu_subtype = CPU_SUBTYPE_POWERPC_ALL;
 | 
						|
		sz_thread_command = 4 * PPC_THREAD_STATE_COUNT;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		/* Can't emit LC_UNIXTHREAD for unknown cpu. */
 | 
						|
		fatal("unknown cpu type -m%d", cpu_type);
 | 
						|
	}
 | 
						|
	sz_thread_command += SZ_THREAD_COMMAND_BF_STATE;
 | 
						|
 | 
						|
	/* Process the rest of the arguments. */
 | 
						|
	switch (argc) {
 | 
						|
	case 1: /* No parameters --- read from stdin, write to stdout. */
 | 
						|
		rd_fdopen(0);
 | 
						|
		break;
 | 
						|
 | 
						|
	case 3: /* Both input and output files specified. */
 | 
						|
		/* Use mode 0777 to allow executing the output file. */
 | 
						|
		fd = open(argv[2], O_CREAT | O_TRUNC | O_WRONLY, 0777);
 | 
						|
		if (fd < 0)
 | 
						|
			fatal("unable to open output file.");
 | 
						|
		output = fdopen(fd, "w");
 | 
						|
		if (!output)
 | 
						|
			fatal("unable to open output file.");
 | 
						|
		outputfile = argv[2];
 | 
						|
		/* FALLTHROUGH */
 | 
						|
 | 
						|
	case 2: /* Input file specified. */
 | 
						|
		if (! rd_open(argv[1]))
 | 
						|
			fatal("unable to open input file.");
 | 
						|
		break;
 | 
						|
 | 
						|
	default:
 | 
						|
		usage();
 | 
						|
	}
 | 
						|
 | 
						|
	rd_ohead(&outhead);
 | 
						|
	if (BADMAGIC(outhead))
 | 
						|
		fatal("Not an ack object file.");
 | 
						|
	if (outhead.oh_flags & HF_LINK)
 | 
						|
		fatal("Contains unresolved references.");
 | 
						|
	if (outhead.oh_nrelo > 0)
 | 
						|
		fprintf(stderr, "Warning: relocation information present.");
 | 
						|
	if (outhead.oh_nsect != NUM_SEGMENTS &&
 | 
						|
	    outhead.oh_nsect != NUM_SEGMENTS + 1 ) {
 | 
						|
		fatal("Input file must have %d sections, not %ld\n",
 | 
						|
		    NUM_SEGMENTS, (long)outhead.oh_nsect);
 | 
						|
	}
 | 
						|
 | 
						|
	rd_sect(outsect, outhead.oh_nsect);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * machseg[1] will start at a page boundary and include the
 | 
						|
	 * Mach header and load commands before ack TEXT and ROM.
 | 
						|
	 *
 | 
						|
	 * Find our entry point (immediately after the load commands)
 | 
						|
	 * and check that TEXT begins there.
 | 
						|
	 */
 | 
						|
	machseg[1].ms_vmaddr = pg_trunc(outsect[TEXT].os_base);
 | 
						|
	sz_load_cmds = 3 * SZ_SEGMENT_COMMAND + 4 * SZ_SECTION_HEADER +
 | 
						|
	    SZ_SYMTAB_COMMAND + sz_thread_command;
 | 
						|
	entry = machseg[1].ms_vmaddr + SZ_MACH_HEADER + sz_load_cmds;
 | 
						|
	if (entry != outsect[TEXT].os_base) {
 | 
						|
		fatal("text segment must have base 0x%lx, not 0x%lx"
 | 
						|
		    "\n\t(suggest em_led -b0:0x%lx)",
 | 
						|
		    (unsigned long)entry,
 | 
						|
		    (unsigned long)outsect[TEXT].os_base,
 | 
						|
		    (unsigned long)entry);
 | 
						|
	}
 | 
						|
 | 
						|
	/* Pad for alignment between TEXT and ROM. */
 | 
						|
	sz = outsect[ROM].os_base - outsect[TEXT].os_base;
 | 
						|
	pad[0] = sz - outsect[TEXT].os_size;
 | 
						|
	if (sz < outsect[TEXT].os_size || pad[0] >= outsect[ROM].os_lign)
 | 
						|
		fatal("the rom segment must follow the text segment.");
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Pad between ROM and DATA such that we can map machseg[2] at
 | 
						|
	 * a page boundary with DATA at its correct base address.
 | 
						|
	 *
 | 
						|
	 * For example, if ROM ends at 0x2bed and DATA begins at
 | 
						|
	 * 0x3000, then we pad to the page boundary.  If ROM ends at
 | 
						|
	 * 0x2bed and DATA begins at 0x3bf0, then pad = 3 and we map
 | 
						|
	 * the page twice, at both 0x2000 and 0x3000.
 | 
						|
	 */
 | 
						|
	end = outsect[ROM].os_base + outsect[ROM].os_size;
 | 
						|
	pad[1] = pg_mod(outsect[DATA].os_base - end);
 | 
						|
 | 
						|
	sz = end - machseg[1].ms_vmaddr;
 | 
						|
	machseg[1].ms_vmsize = machseg[1].ms_filesize = sz;
 | 
						|
	machseg[2].ms_vmaddr = pg_trunc(outsect[DATA].os_base);
 | 
						|
	machseg[2].ms_fileoff = pg_trunc(sz + pad[1]);
 | 
						|
	if (machseg[2].ms_vmaddr < end &&
 | 
						|
	    machseg[2].ms_vmaddr >= machseg[1].ms_vmaddr)
 | 
						|
		fatal("the data and rom segments are too close."
 | 
						|
		    "\n\t(suggest em_led -a2:%d)", (int)CV_PGSZ);
 | 
						|
 | 
						|
	if (outsect[BSS].os_flen != 0)
 | 
						|
		fatal("the bss space contains initialized data.");
 | 
						|
	sz = outsect[BSS].os_base - outsect[DATA].os_base;
 | 
						|
	if (sz < outsect[DATA].os_size ||
 | 
						|
	    sz - outsect[DATA].os_size >= outsect[BSS].os_lign)
 | 
						|
		fatal("the bss segment must follow the data segment.");
 | 
						|
 | 
						|
	end = outsect[DATA].os_base + outsect[DATA].os_size;
 | 
						|
	machseg[2].ms_filesize = end - machseg[2].ms_vmaddr;
 | 
						|
	end = outsect[BSS].os_base + outsect[BSS].os_size;
 | 
						|
	machseg[2].ms_vmsize = end - machseg[2].ms_vmaddr;
 | 
						|
 | 
						|
	if (outhead.oh_nsect == NUM_SEGMENTS + 1) {
 | 
						|
		if (outsect[NUM_SEGMENTS].os_base !=
 | 
						|
		    outsect[BSS].os_base + outsect[BSS].os_size)
 | 
						|
			fatal("end segment must follow bss");
 | 
						|
		if (outsect[NUM_SEGMENTS].os_size != 0)
 | 
						|
			fatal("end segment must be empty");
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Pad to page boundary between BSS and symbol table.
 | 
						|
	 *
 | 
						|
	 * Also, some versions of Mac OS X refuse to load any
 | 
						|
	 * executable smaller than 4096 bytes (1 page).
 | 
						|
	 */
 | 
						|
	pad[2] = pg_mod(-(uint32_t)machseg[2].ms_filesize);
 | 
						|
 | 
						|
	/* Emit the Mach header. */
 | 
						|
	emit32(MH_MAGIC);	/* magic */
 | 
						|
	emit32(cpu_type);	/* cpu type */
 | 
						|
	emit32(cpu_subtype);	/* cpu subtype */
 | 
						|
	emit32(MH_EXECUTE);	/* file type */
 | 
						|
	emit32(5);		/* number of load commands */
 | 
						|
	emit32(sz_load_cmds);	/* size of load commands */
 | 
						|
	emit32(0);		/* flags */
 | 
						|
 | 
						|
	emit_lc_segment(0);
 | 
						|
	emit_lc_segment(1);
 | 
						|
	emit_section_header(1, "__text", TEXT);
 | 
						|
	emit_section_header(1, "__rom", ROM);
 | 
						|
	emit_lc_segment(2);
 | 
						|
	emit_section_header(2, "__data", DATA);
 | 
						|
	emit_section_header(2, "__bss", BSS);
 | 
						|
	emit_lc_symtab();
 | 
						|
	emit_lc_unixthread();
 | 
						|
 | 
						|
	/* Emit non-empty sections. */
 | 
						|
	emit_section(TEXT);
 | 
						|
	writef(zero_pg, 1, pad[0]);
 | 
						|
	emit_section(ROM);
 | 
						|
	writef(zero_pg, 1, pad[1]);
 | 
						|
	emit_section(DATA);
 | 
						|
 | 
						|
	writef(zero_pg, 1, pad[2]);
 | 
						|
	emit_symtab();
 | 
						|
 | 
						|
	if (ferror(output))
 | 
						|
		fatal("write error");
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 |