/* * (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 * liboject is pinched from the Xenix i386 cv (mach/i386/cv/cv.c). */ #include #include #include #include #include #include /* Can't find #include */ /* Header and section table of ack.out */ struct outhead outhead; struct outsect outsect[S_MAX]; 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_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_THREAD_COMMAND_BF_STATE 16 /* 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)) void usage(void) { fprintf(stderr, "Usage: %s -m \n", program); exit(1); } /* Produce an error message and exit. */ 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"); } /* Calculate the result of a aligned to b (rounding up if necessary). * b must be a power of two. */ uint32_t align(uint32_t a, uint32_t b) { a += b - 1; return a & ~(b-1); } /* Writes out a 32-bit value in the appropriate endianness. */ 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. */ 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; if (n > 0) { memset(buffer, 0, BUFSIZ); while (n > 0) { blocksize = (n > BUFSIZ) ? BUFSIZ : n; writef(buffer, 1, blocksize); n -= blocksize; } } } void emit_lc_segment(char *name, uint32_t vm_ad, uint32_t vm_sz, uint32_t f_off, uint32_t f_sz, int prot) { char namebuf[16]; int flags, maxprot; if (prot == VM_PROT_NONE) { /* special values for __PAGEZERO */ maxprot = VM_PROT_NONE; flags = 4; /* NORELOC */ } else { maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; flags = 0; } /* Use strncpy() to pad namebuf with '\0' bytes. */ strncpy(namebuf, name, sizeof(namebuf)); emit32(LC_SEGMENT); /* command */ emit32(SZ_SEGMENT_COMMAND); /* size of command */ writef(namebuf, 1, sizeof(namebuf)); emit32(vm_ad); /* vm address */ emit32(vm_sz); /* vm size */ emit32(f_off); /* file offset */ emit32(f_sz); /* file size */ emit32(maxprot); /* max protection */ emit32(prot); /* initial protection */ emit32(0); /* number of Mach sections */ emit32(flags); /* flags */ } 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); } } int main(int argc, char *argv[]) { uint32_t len2, len3, mach_base, pad, sz_bf_entry, sz_load_cmds; int cpu_subtype, 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. */ output = fopen(argv[2], "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); /* * 1st Mach segment: __PAGEZERO * 2nd Mach segment: __TEXT * Mach headers and load commands * ack TEXT * ack ROM * 3rd Mach segment: __DATA * ack DATA * ack BSS */ /* Find entry point and check that TEXT begins there. */ mach_base = pg_trunc(outsect[TEXT].os_base); sz_load_cmds = 3 * SZ_SEGMENT_COMMAND + sz_thread_command; sz_bf_entry = SZ_MACH_HEADER + sz_load_cmds; entry = mach_base + sz_bf_entry; if (entry != outsect[TEXT].os_base) { fatal("text segment must have base 0x%lx, not 0x%lx\n", entry, outsect[TEXT].os_base); } /* Check that ROM can follow TEXT in 2nd Mach segment. */ outsect[TEXT].os_size = align(outsect[TEXT].os_size, outsect[ROM].os_lign); if (outsect[ROM].os_base != outsect[TEXT].os_base + outsect[TEXT].os_size) fatal("the rom segment must follow the text segment."); /* * Insert padding between ROM and DATA, such that * pg_mod(len2) == pg_mod(outsect[DATA].os_base) * * This will allow us to map the 3rd Mach segment at the * beginning of a page, such that DATA is at its base. */ len2 = sz_bf_entry + outsect[TEXT].os_size + outsect[ROM].os_size; pad = pg_mod(outsect[DATA].os_base - len2); outsect[ROM].os_size += pad; len2 = pg_trunc(len2 + pad); /* Check that BSS can follow DATA in 3rd Mach segment. */ if (outsect[BSS].os_flen != 0) fatal("the bss space contains initialized data."); if (outsect[BSS].os_base < outsect[DATA].os_base + outsect[DATA].os_size) fatal("the bss segment must follow the data segment."); len3 = outsect[BSS].os_base - pg_trunc(outsect[DATA].os_base) + outsect[BSS].os_size; 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"); } /* Emit the Mach header. */ emit32(MH_MAGIC); /* magic */ emit32(cpu_type); /* cpu type */ emit32(cpu_subtype); /* cpu subtype */ emit32(MH_EXECUTE); /* file type */ emit32(4); /* number of load commands */ emit32(sz_load_cmds); /* size of load commands */ emit32(0); /* flags */ /* vm address: vm size: * 1st Mach segment: NULL CV_PGSZ * 2nd Mach segment: mach_base len2 * 3rd Mach segment: mach_base+len2 len3 * * file offset: file size: * 2nd Mach segment: 0 len2 * 3rd Mach segment: len2 DATA os_size */ emit_lc_segment("__PAGEZERO", 0 /* NULL */, CV_PGSZ, 0, 0, VM_PROT_NONE); emit_lc_segment("__TEXT", mach_base, len2, 0, len2, VM_PROT_READ | VM_PROT_EXECUTE); emit_lc_segment("__DATA", mach_base + len2, len3, len2, outsect[DATA].os_size, VM_PROT_READ | VM_PROT_WRITE); emit_lc_unixthread(); /* Emit non-empty sections. */ emit_section(TEXT); emit_section(ROM); emit_section(DATA); if (ferror(output)) fatal("write error"); return 0; }