Merge branch 'pr-osx' of https://github.com/kernigh/ack into kernigh-pr-osx

This commit is contained in:
David Given 2016-12-05 20:14:54 +01:00
commit 3cce93586f
70 changed files with 2368 additions and 60 deletions

View file

@ -9,6 +9,8 @@ vars.plats = {
"linux386",
"linux68k",
"linuxppc",
"osx386",
"osxppc",
"qemuppc",
"pc86",
"rpi",

View file

@ -1,6 +1,7 @@
clibrary {
name = "lib",
srcs = { "./*.c" },
hdrs = { "./object.h" },
deps = {
"modules+headers",
"h+local",

View file

@ -4,42 +4,46 @@
* See the copyright notice in the ACK home directory, in the file "Copyright".
*/
#include <ansi.h>
#ifndef __OBJECT_INCLUDED__
#define __OBJECT_INCLUDED__
_PROTOTYPE(int wr_open, (char *f));
_PROTOTYPE(void wr_close, (void));
_PROTOTYPE(void wr_ohead, (struct outhead *h));
_PROTOTYPE(void wr_sect, (struct outsect *s, unsigned int c));
_PROTOTYPE(void wr_outsect, (int sectno));
_PROTOTYPE(void wr_emit, (char *b, long c));
_PROTOTYPE(void wr_putc, (int c));
_PROTOTYPE(void wr_relo, (struct outrelo *r, unsigned int c));
_PROTOTYPE(void wr_name, (struct outname *n, unsigned int c));
_PROTOTYPE(void wr_string, (char *s, long c));
_PROTOTYPE(void wr_arhdr, (int fd, struct ar_hdr *a));
_PROTOTYPE(void wr_ranlib, (int fd, struct ranlib *r, long cnt));
_PROTOTYPE(void wr_int2, (int fd, int i));
_PROTOTYPE(void wr_long, (int fd, long l));
_PROTOTYPE(void wr_bytes, (int fd, char *buf, long l));
_PROTOTYPE(int rd_open, (char *f));
_PROTOTYPE(int rd_fdopen, (int f));
_PROTOTYPE(void rd_close, (void));
_PROTOTYPE(void rd_ohead, (struct outhead *h));
_PROTOTYPE(void rd_sect, (struct outsect *s, unsigned int c));
_PROTOTYPE(void rd_outsect, (int sectno));
_PROTOTYPE(void rd_emit, (char *b, long c));
_PROTOTYPE(void rd_relo, (struct outrelo *r, unsigned int c));
_PROTOTYPE(void rd_rew_relo, (struct outhead *head));
_PROTOTYPE(void rd_name, (struct outname *n, unsigned int c));
_PROTOTYPE(void rd_string, (char *s, long c));
_PROTOTYPE(int rd_arhdr, (int fd, struct ar_hdr *a));
_PROTOTYPE(void rd_ranlib, (int fd, struct ranlib *r, long cnt));
_PROTOTYPE(int rd_int2, (int fd));
_PROTOTYPE(long rd_long, (int fd));
_PROTOTYPE(void rd_bytes, (int fd, char *buf, long l));
_PROTOTYPE(int rd_fd, (void));
struct ar_hdr;
struct outhead;
struct outrelo;
struct outsect;
struct ranlib;
int wr_open(char *f);
void wr_close(void);
void wr_ohead(struct outhead *h);
void wr_sect(struct outsect *s, unsigned int c);
void wr_outsect(int sectno);
void wr_emit(char *b, long c);
void wr_putc(int c);
void wr_relo(struct outrelo *r, unsigned int c);
void wr_name(struct outname *n, unsigned int c);
void wr_string(char *s, long c);
void wr_arhdr(int fd, struct ar_hdr *a);
void wr_ranlib(int fd, struct ranlib *r, long cnt);
void wr_int2(int fd, int i);
void wr_long(int fd, long l);
void wr_bytes(int fd, char *buf, long l);
int rd_open(char *f);
int rd_fdopen(int f);
void rd_close(void);
void rd_ohead(struct outhead *h);
void rd_sect(struct outsect *s, unsigned int c);
void rd_outsect(int sectno);
void rd_emit(char *b, long c);
void rd_relo(struct outrelo *r, unsigned int c);
void rd_rew_relo(struct outhead *head);
void rd_name(struct outname *n, unsigned int c);
void rd_string(char *s, long c);
int rd_arhdr(int fd, struct ar_hdr *a);
void rd_ranlib(int fd, struct ranlib *r, long cnt);
int rd_int2(int fd);
long rd_long(int fd);
void rd_bytes(int fd, char *buf, long l);
int rd_fd(void);
#endif /* __OBJECT_INCLUDED__ */

15
plat/osx/cvmach/build.lua Normal file
View file

@ -0,0 +1,15 @@
cprogram {
name = "cvmach",
srcs = { "./cvmach.c" },
deps = {
"h+emheaders",
"modules/src/object+lib",
}
}
installable {
name = "pkg",
map = {
["$(PLATDEP)/cvmach"] = "+cvmach",
}
}

96
plat/osx/cvmach/cvmach.6 Normal file
View file

@ -0,0 +1,96 @@
.Dd December 2, 2016
.Dt CVMACH 6
.Os
.Sh NAME
.Nm cvmach
.Nd convert an executable file from ack.out to Mach-o
.Sh SYNOPSIS
.Cm ~em/lib/ack/cvmach
.Fl m Ns Ar number
.Oo
.Ar infile
.Op Ar outfile
.Oc
.Sh DESCRIPTION
The
.Nm
utility converts an executable file from
.Xr ack.out 5
format to Mach-object format.
It can produce Mach-o executables for Mac OS X.
If the
.Ar infile
or
.Ar outfile
are not given, then
.Nm
reads from standard input or writes to standard output.
.Pp
The option is required:
.Bl -tag -width Ds
.It Fl m Ns Ar number
Sets the CPU type in the Mach header.
This must be
.Fl m Ns Cm 7
for Intel i386 or
.Fl m Ns Cm 18
for PowerPC.
.Nm
doesn't know how to make Mach-o files for other CPU types.
.El
.Pp
The input file must have four segments:
TEXT, ROM, DATA and BSS, in that order.
.Nm
converts them into four Mach sections in two Mach segments.
TEXT and ROM go in the RX segment (Read and eXecute).
DATA and BSS go in the RW segment (Read and Write).
.Nm
sets the page protection so programs can't write the RX segment,
and can't execute the RW segment.
The program will begin execution at the beginning of TEXT.
.Pp
.Nm
also converts the symbol table.
.Sh DIAGNOSTICS
.Bl -diag
.It text segment must have base 0x%lx, not 0x%lx
TEXT must begin immediately after the Mach header and load commands.
The message gives the correct
.Ar base .
Relinking the program with
.Cm em_led Fl b Ns 0: Ns Ar base
would fix it.
.It the %s segment must follow the %s segment.
TEXT and ROM must be continuous in memory, because
.Nm
maps them in the same Mach segment.
Likewise, DATA and BSS must be contiguous.
There may be a small gap between TEXT and ROM, or between DATA and
BSS, only if the gap is necessary to align ROM or BSS.
.It the data and rom segments are too close.
DATA and ROM must not share a page in memory, because
.Nm
maps them in different Mach segments, with different page protections.
The page size for i386 and PowerPC is 4096 or 0x1000 bytes.
For example, if ROM ends at address 0x2bed,
then DATA may not begin at 0x2bf0, because
.Nm
can't put the page from 0x2000 to 0x2fff in both Mach segments.
Relinking the program with
.Cm em_led Fl a Ns 2:4096
would fix it.
.It the bss space contains initialized data.
BSS must not contain initialized data, because
.Nm
converts it to a zero-fill section.
.El
.Sh CAVEATS
Mac OS X 10.4 for PowerPC does not protect pages against execution.
All mapped pages are executable, whether or not the execute bit is set
in the page protection.
.Nm
can't prevent the execution of the RW segment.
.Sh BUGS
The symbol conversion preserves the name and value of each symbol, but
may lose other information, such as the symbol type.

665
plat/osx/cvmach/cvmach.c Normal file
View file

@ -0,0 +1,665 @@
/*
* (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 <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;
}

View file

@ -0,0 +1,14 @@
/* $Source$
* $State$
* $Revision$
*/
#ifndef _ACK_CONFIG_H
#define _ACK_CONFIG_H
/* We're providing a time() system call rather than wanting a wrapper around
* gettimeofday() in the libc. */
/* #define ACKCONF_TIME_IS_A_SYSCALL */
#endif

View file

@ -0,0 +1,26 @@
include("plat/build.lua")
headermap = {}
packagemap = {}
local function addheader(h)
headermap[h] = "plat/osx/include/"..h
packagemap["$(PLATIND)/osx/include/"..h] = "plat/osx/include/"..h
end
addheader("ack/config.h")
addheader("sys/dirent.h")
addheader("sys/mman.h")
addheader("sys/stat.h")
addheader("sys/types.h")
addheader("unistd.h")
acklibrary {
name = "headers",
hdrs = headermap
}
installable {
name = "pkg",
map = packagemap
}

View file

@ -0,0 +1,17 @@
#ifndef _SYS_DIRENT_H
#define _SYS_DIRENT_H
#include <sys/types.h>
struct dirent {
ino_t d_ino;
unsigned short d_reclen;
unsigned char d_type;
unsigned char d_namlen;
#define MAXNAMLEN 255
char d_name[MAXNAMLEN + 1];
};
int getdirentries(int, char *, int, long *);
#endif

View file

@ -0,0 +1,20 @@
#ifndef _SYS_MMAN_H
#define _SYS_MMAN_H
#include <sys/types.h>
#define MAP_FAILED ((void *)-1)
#define PROT_NONE 0x00
#define PROT_READ 0x01
#define PROT_WRITE 0x02
#define PROT_EXEC 0x04
#define MAP_PRIVATE 0x0002
#define MAP_FIXED 0x0010
#define MAP_ANON 0x1000
void *mmap(void *, size_t, int, int, int, off_t);
int mprotect(void *, size_t, int);
#endif

View file

@ -0,0 +1,49 @@
#ifndef _SYS_STAT_H
#define _SYS_STAT_H
#include <sys/types.h>
#include <sys/time.h> /* for timespec */
struct stat {
dev_t st_dev;
ino_t st_ino;
mode_t st_mode;
nlink_t st_nlink;
uid_t st_uid;
gid_t st_gid;
dev_t st_rdev;
struct timespec st_atim;
struct timespec st_mtim;
struct timespec st_ctim;
#define st_atime st_atim.tv_sec
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
/*
* XXX - We don't have 64-bit integers, so we only expose the
* lower 32 bits of 64-bit fields. We insert dummy fields for
* the higher 32 bits.
*/
#if defined(__i386)
off_t st_size;
off_t _st_size_hi;
blkcnt_t st_blocks;
blkcnt_t _st_blkcnt_hi;
#elif defined(__powerpc)
off_t _st_size_hi;
off_t st_size;
blkcnt_t _st_blkcnt_hi;
blkcnt_t st_blkcnt;
#else
#error unknown arch
#endif
blksize_t st_blksize;
unsigned int st_flags;
unsigned int st_gen;
unsigned int _st_spare[5];
};
int fstat(int, struct stat *);
int lstat(const char *, struct stat *);
int stat(const char *, struct stat *);
#endif

View file

@ -0,0 +1,17 @@
#ifndef _SYS_TYPES_H
#define _SYS_TYPES_H
#include <stddef.h> /* for off_t, ptrdiff_t, size_t */
typedef int blkcnt_t; /* XXX should have 64 bits */
typedef int blksize_t;
typedef int dev_t;
typedef unsigned int gid_t;
typedef unsigned int ino_t;
typedef unsigned short mode_t;
typedef unsigned short nlink_t;
typedef int pid_t;
typedef ptrdiff_t ssize_t;
typedef unsigned int uid_t;
#endif

137
plat/osx/include/unistd.h Normal file
View file

@ -0,0 +1,137 @@
#ifndef _UNISTD_H
#define _UNISTD_H
#include <sys/types.h>
/*
* XXX - The following parts belong in other header files,
* but those headers are including us!
*/
/* XXX - begin sys/ioctl.h */
#define TIOCGETD 0x4004741a
int ioctl(int, unsigned long, ...);
/* XXX - end sys/ioctl.h */
/* XXX - begin sys/time.h */
/* Don't conflict with time_t from <time.h> */
typedef long _libsys_time_t;
typedef int suseconds_t;
struct timespec {
_libsys_time_t tv_sec;
long tv_nsec;
};
struct timeval {
_libsys_time_t tv_sec;
suseconds_t tv_usec;
};
struct timezone {
int tz_minuteswest;
int tz_dsttime;
};
int gettimeofday(struct timeval *, struct timezone *);
/* XXX - end sys/time.h */
/* XXX - begin fcntl.h */
/* flags for open() */
#define O_RDONLY 0x0000
#define O_WRONLY 0x0001
#define O_RDWR 0x0002
#define O_NONBLOCK 0x0004
#define O_APPEND 0x0008
#define O_CREAT 0x0200
#define O_TRUNC 0x0400
#define O_EXCL 0x0800
int creat(const char *, mode_t);
int open(const char *, int, ...);
/* XXX - end fcntl.h */
/* XXX - begin signal.h */
#define SIGHUP 1
#define SIGINT 2
#define SIGQUIT 3
#define SIGILL 4
#define SIGTRAP 5
#define SIGABRT 6
#define SIGEMT 7
#define SIGFPE 8
#define SIGKILL 9
#define SIGBUS 10
#define SIGSEGV 11
#define SIGSYS 12
#define SIGPIPE 13
#define SIGALRM 14
#define SIGTERM 15
#define SIGURG 16
#define SIGSTOP 17
#define SIGTSTP 18
#define SIGCONT 19
#define SIGCHLD 20
#define SIGTTIN 21
#define SIGTTOU 22
#define SIGIO 23
#define SIGXCPU 24
#define SIGXFSZ 25
#define SIGVTALRM 26
#define SIGPROF 27
#define SIGWINCH 28
#define SIGINFO 29
#define SIGUSR1 30
#define SIGUSR2 31
#define _NSIG 32
/* sa_flags */
#define SA_RESTART 0x0002
typedef void (*sig_t)(int);
#define SIG_DFL ((sig_t)0)
#define SIG_IGN ((sig_t)1)
#define SIG_ERR ((sig_t)-1)
typedef unsigned int sigset_t;
struct __siginfo;
struct sigaction {
union {
void (*__sa_handler)(int);
void (*__sa_sigaction)(int, struct __siginfo *, void *);
} __sigaction_u;
sigset_t sa_mask;
int sa_flags;
};
#define sa_handler __sigaction_u.__sa_handler
#define sa_sigaction __sigaction_u.__sa_sigaction
int kill(pid_t, int);
int sigaction(int, const struct sigaction *, struct sigaction *);
sig_t signal(int, sig_t);
int raise(int); /* in libc */
/* XXX - end signal.h */
void _exit(int);
int brk(void *);
int close(int);
pid_t getpid(void);
int isatty(int);
off_t lseek(int, off_t, int);
ssize_t read(int, void *, size_t);
void *sbrk(int);
ssize_t write(int, const void *, size_t);
#endif

86
plat/osx/libsys/brk.c Normal file
View file

@ -0,0 +1,86 @@
/*
* This emulates brk() and sbrk() using mmap() and mprotect().
*
* We reserve exactly SEGMENTSZ bytes of address space by calling
* mmap() with PROT_NONE. Then we allocate pages in our segment by
* calling mprotect() with PROT_READ|PROT_WRITE.
*
* This emulation can't resize its segment. If SEGMENTSZ is too big,
* then programs might run out of address space for other mappings.
*/
#include <sys/mman.h>
#include <errno.h>
#include <unistd.h>
/*
* PAGESZ must be correct for this system!
* SEGMENTSZ must be a multiple of PAGESZ.
*/
#define PAGESZ 0x1000 /* page size for i386, powerpc */
#define SEGMENTSZ 0x20000000
static char *segment;
static char *cbreak; /* current break */
static void brk_init(void)
{
/*
* Try exactly once to reserve our segment. If we fail, then
* segment == MAP_FAILED and we never try again.
*/
if (segment == NULL) {
segment = mmap(NULL, SEGMENTSZ, PROT_NONE,
MAP_PRIVATE|MAP_ANON, -1, 0);
cbreak = segment;
}
}
static int brk1(char *nbreak)
{
size_t sz;
char *new, *old;
sz = (segment == MAP_FAILED) ? 0 : SEGMENTSZ;
if (nbreak < segment || nbreak > segment + sz) {
errno = ENOMEM;
return -1;
}
/* Round up to page size. */
old = (char *)(((size_t)cbreak + (PAGESZ-1)) & ~(PAGESZ-1));
new = (char *)(((size_t)nbreak + (PAGESZ-1)) & ~(PAGESZ-1));
if (new > old) {
/* Allocate pages by unprotecting them. */
if (mprotect(old, new - old, PROT_READ|PROT_WRITE) < 0) {
errno = ENOMEM;
return -1;
}
} else if (new < old) {
/*
* Free pages by using MAP_FIXED to replace the
* mapping. Ignore errors.
*/
mmap(new, old - new, PROT_NONE,
MAP_PRIVATE|MAP_ANON|MAP_FIXED, -1, 0);
}
cbreak = nbreak;
return 0;
}
int brk(void *addr)
{
brk_init();
return brk1(addr);
}
void *sbrk(int incr)
{
char *base;
brk_init();
base = cbreak;
if (brk1(base + incr) < 0)
return (void*)-1;
return base;
}

6
plat/osx/libsys/creat.c Normal file
View file

@ -0,0 +1,6 @@
#include <fcntl.h>
int creat(const char *path, mode_t mode)
{
return open(path, O_CREAT | O_TRUNC | O_WRONLY, mode);
}

7
plat/osx/libsys/isatty.c Normal file
View file

@ -0,0 +1,7 @@
#include <sys/ioctl.h>
int isatty(int fd)
{
int line_disc;
return 0 <= ioctl(fd, TIOCGETD, &line_disc);
}

16
plat/osx/libsys/signal.c Normal file
View file

@ -0,0 +1,16 @@
#include <signal.h>
sig_t signal(int sig, sig_t func)
{
struct sigaction newsa, oldsa;
int i;
newsa.sa_handler = func;
newsa.sa_mask = 0; /* empty set */
newsa.sa_flags = SA_RESTART;
i = sigaction(sig, &newsa, &oldsa);
if (i < 0)
return SIG_ERR;
return oldsa.sa_handler;
}

25
plat/osx386/README Normal file
View file

@ -0,0 +1,25 @@
The osx386 platform
===================
ack -mosx386 ...
This platform produces Mach-o executables for Intel Mac OS X. These
are 32-bit executables using our i386 code generator.
See ../osxppc/README, because our osx386 platform has many of the same
limitations and bugs as our osxppc platform.
Bugs
----
Some programs can't read the tty after using job control to suspend
and resume the program (with ^Z and "fg" in bash). The read(2) system
call fails with EINTR. In ACK's stdio (in libc), the error is sticky,
so all reads fail. In Apple's stdio, the error is not sticky, and
only the next read fails. The EINTR seems to happen only on Intel Mac
OS X, and not on other platforms.
George Koehler <xkernigh@netscape.net>
2016-12-03

69
plat/osx386/boot.s Normal file
View file

@ -0,0 +1,69 @@
! plat/osx386/boot.s
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
begtext:
! This code is placed at the entry point of the Mach-o
! executable and is the first thing that runs.
!
! On entry, the stack looks like this:
!
! sp+.. NULL
! sp+8+(4*argc) env (X quads)
! sp+4+(4*argc) NULL
! sp+4 argv (argc quads)
! sp argc
!
! The ACK actually expects:
!
! sp+8 argc
! sp+4 argv
! sp env
mov eax, (esp) ! eax = argc
lea ebx, 4(esp) ! ebx = argv
lea ecx, (esp)(eax*4)
add ecx, 12 ! environ
push ecx ! environ
push ebx ! argc
push eax ! argv
push eax ! dummy, representing the return argument
xor ebp, ebp
jmp __m_a_i_n
! This provides an emergency exit routine used by EM.
.define EXIT
.extern EXIT
EXIT:
push 1
jmp __exit
.sect .rom
begrom:
.sect .data
begdata:
! Some magic data. All EM systems need these.
.sect .bss
begbss:
.define hol0
.comm hol0, 8 ! line number and filename (for debugging)
.define _errno
.comm _errno, 4 ! Posix errno storage
.define .trppc, .ignmask
.comm .trppc, 4 ! ptr to user trap handler
.comm .ignmask, 4 ! user trap ignore mask

24
plat/osx386/build-pkg.lua Normal file
View file

@ -0,0 +1,24 @@
include("plat/build.lua")
ackfile {
name = "boot",
srcs = { "./boot.s" },
vars = { plat = "osx386" }
}
build_plat_libs {
name = "libs",
arch = "i386",
plat = "osx386",
}
installable {
name = "pkg",
map = {
"+tools",
"+libs",
"plat/osx/include+pkg",
["$(PLATIND)/osx386/boot.o"] = "+boot",
["$(PLATIND)/osx386/libsys.a"] = "./libsys+lib",
}
}

View file

@ -0,0 +1,9 @@
return installable {
name = "tools",
map = {
["$(PLATIND)/descr/osx386"] = "./descr",
"plat/linux386+tools",
"plat/osx/cvmach+pkg",
"util/opt+pkg",
}
}

80
plat/osx386/descr Normal file
View file

@ -0,0 +1,80 @@
# plat/osx386/descr
var w=4
var wa=4
var p={w}
var pa={w}
var s=2
var sa={s}
var l={w}
var la={w}
var f={w}
var fa={w}
var d=8
var da={d}
var x=8
var xa={x}
var ARCH=i386
var PLATFORM=osx386
var PLATFORMDIR={EM}/share/ack/{PLATFORM}
var CPP_F=-D__unix
var ALIGN=-a0:4 -a1:4 -a2:4096 -a3:4 -b0:0x123c
var C_LIB={PLATFORMDIR}/libc-ansi.a
# bitfields reversed for compatibility with (g)cc.
var CC_ALIGN=-Vr
var OLD_C_LIB={C_LIB}
var MACHOPT_F=-m10
var EGO_PLAT_FLAGS=-M{EM}/share/ack/ego/{ARCH}.descr
# Override the setting in fe so that files compiled for osx386 can see
# the platform-specific headers.
var C_INCLUDES=-I{EM}/share/ack/osx/include -I{EM}/share/ack/include/ansi
name be
from .m.g
to .s
program {EM}/lib/ack/linux386/ncg
mapflag -gdb GF=-gdb
args {GF?} <
stdout
need .e
end
name as
from .s.so
to .o
program {EM}/lib/ack/linux386/as
args - -o > <
prep cond
end
name led
from .o.a
to .out
program {EM}/lib/ack/em_led
mapflag -l* LNAME={PLATFORMDIR}/lib*
mapflag -fp FLOATS={EM}/{LIB}fp
args {ALIGN} {SEPID?} \
(.e:{HEAD}={PLATFORMDIR}/boot.o) \
({RTS}:.ocm.b={PLATFORMDIR}/c-ansi.o) \
({RTS}:.c={PLATFORMDIR}/c-ansi.o) \
({RTS}:.mod={PLATFORMDIR}/modula2.o) \
({RTS}:.p={PLATFORMDIR}/pascal.o) \
-o > < \
(.p:{TAIL}={PLATFORMDIR}/libpascal.a) \
(.b:{TAIL}={PLATFORMDIR}/libbasic.a) \
(.mod:{TAIL}={PLATFORMDIR}/libmodula2.a) \
(.ocm:{TAIL}={PLATFORMDIR}/liboccam.a) \
(.ocm.b.mod.c.p:{TAIL}={PLATFORMDIR}/libc.a) \
{FLOATS?} \
(.e:{TAIL}={PLATFORMDIR}/libem.a \
{PLATFORMDIR}/libsys.a \
{PLATFORMDIR}/libend.a)
linker
end
name cv
from .out
to .exe
program {EM}/lib/ack/cvmach
args -m7 < >
outfile osx386.exe
end

View file

@ -0,0 +1,7 @@
simplerule {
name = "headers",
deps = { "plat/osx/include+headers" },
ins = {},
outs = {},
commands = {},
}

View file

@ -0,0 +1,5 @@
.sect .text
.define __exit
__exit:
mov eax, 1
int 0x80

View file

@ -0,0 +1,35 @@
acklibrary {
name = "lib",
srcs = {
"./_exit.s",
"./close.s",
"./fstat.s",
"./getdirentries.s",
"./getpid.s",
"./gettimeofday.s",
"./ioctl.s",
"./kill.s",
"./lseek.s",
"./lstat.s",
"./mmap.s",
"./mprotect.s",
"./open.s",
"./read.s",
"./set_errno.s",
"./sigaction.s",
"./stat.s",
"./write.s",
"plat/linux/libsys/errno.s",
"plat/osx/libsys/brk.c",
"plat/osx/libsys/creat.c",
"plat/osx/libsys/isatty.c",
"plat/osx/libsys/signal.c",
},
deps = {
"lang/cem/libcc.ansi/headers+headers",
"plat/osx386/include+headers",
},
vars = {
plat = "osx386"
}
}

View file

@ -0,0 +1,7 @@
.sect .text
.define _close
_close:
mov eax, 6
int 0x80
jb .set_errno
ret

View file

@ -0,0 +1,7 @@
.sect .text
.define _fstat
_fstat:
mov eax, 189
int 0x80
jb .set_errno
ret

View file

@ -0,0 +1,7 @@
.sect .text
.define _getdirentries
_getdirentries:
mov eax, 196
int 0x80
jb .set_errno
ret

View file

@ -0,0 +1,7 @@
.sect .text
.define _getpid
_getpid:
mov eax, 20
int 0x80
jb .set_errno
ret

View file

@ -0,0 +1,18 @@
! The system call checks the timeval pointer but doesn't store the
! time there. If the pointer wasn't NULL, then the system call
! returns the time in a pair of registers.
.sect .text
.define _gettimeofday
_gettimeofday:
mov eax, 116
int 0x80
jb .set_errno
mov ebx, 4(esp) ! timeval pointer
test ebx, ebx
je 1f
mov 0(ebx), eax ! seconds
mov 4(ebx), edx ! microseconds
1:
mov eax, 0 ! return 0
ret

View file

@ -0,0 +1,7 @@
.sect .text
.define _ioctl
_ioctl:
mov eax, 54
int 0x80
jb .set_errno
ret

View file

@ -0,0 +1,7 @@
.sect .text
.define _kill
_kill:
mov eax, 37
int 0x80
jb .set_errno
ret

View file

@ -0,0 +1,17 @@
.sect .text
.define _lseek
_lseek:
! ack passes 4-byte off_t, but system call takes 8-byte off_t
mov eax, esp
push 12(eax) ! whence
push 0 ! offset (high long)
push 8(eax) ! offset (low long)
push 4(eax) ! fd
call 1f
add esp, 16
ret
1:
mov eax, 199
int 0x80
jb .set_errno
ret

View file

@ -0,0 +1,7 @@
.sect .text
.define _lstat
_lstat:
mov eax, 190
int 0x80
jb .set_errno
ret

20
plat/osx386/libsys/mmap.s Normal file
View file

@ -0,0 +1,20 @@
.sect .text
.define _mmap
_mmap:
! ack passes 4-byte off_t, but system call takes 8-byte off_t
mov eax, esp
push 0 ! offset (high long)
push 24(eax) ! offset (low long)
push 20(eax) ! fd
push 16(eax) ! flags
push 12(eax) ! protection
push 8(eax) ! length
push 4(eax) ! address
call 1f
add esp, 28
ret
1:
mov eax, 197
int 0x80
jb .set_errno
ret

View file

@ -0,0 +1,7 @@
.sect .text
.define _mprotect
_mprotect:
mov eax, 74
int 0x80
jb .set_errno
ret

View file

@ -0,0 +1,7 @@
.sect .text
.define _open
_open:
mov eax, 5
int 0x80
jb .set_errno
ret

View file

@ -0,0 +1,7 @@
.sect .text
.define _read
_read:
mov eax, 3
int 0x80
jb .set_errno
ret

View file

@ -0,0 +1,6 @@
.sect .text
.define .set_errno
.set_errno:
mov (_errno), eax
mov eax, -1
ret

View file

@ -0,0 +1,59 @@
! OS X, unlike FreeBSD, requires us to provide our own signal
! trampoline. We must change the new action from a struct sigaction
! to a bigger struct that includes the trampoline.
.sect .text
.define _sigaction
_sigaction:
mov eax, esp
mov ebx, 8(esp) ! ebx = ptr to new action
cmp ebx, 0
je 1f
! push bigger struct
push 8(ebx) ! sa_flags
push 4(ebx) ! sa_mask
push trampoline ! sa_tramp
push 0(ebx) ! sa_handler
mov ebx, esp
jmp 2f
1:
sub esp, 16
2:
push 12(eax) ! ptr to old action
push ebx ! ptr to bigger struct
push 4(eax) ! sig
call 3f
add esp, 28
ret
3:
mov eax, 46
int 0x80
jb .set_errno
ret
trampoline:
! 4(esp) = handler
! 8(esp) = info style
! 12(esp) = sig
! 16(esp) = info
! 20(esp) = context
! Call handler(sig, info, context)
mov eax, esp
push 20(eax)
push 16(eax)
push 12(eax)
call 4(eax)
add esp, 12
! Return from trampoline.
mov eax, esp
push 8(eax) ! info style
push 20(eax) ! context
sub esp, 4
mov eax, 184 ! sigreturn
int 0x80
! Only if sigreturn() fails:
mov eax, 1 ! exit
int 0x80

View file

@ -0,0 +1,7 @@
.sect .text
.define _stat
_stat:
mov eax, 188
int 0x80
jb .set_errno
ret

View file

@ -0,0 +1,7 @@
.sect .text
.define _write
_write:
mov eax, 4
int 0x80
jb .set_errno
ret

110
plat/osxppc/README Normal file
View file

@ -0,0 +1,110 @@
The osxppc platform
===================
ack -mosxppc ...
This platform produces Mach-o executables for PowerPC Mac OS X. You
can run them from the command line in the Terminal.
You *can't* link to libraries from other compilers. These static
executables don't use the dynamic linker. They don't load Apple's
libraries, so they can't call Carbon or Cocoa.
The executables use BSD system calls to interact with your Mac. Our
libsys provides only a few system calls, enough to run a few demo
programs, but not much else. Check the header files in ../osx/include
for the available system calls.
Bugs
----
ACK didn't run on Mac OS X when this platform was added. The only way
to run ack -mosxppc was as a cross compiler from another operating
system.
ACK doesn't have 64-bit integers, but Mac OS X uses 64-bit integers in
its system calls. Our libsys converts between 32-bit and 64-bit
integers by setting the high bits to zero, or discarding the high
bits. This affects lseek() and stat(). They report the wrong values
for file sizes and offsets beyond 4 gigabytes.
Our PowerPC code generator is new and probably has bugs. Its stack
layout and calling conventions are not compatible with other
compilers. It passes all function arguments on the stack, which is
slower than passing them in registers.
Example
-------
Compile something:
ack -mosxppc -O6 -o paranoia examples/paranoia.c
The executable has a symbol table. If you have Apple's Xcode, try
nm -g paranoia # to list the global symbols
otool -hl paranoia # to check the Mach header and load commands
gdb paranoia # to debug it
Within gdb, commands like "gdb main" and "gdb '.ret'" can disassemble
functions. Backtraces don't work, because our stack layout is not the
same as Apple's.
Other hints
-----------
PowerPC Macs became obsolete after Apple's transition to Intel. Mac
OS X 10.5 Leopard was the last version to run on PowerPC. The older
Mac OS X 10.4 Tiger was the last version to include Classic for
running Mac OS 9 programs. Our ack -mosxppc began to produce
executables in 2016, about 7 years after Apple released Mac OS X 10.6
Snow Leopard for Intel only.
Apple's Xcode included tools like gcc and gdb. It also had manual
pages for some system calls, like getdirentries(2). Some system calls
are like FreeBSD, some are unique to OS X. If you want to learn how
to call write(2) or sigaction(2), then a manual page from another BSD
or Linux might be enough.
Xcode 2.5 was the last version to run on Tiger. The "Xcode 2.5
Developer Tools" were a 902.9 MB download from Apple. As of 2016, the
download required an Apple ID and was available at:
https://developer.apple.com/download/more/
Older versions of Xcode came with Mac OS X. If your version of OS X
came with your Mac, /Applications/Installers might contain an Xcode
installer. If you upgraded OS X, your install DVD might have Xcode.
The source code at https://opensource.apple.com/ might reveal more
about system calls. For 10.4.11.ppc, the kernel is in xnu-792.24.17,
and Libc is in Libc-391.2.10. These files might help:
xnu*/bsd/kern/syscalls.master
master list of BSD system calls
xnu*/osfmk/kern/syscall_sw.c
master list of Mach traps
xnu*/bsd/kern/mach_loader.c
details about loading Mach-o executables
xnu*/bsd/dev/ppc/unix_signal.c
details about sending signals to processes
xnu*/bsd/sys/*.h
headers that Xcode installs as /usr/include/sys/*.h
xnu*/bsd/man/man2/*.2
manual pages that Xcode installs as /usr/share/man/man2/*.2
Libc*/ppc/sys/SYS.h
Libc*/ppc/sys/*.s
assembly code (in gas syntax) for making system calls
The 10.4.11.ppc sources are wrong for Intel; use 10.4.11.x86 or 10.5
or newer. 10.5 moved SYS.h to xnu*/libsyscall/custom/SYS.h
The kernel maps a common page into every process, and Apple's Libc
uses the common page to speed up system calls like gettimeofday(2).
Our libsys does not use the common page.
George Koehler <xkernigh@netscape.net>
2016-12-03

60
plat/osxppc/boot.s Normal file
View file

@ -0,0 +1,60 @@
! boot.s for osxppc
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
begtext:
! This code is placed at the entry point of the Mach-o
! executable and is the first thing that runs.
!
! On entry, the stack looks like this:
!
! sp+... NULL
! sp+8+(4*argc) env (X quads)
! sp+4+(4*argc) NULL
! sp+4 argv (argc quads)
! sp argc
!
! The ACK actually expects:
!
! sp+8 argc
! sp+4 ptr to argv
! sp ptr to env
lwz r3, 0(sp) ! r3 = argc
addi r4, sp, 4 ! r4 = argv
rlwinm r5, r3, 32-2, 2, 31 ! shift left 2 bits
add r5, r5, r4
addi r5, r5, 8 ! r5 = env
stwu r5, -4(sp)
stwu r4, -4(sp)
stwu r3, -4(sp)
b __m_a_i_n
.sect .rom
begrom:
.sect .data
begdata:
! Some magic data. All EM systems need these.
.sect .bss
begbss:
.define hol0
.comm hol0, 8 ! line number and filename (for debugging)
.define _errno
.comm _errno, 4 ! Posix errno storage
.define .trppc, .ignmask
.comm .trppc, 4 ! ptr to user trap handler
.comm .ignmask, 4 ! user trap ignore mask

24
plat/osxppc/build-pkg.lua Normal file
View file

@ -0,0 +1,24 @@
include("plat/build.lua")
ackfile {
name = "boot",
srcs = { "./boot.s" },
vars = { plat = "osxppc" }
}
build_plat_libs {
name = "libs",
arch = "powerpc",
plat = "osxppc",
}
installable {
name = "pkg",
map = {
"+tools",
"+libs",
"plat/osx/include+pkg",
["$(PLATIND)/osxppc/boot.o"] = "+boot",
["$(PLATIND)/osxppc/libsys.a"] = "./libsys+lib",
}
}

View file

@ -0,0 +1,9 @@
return installable {
name = "tools",
map = {
["$(PLATIND)/descr/osxppc"] = "./descr",
"plat/linuxppc+tools",
"plat/osx/cvmach+pkg",
"util/opt+pkg",
}
}

85
plat/osxppc/descr Normal file
View file

@ -0,0 +1,85 @@
# plat/osxppc/descr
var w=4
var wa=4
var p={w}
var pa={w}
var s=2
var sa={s}
var l={w}
var la={w}
var f={w}
var fa={w}
var d=8
var da={d}
var x=8
var xa={x}
var ARCH=powerpc
var PLATFORM=osxppc
var PLATFORMDIR={EM}/share/ack/{PLATFORM}
var CPP_F=-D__unix
var ALIGN=-a0:4 -a1:4 -a2:4096 -a3:4 -b0:0x129c
var MACHOPT_F=-m3
var EGO_PLAT_FLAGS=-M{EM}/share/ack/ego/{ARCH}.descr
# Override the setting in fe so that files compiled for osxppc can see
# the platform-specific headers.
var C_INCLUDES=-I{EM}/share/ack/osx/include -I{EM}/share/ack/include/ansi
name be
from .m.g
to .s
program {EM}/lib/ack/linuxppc/ncg
mapflag -gdb GF=-gdb
args {GF?} <
stdout
need .e
end
name asopt
from .s
to .so
program {EM}/lib/ack/linuxppc/top
args
optimizer
stdin
stdout
end
name as
from .s.so
to .o
program {EM}/lib/ack/linuxppc/as
args - -o > <
prep cond
end
name led
from .o.a
to .out
program {EM}/lib/ack/em_led
mapflag -l* LNAME={PLATFORMDIR}/lib*
mapflag -fp FLOATS={EM}/{LIB}fp
args {ALIGN} {SEPID?} \
(.e:{HEAD}={PLATFORMDIR}/boot.o) \
({RTS}:.ocm.b={PLATFORMDIR}/c-ansi.o) \
({RTS}:.c={PLATFORMDIR}/c-ansi.o) \
({RTS}:.mod={PLATFORMDIR}/modula2.o) \
({RTS}:.p={PLATFORMDIR}/pascal.o) \
-o > < \
(.p:{TAIL}={PLATFORMDIR}/libpascal.a) \
(.b:{TAIL}={PLATFORMDIR}/libbasic.a) \
(.mod:{TAIL}={PLATFORMDIR}/libmodula2.a) \
(.ocm:{TAIL}={PLATFORMDIR}/liboccam.a) \
(.ocm.b.mod.c.p:{TAIL}={PLATFORMDIR}/libc.a) \
{FLOATS?} \
(.e:{TAIL}={PLATFORMDIR}/libem.a \
{PLATFORMDIR}/libsys.a \
{PLATFORMDIR}/libend.a)
linker
end
name cv
from .out
to .exe
program {EM}/lib/ack/cvmach
args -m18 < >
outfile osxppc.exe
end

View file

@ -0,0 +1,7 @@
simplerule {
name = "headers",
deps = { "plat/osx/include+headers" },
ins = {},
outs = {},
commands = {},
}

View file

@ -0,0 +1,6 @@
.sect .text
.define __exit
__exit:
addi r0, r0, 1 ! _exit
lwz r3, 0(sp) ! status
sc 0

View file

@ -0,0 +1,35 @@
acklibrary {
name = "lib",
srcs = {
"./_exit.s",
"./close.s",
"./fstat.s",
"./getdirentries.s",
"./getpid.s",
"./gettimeofday.s",
"./ioctl.s",
"./kill.s",
"./lseek.s",
"./lstat.s",
"./mmap.s",
"./mprotect.s",
"./open.s",
"./read.s",
"./set_errno.s",
"./sigaction.s",
"./stat.s",
"./write.s",
"plat/linuxppc/libsys/trap.s",
"plat/osx/libsys/brk.c",
"plat/osx/libsys/creat.c",
"plat/osx/libsys/isatty.c",
"plat/osx/libsys/signal.c",
},
deps = {
"lang/cem/libcc.ansi/headers+headers",
"plat/osxppc/include+headers",
},
vars = {
plat = "osxppc"
}
}

View file

@ -0,0 +1,8 @@
.sect .text
.define _close
_close:
addi r0, r0, 6 ! close
lwz r3, 0(sp) ! fd
sc 0
b .set_errno
bclr 20, 0, 0

View file

@ -0,0 +1,9 @@
.sect .text
.define _fstat
_fstat:
addi r0, r0, 189 ! fstat
lwz r3, 0(sp) ! fd
lwz r4, 4(sp) ! stat pointer
sc 0
b .set_errno
bclr 20, 0, 0

View file

@ -0,0 +1,11 @@
.sect .text
.define _getdirentries
_getdirentries:
addi r0, r0, 196 ! getdirentries
lwz r3, 0(sp) ! fd
lwz r4, 4(sp) ! buffer
lwz r5, 8(sp) ! buffer size
lwz r6, 12(sp) ! base pointer
sc 0
b .set_errno
bclr 20, 0, 0

View file

@ -0,0 +1,7 @@
.sect .text
.define _getpid
_getpid:
addi r0, r0, 20 ! getpid
sc 0
b .set_errno
bclr 20, 0, 0

View file

@ -0,0 +1,19 @@
! The system call checks the timeval pointer but doesn't store the
! time there. If the pointer wasn't NULL, then the system call
! returns the time in a pair of registers.
.sect .text
.define _gettimeofday
_gettimeofday:
addi r0, r0, 116 ! gettimeofday
lwz r3, 0(sp) ! timeval pointer
lwz r4, 4(sp) ! timezone pointer
or. r5, r3, r3
sc 0
b .set_errno
bc 12, 2, 1f ! beq 1f
stw r3, 0(r5) ! seconds
stw r4, 4(r5) ! microseconds
1:
addi r3, r0, 0 ! return 0
bclr 20, 0, 0

View file

@ -0,0 +1,10 @@
.sect .text
.define _ioctl
_ioctl:
addi r0, r0, 54 ! ioctl
lwz r3, 0(sp) ! fd
lwz r4, 4(sp) ! command
lwz r5, 8(sp) ! argument pointer
sc 0
b .set_errno
bclr 20, 0, 0

View file

@ -0,0 +1,9 @@
.sect .text
.define _kill
_kill:
addi r0, r0, 37 ! kill
lwz r3, 0(sp) ! pid
lwz r4, 4(sp) ! signal
sc 0
b .set_errno
bclr 20, 0, 0

View file

@ -0,0 +1,13 @@
.sect .text
.define _lseek
_lseek:
addi r0, r0, 199 ! lseek
lwz r3, 0(sp) ! fd
! ack passes 4-byte off_t, but system call takes 8-byte off_t
addi r4, r0, 0 ! offset (high word)
lwz r5, 4(sp) ! offset (low word)
lwz r6, 8(sp) ! whence
sc 0
b .set_errno
or r3, r4, r4 ! return offset (low word)
bclr 20, 0, 0

View file

@ -0,0 +1,9 @@
.sect .text
.define _lstat
_lstat:
addi r0, r0, 190 ! lstat
lwz r3, 0(sp) ! path
lwz r4, 4(sp) ! stat pointer
sc 0
b .set_errno
bclr 20, 0, 0

15
plat/osxppc/libsys/mmap.s Normal file
View file

@ -0,0 +1,15 @@
.sect .text
.define _mmap
_mmap:
addi r0, r0, 197 ! mmap
lwz r3, 0(sp) ! address
lwz r4, 4(sp) ! length
lwz r5, 8(sp) ! protection
lwz r6, 12(sp) ! flags
lwz r7, 16(sp) ! fd
! ack passes 4-byte off_t, but system call takes 8-byte off_t
addi r8, r0, 0 ! offset (high word)
lwz r9, 20(sp) ! offset (low word)
sc 0
b .set_errno
bclr 20, 0, 0

View file

@ -0,0 +1,10 @@
.sect .text
.define _mprotect
_mprotect:
addi r0, r0, 74 ! mprotect
lwz r3, 0(sp) ! address
lwz r4, 4(sp) ! length
lwz r5, 8(sp) ! protection
sc 0
b .set_errno
bclr 20, 0, 0

10
plat/osxppc/libsys/open.s Normal file
View file

@ -0,0 +1,10 @@
.sect .text
.define _open
_open:
addi r0, r0, 5 ! open
lwz r3, 0(sp) ! path
lwz r4, 4(sp) ! flags
lwz r5, 8(sp) ! mode
sc 0
b .set_errno
bclr 20, 0, 0

10
plat/osxppc/libsys/read.s Normal file
View file

@ -0,0 +1,10 @@
.sect .text
.define _read
_read:
addi r0, r0, 3 ! read
lwz r3, 0(sp) ! fd
lwz r4, 4(sp) ! buffer
lwz r5, 8(sp) ! buffer size
sc 0
b .set_errno
bclr 20, 0, 0

View file

@ -0,0 +1,7 @@
.sect .text
.define .set_errno
.set_errno:
li32 r10, _errno
stw r3, 0(r10) ! set errno
addi r3, r0, -1 ! return -1
bclr 20, 0, 0

View file

@ -0,0 +1,59 @@
#define IFTRUE 12
#define ALWAYS 20
#define EQ 2
! OS X, unlike FreeBSD, requires us to provide our own signal
! trampoline. We must change the new action from a struct sigaction
! to a bigger struct that includes the trampoline.
.sect .text
.define _sigaction
_sigaction:
addi r0, r0, 46 ! sigaction
lwz r3, 0(sp) ! sig
lwz r4, 4(sp) ! ptr to new action
lwz r5, 8(sp) ! ptr to old action
or. r6, r4, r4
bc IFTRUE, EQ, 1f ! skip if new action is NULL
! We may use the "red zone" from -224(sp) to 0(sp).
addi r4, sp, -16 ! r4 = bigger struct
lwz r7, 0(r6)
stw r7, 0(r4) ! sa_handler
li32 r7, trampoline
stw r7, 4(r4) ! sa_tramp
lwz r7, 4(r6)
stw r7, 8(r4) ! sa_mask
lwz r7, 8(r6)
stw r7, 12(r4) ! sa_flags
1:
sc 0
b .set_errno
bclr 20, 0, 0
trampoline:
! r3 = handler
! r4 = info style
! r5 = sig
! r6 = info
! r7 = context
or r31, r4, r4 ! ack preserves r30, r31
or r30, r7, r7
! Call handler(sig, info, context).
mtspr ctr, r3
stwu r7, -4(sp) ! ack expects arguments on stack
stwu r6, -4(sp)
stwu r5, -4(sp)
bcctrl ALWAYS, 0, 0
! Return from trampoline.
addi r0, r0, 184 ! sigreturn
or r3, r30, r30 ! context
or r4, r31, r31 ! info style
sc 0
ori r0, r0, 0 ! nop
! Only if sigreturn() fails:
addi r0, r0, 1 ! exit
sc 0

View file

@ -0,0 +1,9 @@
.sect .text
.define _stat
_stat:
addi r0, r0, 188 ! stat
lwz r3, 0(sp) ! path
lwz r4, 4(sp) ! stat pointer
sc 0
b .set_errno
bclr 20, 0, 0

View file

@ -0,0 +1,10 @@
.sect .text
.define _write
_write:
addi r0, r0, 4 ! write
lwz r3, 0(sp) ! fd
lwz r4, 4(sp) ! buffer
lwz r5, 8(sp) ! buffer size
sc 0
b .set_errno
bclr 20, 0, 0

View file

@ -22,6 +22,7 @@ end
build_descr("i386")
build_descr("i86")
build_descr("m68020")
build_descr("powerpc")
installable {
name = "pkg",

View file

@ -0,0 +1,118 @@
wordsize: 4
pointersize: 4
%%RA
general registers: 19
address registers: 0
floating point registers: 0
use general as pointer: yes
register score parameters:
local variable:
(2 cases)
pointer,general
(1 size)
default -> (3,4)
general,general
(1 size)
default -> (3,4)
address of local variable:
(2 cases)
pointer,general
(1 size)
default -> (0,0)
general,general
(1 size)
default -> (0,0)
constant:
(2 sizes)
fitbyte -> (-1,-1)
default -> (-1,-1)
double constant:
(1 size)
default -> (-1,-1)
address of global variable:
(1 size)
default -> (2,8)
address of procedure:
(1 size)
default -> (-1,-1)
opening cost parameters:
local variable:
(2 cases)
pointer
(1 size)
default -> (3,4)
general
(1 size)
default -> (3,4)
address of local variable:
(2 cases)
pointer
(1 size)
default -> (1,4)
general
(1 size)
general -> (1,4)
constant:
(2 sizes)
fitbyte -> (1000,1000)
default -> (1000,1000)
double constant:
(1 size)
default -> (1000,1000)
address of global variable:
(1 size)
default -> (2,8)
address of procedure:
(1 size)
default -> (1000,1000)
register save costs:
(21 cases)
0 -> (0,0)
1 -> (6,8)
2 -> (12,16)
3 -> (18,24)
4 -> (24,32)
5 -> (30,40)
6 -> (36,48)
7 -> (42,56)
8 -> (48,64)
9 -> (54,72)
10 -> (60,80)
11 -> (66,88)
12 -> (72,96)
13 -> (78,104)
14 -> (84,112)
15 -> (90,120)
16 -> (96,128)
17 -> (102,136)
18 -> (108,144)
19 -> (114,152)
0 -> (0,0)
%%UD
access costs of global variables:
(1 size)
default -> (5,12)
access costs of local variables:
(1 size)
default -> (3,4)
%%SR
overflow harmful?: no
array bound harmful?: yes
reduce sli if shift count larger than: 0
%%CS
#include "em_mnem.h"
first time then space:
addressing modes: op_ads op_adp op_lof op_ldf op_loi op_dch op_lpb -1
op_ads op_adp op_lof op_ldf op_loi op_dch op_lpb -1
cheap operations: op_cii op_ciu op_cui op_cuu op_cmi op_cmu op_cmp -1
op_cii op_ciu op_cui op_cuu op_cmi op_cmu op_cmp -1
lexical tresholds: 1 1
indirection limit: 8
do not eliminate sli if index on shiftcounts: -1
-1
forbidden operators: -1 -1
%%SP
global stack pollution allowed?: yes

View file

@ -11,6 +11,7 @@ static char rcsid[] = "$Id$";
*/
#include <stdio.h>
#include <stdint.h>
#include <out.h>
#include "const.h"
#include "debug.h"
@ -29,15 +30,16 @@ int Verbose = 0;
static initializations();
static first_pass();
static long number();
static setlign();
static setbase();
static uint32_t number(const char *);
static void setlign(int, uint32_t);
static void setbase(int, uint32_t);
static struct outname *makename();
static pass1();
static evaluate();
static void norm_commons();
static complete_sections();
static void change_names();
static bool setbit();
static bool tstbit();
static second_pass();
static pass2();
@ -251,12 +253,11 @@ first_pass(argv)
* else if it starts with 0, it's octal,
* else it's decimal.
*/
static long
number(s)
register char *s;
static uint32_t
number(const char *s)
{
register int digit;
register long value = 0;
register uint32_t value = 0;
register int radix = 10;
if (*s == '0') {
@ -291,22 +292,17 @@ number(s)
* not. Only one base may be given. The same applies for alignments.
*/
static char basemap[MAXSECT / WIDTH];
static long sect_base[MAXSECT];
static uint32_t sect_base[MAXSECT];
static char lignmap[MAXSECT / WIDTH];
static long sect_lign[MAXSECT];
static uint32_t sect_lign[MAXSECT];
/*
/*
* Set the alignment of section `sectno' to `lign', if this doesn't
* conflict with earlier alignment.
*/
static
setlign(sectno, lign)
register int sectno;
register long lign;
static void
setlign(int sectno, uint32_t lign)
{
extern bool setbit();
if (setbit(sectno, lignmap) && sect_lign[sectno] != lign)
fatal("section has different alignments");
if (lign == (long)0)
@ -318,13 +314,9 @@ setlign(sectno, lign)
* Set the base of section `sectno' to `base', if no other base has been
* given yet.
*/
static
setbase(sectno, base)
register int sectno;
register long base;
static void
setbase(int sectno, uint32_t base)
{
extern bool setbit();
if (setbit(sectno, basemap) && sect_base[sectno] != base)
fatal("section has different bases");
sect_base[sectno] = base;
@ -459,8 +451,8 @@ struct orig relorig[MAXSECT];
static
complete_sections()
{
register long base = 0;
register long foff;
register uint32_t base = 0;
register uint32_t foff;
register struct outsect *sc;
register int sectindex;