Merge from default.

This commit is contained in:
David Given 2016-12-26 19:44:48 +00:00
commit e50f4be710
100 changed files with 3382 additions and 418 deletions

View file

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

View file

@ -3,9 +3,15 @@ echo ""
succeeding="$(find "$@" -size 0)" succeeding="$(find "$@" -size 0)"
notsucceeding="$(find "$@" ! -size 0)" notsucceeding="$(find "$@" ! -size 0)"
skipped="$(grep -l @@SKIPPED $notsucceeding)" if [ "$notsucceeding" != "" ]; then
timedout="$(grep -l @@TIMEDOUT $notsucceeding)" skipped="$(grep -l @@SKIPPED $notsucceeding)"
failed="$(grep -l @@FAIL $notsucceeding)" timedout="$(grep -l @@TIMEDOUT $notsucceeding)"
failed="$(grep -l @@FAIL $notsucceeding)"
else
skipped=
timedout=
failed=
fi
for a in $failed $timedout; do for a in $failed $timedout; do
echo "**** $a" echo "**** $a"
@ -13,11 +19,11 @@ for a in $failed $timedout; do
echo "" echo ""
done done
echo "$(echo $succeeding | wc -w) tests passed" echo "$(echo "$succeeding" | wc -w) tests passed"
echo "$(echo $notsucceeding | wc -w) tests failed to pass" echo "$(echo "$notsucceeding" | wc -w) tests failed to pass"
echo "$(echo $skipped | wc -w) were skipped (see build log for details)" echo "$(echo "$skipped" | wc -w) were skipped (see build log for details)"
echo "$(echo $timedout | wc -w) timed out" echo "$(echo "$timedout" | wc -w) timed out"
echo "$(echo $failed | wc -w) failed" echo "$(echo "$failed" | wc -w) failed"
echo "" echo ""
if [ "$failed" != "" -o "$timedout" != "" ]; then if [ "$failed" != "" -o "$timedout" != "" ]; then

View file

@ -11,8 +11,8 @@ for _, plat in ipairs(vars.plats) do
deps = { deps = {
"./*.h", "./*.h",
"h+emheaders", "h+emheaders",
"lang/cem/libcc.ansi/headers+headers", "lang/cem/libcc.ansi/headers+pkg",
"plat/"..plat.."/include+headers", "plat/"..plat.."/include+pkg",
}, },
vars = { plat = plat } vars = { plat = plat }
} }

View file

@ -10,8 +10,8 @@ for _, plat in ipairs(vars.plats) do
hdrs = {}, -- must be empty hdrs = {}, -- must be empty
deps = { deps = {
"h+emheaders", "h+emheaders",
"lang/cem/libcc.ansi/headers+headers", "lang/cem/libcc.ansi/headers+pkg",
"plat/"..plat.."/include+headers", "plat/"..plat.."/include+pkg",
}, },
vars = { plat = plat } vars = { plat = plat }
} }

View file

@ -51,8 +51,8 @@ for _, plat in ipairs(vars.plats) do
}, },
hdrs = {}, -- must be empty hdrs = {}, -- must be empty
deps = { deps = {
"lang/cem/libcc.ansi/headers+headers", "lang/cem/libcc.ansi/headers+pkg",
"plat/"..plat.."/include+headers", "plat/"..plat.."/include+pkg",
"./malloc/malloc.h", "./malloc/malloc.h",
"./math/localmath.h", "./math/localmath.h",
"./stdio/loc_incl.h", "./stdio/loc_incl.h",

View file

@ -29,8 +29,8 @@ for _, plat in ipairs(vars.plats) do
}, },
hdrs = {}, -- must be empty hdrs = {}, -- must be empty
deps = { deps = {
"lang/cem/libcc.ansi/headers+headers", "lang/cem/libcc.ansi/headers+pkg",
"plat/"..plat.."/include+headers", "plat/"..plat.."/include+pkg",
"h+emheaders", "h+emheaders",
}, },
vars = { plat = plat } vars = { plat = plat }

View file

@ -17,8 +17,8 @@ for _, plat in ipairs(vars.plats) do
}, },
hdrs = {}, -- must be empty hdrs = {}, -- must be empty
deps = { deps = {
"lang/cem/libcc.ansi/headers+headers", "lang/cem/libcc.ansi/headers+pkg",
"plat/"..plat.."/include+headers", "plat/"..plat.."/include+pkg",
"h+emheaders", "h+emheaders",
}, },
vars = { plat = plat } vars = { plat = plat }

View file

@ -88,4 +88,4 @@
%type <y_word> c %type <y_word> c
%type <y_word> e16 u8 u7 u6 u5 u4 u2 u1 %type <y_word> e16 u8 u7 u6 u5 u4 u2 u1
%type <y_word> nb ds bda bdl lia lil %type <y_word> nb ds bda bdl lia lil spr_num

View file

@ -34,7 +34,7 @@ operation
| OP_RT_RA_RB_C c GPR ',' GPR ',' GPR { emit4($1 | $2 | ($3<<21) | ($5<<16) | ($7<<11)); } | OP_RT_RA_RB_C c GPR ',' GPR ',' GPR { emit4($1 | $2 | ($3<<21) | ($5<<16) | ($7<<11)); }
| OP_RT_RA_SI GPR ',' GPR ',' e16 { emit4($1 | ($2<<21) | ($4<<16) | $6); } | OP_RT_RA_SI GPR ',' GPR ',' e16 { emit4($1 | ($2<<21) | ($4<<16) | $6); }
| OP_RT_RA_SI_addic c GPR ',' GPR ',' e16 { emit4($1 | ($2<<26) | ($3<<21) | ($5<<16) | $7); } | OP_RT_RA_SI_addic c GPR ',' GPR ',' e16 { emit4($1 | ($2<<26) | ($3<<21) | ($5<<16) | $7); }
| OP_RT_SPR GPR ',' SPR { emit4($1 | ($2<<21) | ($4<<11)); } | OP_RT_SPR GPR ',' spr_num { emit4($1 | ($2<<21) | ($4<<11)); }
| OP_RS_FXM u7 ',' GPR { emit4($1 | ($4<<21) | ($2<<12)); } | OP_RS_FXM u7 ',' GPR { emit4($1 | ($4<<21) | ($2<<12)); }
| OP_RS_RA_C c GPR ',' GPR { emit4($1 | $2 | ($5<<21) | ($3<<16)); } | OP_RS_RA_C c GPR ',' GPR { emit4($1 | $2 | ($5<<21) | ($3<<16)); }
| OP_RS_RA_D GPR ',' e16 '(' GPR ')' { emit4($1 | ($2<<21) | ($6<<16) | $4); } | OP_RS_RA_D GPR ',' e16 '(' GPR ')' { emit4($1 | ($2<<21) | ($6<<16) | $4); }
@ -53,7 +53,7 @@ operation
| OP_RS_RA_SH_ME6_SH_C c GPR ',' GPR ',' u6 ',' u6 { emit4($1 | $2 | ($5<<21) | ($3<<16) | (($7&0x1F)<<11) | ($9<<6) | (($7&0x20)>>4)); } | OP_RS_RA_SH_ME6_SH_C c GPR ',' GPR ',' u6 ',' u6 { emit4($1 | $2 | ($5<<21) | ($3<<16) | (($7&0x1F)<<11) | ($9<<6) | (($7&0x20)>>4)); }
| OP_RS_RA_SH5_C c GPR ',' GPR ',' u5 { emit4($1 | $2 | ($5<<21) | ($3<<16) | ($7<<11)); } | OP_RS_RA_SH5_C c GPR ',' GPR ',' u5 { emit4($1 | $2 | ($5<<21) | ($3<<16) | ($7<<11)); }
| OP_RS_RA_SH6_C c GPR ',' GPR ',' u6 { emit4($1 | $2 | ($5<<21) | ($3<<16) | (($7&0x1F)<<11) | (($7&0x20)>>4)); } | OP_RS_RA_SH6_C c GPR ',' GPR ',' u6 { emit4($1 | $2 | ($5<<21) | ($3<<16) | (($7&0x1F)<<11) | (($7&0x20)>>4)); }
| OP_RS_SPR SPR ',' GPR { emit4($1 | ($4<<21) | ($2<<11)); } | OP_RS_SPR spr_num ',' GPR { emit4($1 | ($4<<21) | ($2<<11)); }
| OP_TO_RA_RB u5 ',' GPR ',' GPR { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); } | OP_TO_RA_RB u5 ',' GPR ',' GPR { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); }
| OP_TO_RA_SI u5 ',' GPR ',' e16 { emit4($1 | ($2<<21) | ($4<<16) | $6); } | OP_TO_RA_SI u5 ',' GPR ',' e16 { emit4($1 | ($2<<21) | ($4<<16) | $6); }
| OP_LEV u7 { emit4($1 | ($2<<5)); } | OP_LEV u7 { emit4($1 | ($2<<5)); }
@ -238,3 +238,13 @@ lia
} }
; ;
spr_num
: SPR { $$ = $1; }
| absexp
{
if (($1 < 0) || ($1 > 0x3ff))
serror("spr number out of range");
/* mfspr, mtspr swap the low and high 5 bits */
$$ = ($1 >> 5) | (($1 & 0x1f) << 5);
}
;

24
mach/powerpc/libem/and.s Normal file
View file

@ -0,0 +1,24 @@
#include "powerpc.h"
.sect .text
! Set intersection.
! Stack: ( b a -- a*b )
! With r3 = size of set
.define .and
.and:
mr r4, sp ! r4 = ptr to set a
add r5, sp, r3 ! r5 = ptr to set b
rlwinm r6, r3, 30, 2, 31
mtspr ctr, r6 ! ctr = r3 / 4
1:
lwz r7, 0(r4)
lwz r8, 0(r5)
and r8, r7, r8 ! intersection of words
stw r8, 0(r5)
addi r4, r4, 4
addi r5, r5, 4
bc DNZ, 0, 1b ! loop ctr times
add sp, sp, r3
bclr ALWAYS, 0, 0

View file

@ -7,7 +7,7 @@ for _, plat in ipairs(vars.plats) do
acklibrary { acklibrary {
name = "lib_"..plat, name = "lib_"..plat,
srcs = { srcs = {
"./*.s", "./*.s", -- zer.s
"./*.e", "./*.e",
}, },
vars = { plat = plat }, vars = { plat = plat },

32
mach/powerpc/libem/cms.s Normal file
View file

@ -0,0 +1,32 @@
#include "powerpc.h"
.sect .text
! Compare sets a, b.
! Stack: ( b a -- )
! With r3 = size of each set
! Yields r3 = 0 if equal, nonzero if not equal
.define .cms
.cms:
mr r4, sp ! r4 = ptr to set a
add r5, sp, r3 ! r5 = ptr to set b
mr r6, r3 ! r6 = size
rlwinm r3, r3, 30, 2, 31
mtspr ctr, r3 ! ctr = size / 4
1:
lwz r7, 0(r4)
lwz r8, 0(r5)
cmp cr0, 0, r7, r8 ! compare words in sets
addi r4, r4, 4
addi r5, r5, 4
bc IFFALSE, EQ, 2f ! branch if not equal
bc DNZ, 0, 1b ! loop ctr times
addi r3, r0, 0 ! equal: return 0
b 3f
2:
addi r3, r0, 1 ! not equal: return 1
3:
rlwinm r6, r6, 1, 0, 30 ! r6 = size * 2
add sp, sp, r6 ! remove sets from stack
bclr ALWAYS, 0, 0

20
mach/powerpc/libem/com.s Normal file
View file

@ -0,0 +1,20 @@
#include "powerpc.h"
.sect .text
! Set complement.
! Stack: ( a -- ~a )
! With r3 = size of set
.define .com
.com:
mr r4, sp ! r4 = pointer to set a
rlwinm r5, r3, 30, 2, 31
mtspr ctr, r5 ! ctr = r3 / 4
1:
lwz r6, 0(r4)
nor r6, r6, r6 ! complement of word
stw r6, 0(r4)
addi r4, r4, 4
bc DNZ, 0, 1b ! loop ctr times
bclr ALWAYS, 0, 0

View file

@ -13,10 +13,10 @@
lwz r4, 4(sp) /* r4 = bit number */ lwz r4, 4(sp) /* r4 = bit number */
addi r5, sp, 8 /* r5 = base address of bit set */ addi r5, sp, 8 /* r5 = base address of bit set */
srawi r6, r4, 3 /* r6 = byte address into set */ rlwinm r6, r4, 29, 3, 29 /* r6 = byte index of word in set */
andi. r7, r4, 7 /* r7 = bit within byte */ andi. r7, r4, 31 /* r7 = bit within word */
lbzx r8, r5, r6 /* r8 = individual byte from set */ lwzx r8, r5, r6 /* r8 = individual byte from set */
sraw r8, r8, r7 sraw r8, r8, r7
rlwinm r8, r8, 0, 31, 31 rlwinm r8, r8, 0, 31, 31

24
mach/powerpc/libem/ior.s Normal file
View file

@ -0,0 +1,24 @@
#include "powerpc.h"
.sect .text
! Set union.
! Stack: ( b a -- a+b )
! With r3 = size of set
.define .ior
.ior:
mr r4, sp ! r4 = ptr to set a
add r5, sp, r3 ! r5 = ptr to set b
rlwinm r6, r3, 30, 2, 31
mtspr ctr, r6 ! ctr = r3 / 4
1:
lwz r7, 0(r4)
lwz r8, 0(r5)
or r8, r7, r8 ! union of words
stw r8, 0(r5)
addi r4, r4, 4
addi r5, r5, 4
bc DNZ, 0, 1b ! loop ctr times
add sp, sp, r3
bclr ALWAYS, 0, 0

29
mach/powerpc/libem/set.s Normal file
View file

@ -0,0 +1,29 @@
#include "powerpc.h"
.sect .text
! Create singleton set.
! Stack: ( -- set )
! With r3 = size of set, r4 = bit number
.define .set
.set:
rlwinm r7, r3, 30, 2, 31
neg r5, r3
add sp, sp, r5 ! allocate set
mr r6, sp ! r6 = ptr to set
mtspr ctr, r7 ! ctr = r3 / 4
1:
rlwinm. r7, r4, 0, 0, 26 ! r7 = r4 & ~31
bc IFTRUE, EQ, 2f ! branch if r4 in 0..31
addi r5, r0, 0 ! no bit, word is zero
b 3f
2:
addi r5, r0, 1
slw r5, r5, r4 ! yes bit, set bit in word
3:
stw r5, 0(r6) ! store word in set
addi r4, r4, -32
addi r6, r6, 4
bc DNZ, 0, 1b ! loop ctr times
bclr ALWAYS, 0, 0

24
mach/powerpc/libem/xor.s Normal file
View file

@ -0,0 +1,24 @@
#include "powerpc.h"
.sect .text
! Set symmetric difference.
! Stack: ( b a -- a/b )
! With r3 = size of set
.define .xor
.xor:
mr r4, sp ! r4 = ptr to set a
add r5, sp, r3 ! r5 = ptr to set b
rlwinm r6, r3, 30, 2, 31
mtspr ctr, r6 ! ctr = r3 / 4
1:
lwz r7, 0(r4)
lwz r8, 0(r5)
xor r8, r7, r8 ! symmetric difference of words
stw r8, 0(r5)
addi r4, r4, 4
addi r5, r5, 4
bc DNZ, 0, 1b ! loop ctr times
add sp, sp, r3
bclr ALWAYS, 0, 0

21
mach/powerpc/libem/zer.s Normal file
View file

@ -0,0 +1,21 @@
#include "powerpc.h"
.sect .text
! Create empty set.
! Stack: ( -- set )
! With r3 = size of set
.define .zer
.zer:
rlwinm r7, r3, 30, 2, 31
addi r4, r0, 0 ! r4 = zero
neg r5, r3
add sp, sp, r5 ! allocate set
mr r6, sp ! r6 = ptr to set
mtspr ctr, r7 ! ctr = r3 / 4
1:
stw r4, 0(r6) ! store zero in set
addi r6, r6, 4
bc DNZ, 0, 1b ! loop ctr times
bclr ALWAYS, 0, 0

View file

@ -10,7 +10,7 @@ INT64 = 8
FP_OFFSET = 0 /* Offset of saved FP relative to our FP */ FP_OFFSET = 0 /* Offset of saved FP relative to our FP */
PC_OFFSET = 4 /* Offset of saved PC relative to our FP */ PC_OFFSET = 4 /* Offset of saved PC relative to our FP */
#define COMMENT(n) /* noop */ #define COMMENT(n) /* comment {LABEL, n} */
#define nicesize(x) ((x)==INT8 || (x)==INT16 || (x)==INT32 || (x)==INT64) #define nicesize(x) ((x)==INT8 || (x)==INT16 || (x)==INT32 || (x)==INT64)
@ -389,7 +389,7 @@ MOVES
/* Constants */ /* Constants */
from CONST_ALL smalls(%val) to GPR from CONST_ALL + CONST smalls(%val) to GPR
gen gen
COMMENT("move CONST_ALL->GPR smalls") COMMENT("move CONST_ALL->GPR smalls")
addi %2, R0, {CONST, %1.val} addi %2, R0, {CONST, %1.val}
@ -1160,7 +1160,7 @@ PATTERNS
loc $1 loc $1
los INT32 los INT32
pat los /* Load arbitrary size */ pat los $1==INT32 /* Load arbitrary size */
with GPR3 GPR4 STACK with GPR3 GPR4 STACK
kills ALL kills ALL
gen gen
@ -1283,7 +1283,7 @@ PATTERNS
loc $1 loc $1
sts INT32 sts INT32
pat sts /* Store arbitrary size */ pat sts $1==INT32 /* Store arbitrary size */
with GPR3 GPR4 STACK with GPR3 GPR4 STACK
kills ALL kills ALL
gen gen
@ -1463,9 +1463,11 @@ PATTERNS
andisX %a, %2, {CONST, hi(%1.val)} andisX %a, %2, {CONST, hi(%1.val)}
yields %a yields %a
pat and !defined($1) /* AND set */ pat and defined($1) /* AND set */
with STACK with STACK
kills ALL
gen gen
move {CONST, $1}, R3
bl {LABEL, ".and"} bl {LABEL, ".and"}
pat ior $1==4 /* OR word */ pat ior $1==4 /* OR word */
@ -1498,8 +1500,17 @@ PATTERNS
uses reusing %2, REG={OR_RIS, %2, hi(%1.val)} uses reusing %2, REG={OR_RIS, %2, hi(%1.val)}
yields {OR_RC, %2, lo(%1.val)} yields {OR_RC, %2, lo(%1.val)}
pat ior !defined($1) /* OR set */ pat ior defined($1) /* OR set */
with STACK with STACK
kills ALL
gen
move {CONST, $1}, R3
bl {LABEL, ".ior"}
/* OR set (variable), used in lang/m2/libm2/LtoUset.e */
pat ior !defined($1)
with GPR3 STACK
kills ALL
gen gen
bl {LABEL, ".ior"} bl {LABEL, ".ior"}
@ -1523,9 +1534,11 @@ PATTERNS
uses reusing %2, REG={XOR_RIS, %2, hi(%1.val)} uses reusing %2, REG={XOR_RIS, %2, hi(%1.val)}
yields {XOR_RC, %2, lo(%1.val)} yields {XOR_RC, %2, lo(%1.val)}
pat xor !defined($1) /* XOR set */ pat xor defined($1) /* XOR set */
with STACK with STACK
kills ALL
gen gen
move {CONST, $1}, R3
bl {LABEL, ".xor"} bl {LABEL, ".xor"}
pat com $1==INT32 /* NOT word */ pat com $1==INT32 /* NOT word */
@ -1547,11 +1560,23 @@ PATTERNS
with GPR with GPR
yields {NOT_R, %1} yields {NOT_R, %1}
pat com !defined($1) /* NOT set */ pat com defined($1) /* NOT set */
with STACK with STACK
gen gen
move {CONST, $1}, R3
bl {LABEL, ".com"} bl {LABEL, ".com"}
pat zer $1==4 /* Push zero */
leaving
loc 0
pat zer defined($1) /* Create empty set */
with STACK
kills ALL
gen
move {CONST, $1}, R3
bl {LABEL, ".zer"}
pat sli $1==4 /* Shift left (second << top) */ pat sli $1==4 /* Shift left (second << top) */
with CONST_ALL GPR with CONST_ALL GPR
uses reusing %2, REG uses reusing %2, REG
@ -1594,6 +1619,7 @@ PATTERNS
pat aar $1==INT32 /* Index array */ pat aar $1==INT32 /* Index array */
with GPR3 GPR4 GPR5 with GPR3 GPR4 GPR5
kills ALL
gen gen
bl {LABEL, ".aar4"} bl {LABEL, ".aar4"}
yields R3 yields R3
@ -1624,21 +1650,23 @@ PATTERNS
/* Sets */ /* Sets */
pat set defined($1) /* Create word with set bit */ pat set defined($1) /* Create singleton set */
leaving with GPR4 STACK
loc 1 kills ALL
exg INT32 gen
sli INT32 move {CONST, $1}, R3
bl {LABEL, ".set"}
pat set !defined($1) /* Create structure with set bit (variable) */ /* Create set (variable), used in lang/m2/libm2/LtoUset.e */
pat set !defined($1)
with GPR3 GPR4 STACK with GPR3 GPR4 STACK
kills ALL
gen gen
bl {LABEL, ".set"} bl {LABEL, ".set"}
pat inn /* Test for set bit */ pat inn defined($1) /* Test for set bit */
with STACK with STACK
kills ALL kills ALL
uses REG uses REG
@ -1648,7 +1676,6 @@ PATTERNS
bl {LABEL, ".inn"} bl {LABEL, ".inn"}
/* Boolean resolutions */ /* Boolean resolutions */
pat teq /* top = (top == 0) */ pat teq /* top = (top == 0) */
@ -1785,13 +1812,13 @@ PATTERNS
/* Compare and jump */ /* Compare and jump */
pat cmi /* Signed tristate compare */ pat cmi $1==INT32 /* Signed tristate compare */
with CONST_ALL GPR with CONST_ALL GPR
yields {TRISTATE_RC_S, %2, %1.val} yields {TRISTATE_RC_S, %2, %1.val}
with GPR GPR with GPR GPR
yields {TRISTATE_RR_S, %2, %1} yields {TRISTATE_RR_S, %2, %1}
pat cmu /* Unsigned tristate compare */ pat cmu $1==INT32 /* Unsigned tristate compare */
with CONST_ALL GPR with CONST_ALL GPR
yields {TRISTATE_RC_U, %2, %1.val} yields {TRISTATE_RC_U, %2, %1.val}
with GPR GPR with GPR GPR
@ -1805,6 +1832,13 @@ PATTERNS
leaving leaving
cmi INT32 cmi INT32
pat cms defined($1)
with STACK
kills ALL
gen
move {CONST, $1}, R3
bl {LABEL, ".cms"}
yields R3
@ -1993,10 +2027,6 @@ PATTERNS
move SP, %a move SP, %a
yields %a yields %a
pat lor $1==2 /* Load HP */
leaving
loe ".reghp"
pat str $1==0 /* Store FP */ pat str $1==0 /* Store FP */
with GPR with GPR
gen gen
@ -2007,18 +2037,14 @@ PATTERNS
gen gen
move %1, SP move %1, SP
pat str $1==2 /* Store HP */ pat loc ass $1==4 && $2==4 /* Drop 4 bytes from stack */
leaving
ste ".reghp"
pat loc ass $1==4 /* Drop 4 bytes from stack */
with exact GPR with exact GPR
/* nop */ /* nop */
with STACK with STACK
gen gen
addi SP, SP, {CONST, 4} addi SP, SP, {CONST, 4}
pat ass /* Adjust stack by variable amount */ pat ass $1==4 /* Adjust stack by variable amount */
with CONST2 STACK with CONST2 STACK
gen gen
move {SUM_RC, SP, %1.val}, SP move {SUM_RC, SP, %1.val}, SP
@ -2036,7 +2062,7 @@ PATTERNS
pat asp /* Adjust stack by constant amount */ pat asp /* Adjust stack by constant amount */
leaving leaving
loc $1 loc $1
ass ass 4

View file

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

View file

@ -4,42 +4,46 @@
* See the copyright notice in the ACK home directory, in the file "Copyright". * See the copyright notice in the ACK home directory, in the file "Copyright".
*/ */
#include <ansi.h>
#ifndef __OBJECT_INCLUDED__ #ifndef __OBJECT_INCLUDED__
#define __OBJECT_INCLUDED__ #define __OBJECT_INCLUDED__
_PROTOTYPE(int wr_open, (char *f)); struct ar_hdr;
_PROTOTYPE(void wr_close, (void)); struct outhead;
_PROTOTYPE(void wr_ohead, (struct outhead *h)); struct outrelo;
_PROTOTYPE(void wr_sect, (struct outsect *s, unsigned int c)); struct outsect;
_PROTOTYPE(void wr_outsect, (int sectno)); struct ranlib;
_PROTOTYPE(void wr_emit, (char *b, long c));
_PROTOTYPE(void wr_putc, (int c)); int wr_open(char *f);
_PROTOTYPE(void wr_relo, (struct outrelo *r, unsigned int c)); void wr_close(void);
_PROTOTYPE(void wr_name, (struct outname *n, unsigned int c)); void wr_ohead(struct outhead *h);
_PROTOTYPE(void wr_string, (char *s, long c)); void wr_sect(struct outsect *s, unsigned int c);
_PROTOTYPE(void wr_arhdr, (int fd, struct ar_hdr *a)); void wr_outsect(int sectno);
_PROTOTYPE(void wr_ranlib, (int fd, struct ranlib *r, long cnt)); void wr_emit(char *b, long c);
_PROTOTYPE(void wr_int2, (int fd, int i)); void wr_putc(int c);
_PROTOTYPE(void wr_long, (int fd, long l)); void wr_relo(struct outrelo *r, unsigned int c);
_PROTOTYPE(void wr_bytes, (int fd, char *buf, long l)); void wr_name(struct outname *n, unsigned int c);
_PROTOTYPE(int rd_open, (char *f)); void wr_string(char *s, long c);
_PROTOTYPE(int rd_fdopen, (int f)); void wr_arhdr(int fd, struct ar_hdr *a);
_PROTOTYPE(void rd_close, (void)); void wr_ranlib(int fd, struct ranlib *r, long cnt);
_PROTOTYPE(void rd_ohead, (struct outhead *h)); void wr_int2(int fd, int i);
_PROTOTYPE(void rd_sect, (struct outsect *s, unsigned int c)); void wr_long(int fd, long l);
_PROTOTYPE(void rd_outsect, (int sectno)); void wr_bytes(int fd, char *buf, long l);
_PROTOTYPE(void rd_emit, (char *b, long c)); int rd_open(char *f);
_PROTOTYPE(void rd_relo, (struct outrelo *r, unsigned int c)); int rd_fdopen(int f);
_PROTOTYPE(void rd_rew_relo, (struct outhead *head)); void rd_close(void);
_PROTOTYPE(void rd_name, (struct outname *n, unsigned int c)); void rd_ohead(struct outhead *h);
_PROTOTYPE(void rd_string, (char *s, long c)); void rd_sect(struct outsect *s, unsigned int c);
_PROTOTYPE(int rd_arhdr, (int fd, struct ar_hdr *a)); void rd_outsect(int sectno);
_PROTOTYPE(void rd_ranlib, (int fd, struct ranlib *r, long cnt)); void rd_emit(char *b, long c);
_PROTOTYPE(int rd_int2, (int fd)); void rd_relo(struct outrelo *r, unsigned int c);
_PROTOTYPE(long rd_long, (int fd)); void rd_rew_relo(struct outhead *head);
_PROTOTYPE(void rd_bytes, (int fd, char *buf, long l)); void rd_name(struct outname *n, unsigned int c);
_PROTOTYPE(int rd_fd, (void)); 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__ */ #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,22 @@
include("plat/build.lua")
build_as {
name = "as",
arch = "i386",
}
build_ncg {
name = "ncg",
arch = "i386",
}
return installable {
name = "tools",
map = {
["$(PLATDEP)/osx386/as"] = "+as",
["$(PLATDEP)/osx386/ncg"] = "+ncg",
["$(PLATIND)/descr/osx386"] = "./descr",
"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/{PLATFORM}/ncg
mapflag -gdb GF=-gdb
args {GF?} <
stdout
need .e
end
name as
from .s.so
to .o
program {EM}/lib/ack/{PLATFORM}/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,4 @@
installable {
name = "pkg",
map = { "plat/osx/include+pkg" }
}

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+pkg",
},
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,28 @@
include("plat/build.lua")
build_as {
name = "as",
arch = "powerpc",
}
build_ncg {
name = "ncg",
arch = "powerpc",
}
build_top {
name = "top",
arch = "powerpc",
}
return installable {
name = "tools",
map = {
["$(PLATDEP)/osxppc/as"] = "+as",
["$(PLATDEP)/osxppc/ncg"] = "+ncg",
["$(PLATDEP)/osxppc/top"] = "+top",
["$(PLATIND)/descr/osxppc"] = "./descr",
"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/{PLATFORM}/ncg
mapflag -gdb GF=-gdb
args {GF?} <
stdout
need .e
end
name asopt
from .s
to .so
program {EM}/lib/ack/{PLATFORM}/top
args
optimizer
stdin
stdout
end
name as
from .s.so
to .o
program {EM}/lib/ack/{PLATFORM}/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,4 @@
installable {
name = "pkg",
map = { "plat/osx/include+pkg" }
}

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+pkg",
},
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

@ -13,22 +13,14 @@
.sect .text .sect .text
begtext: begtext:
! This code is placed at the beginning of the ELF executable and is the ! This code is the first thing that runs. The booloader
! first thing that runs. ! passes the Open Firmware pointer in r5.
! !
! On entry, the stack looks like this: ! We keep the bootloader's stack. The ACK expects:
! !
! sp+... NULL ! sp+8 environment pointer
! sp+8+(4*argc) env (X quads) ! sp+4 argv as a pointer
! sp+4+(4*argc) NULL
! sp+4 argv (argc quads)
! sp argc ! sp argc
!
! The ACK actually expects:
!
! sp+8 argc
! sp+4 ptr to argv
! sp ptr to env
li32 r3, __openfirmware_ptr li32 r3, __openfirmware_ptr
stw r5, 0(r3) stw r5, 0(r3)
@ -47,15 +39,23 @@ begtext:
! falls through ! falls through
.define __exit .define __exit
.extern __exit
.define EXIT .define EXIT
.extern EXIT
__exit: __exit:
EXIT: EXIT:
b EXIT ! Halt the CPU. This code halts the default G3 emulation of
! qemu-system-ppc. It's wrong for some other CPU models.
#define hid0 0x3f0
#define mfmsr(r) [[31<<26]|[[r]<<21]|0x0a6]
#define mtmsr(r) [[31<<26]|[[r]<<21]|0x124]
mfspr r3, hid0
oris r3, r3, 0x00e0 ! set DOZE, NAP, SLEEP
mtspr hid0, r3 ! in hid0
.data4 mfmsr(3)
oris r3, r3, 0x0004 ! set POW
.data4 mtmsr(3) ! in msr
b EXIT ! If we failed to halt, then spin.
.define _openfirmware_call .define _openfirmware_call
.extern _openfirmware_call
_openfirmware_call: _openfirmware_call:
lwz r3, 0(sp) lwz r3, 0(sp)
li32 r4, __openfirmware_ptr li32 r4, __openfirmware_ptr
@ -66,15 +66,10 @@ _openfirmware_call:
! Define symbols at the beginning of our various segments, so that we can find ! Define symbols at the beginning of our various segments, so that we can find
! them. (Except .text, which has already been done.) ! them. (Except .text, which has already been done.)
.sect .data; begdata:
.sect .rom; begrom: .sect .rom; begrom:
.sect .data; begdata:
.sect .bss; begbss: .sect .bss; begbss:
! Some magic data. All EM systems need these.
.define _errno
.comm _errno, 4 ! Posix errno storage
! The argv and env arrays. ! The argv and env arrays.
.sect .rom .sect .rom
@ -82,6 +77,12 @@ argv: .data4 exename, 0
envp: .data4 0 envp: .data4 0
exename: .asciz 'qemuppc.img' exename: .asciz 'qemuppc.img'
! Some magic data. All EM systems need these.
.sect .bss
.define _errno
.comm _errno, 4 ! Posix errno storage
.define .trppc, .ignmask .define .trppc, .ignmask
.comm .trppc, 4 ! ptr to user trap handler .comm .trppc, 4 ! ptr to user trap handler
.comm .ignmask, 4 ! user trap ignore mask .comm .ignmask, 4 ! user trap ignore mask

View file

@ -0,0 +1,22 @@
MODULE test;
FROM Test IMPORT fail, finished;
TYPE charset = SET OF CHAR;
PROCEDURE Space(c: CHAR): BOOLEAN;
BEGIN
RETURN c IN charset{' ', 11C, 12C, 15C}
END Space;
BEGIN
IF Space('a') THEN
fail(1);
END;
IF NOT Space(' ') THEN
fail(2);
END;
IF NOT Space(12C) THEN
fail(3);
END;
finished;
END test.

View file

@ -11,20 +11,24 @@ definerule("plat_testsuite",
local testfiles = filenamesof( local testfiles = filenamesof(
"tests/plat/*.c", "tests/plat/*.c",
"tests/plat/*.e", "tests/plat/*.e",
"tests/plat/*.p" "tests/plat/*.p",
"tests/plat/bugs/*.mod"
) )
acklibrary { acklibrary {
name = "lib", name = "lib",
srcs = { "tests/plat/lib/test.c" }, srcs = { "tests/plat/lib/test.c" },
hdrs = { "tests/plat/lib/test.h" }, hdrs = {
"tests/plat/lib/test.h",
"tests/plat/lib/Test.def"
},
vars = { plat = e.plat }, vars = { plat = e.plat },
} }
local tests = {} local tests = {}
for _, f in ipairs(testfiles) do for _, f in ipairs(testfiles) do
local fs = replace(basename(f), "%..$", "") local fs = replace(basename(f), "%.[^.]+$", "")
local _, _, lang = fs:find("_(.)$") local _, _, lang = fs:find("_([^_]+)$")
if not lang then if not lang then
lang = "e" lang = "e"
end end
@ -49,7 +53,7 @@ definerule("plat_testsuite",
"util/build+testrunner" "util/build+testrunner"
}, },
commands = { commands = {
"(%{ins[2]} "..e.method.." %{ins[1]} 5 %{ins[3]} || echo FAILED) 2>&1 > %{outs}", "(%{ins[2]} "..e.method.." %{ins[1]} 5 %{ins[3]} || echo FAILED) > %{outs}",
} }
} }
end end

View file

@ -7,10 +7,10 @@
/* Test non-existent bit */ /* Test non-existent bit */
.1 .1
rom 0I1, 0I1, 0I1, 0I1 rom 0I4
loe .1 loe .1
loc 1 /* bit number */ loc 1 /* bit number */
inn EM_WSIZE inn 4
zeq *1 zeq *1
loc __LINE__ loc __LINE__
@ -21,9 +21,12 @@
/* Test existent bit */ /* Test existent bit */
.2 .2
rom 2I1, 0I1, 0I1, 0I1 rom 16384
.21
rom 14 /* to defeat constant folding */
loe .2 loe .2
loc 1 /* bit number */ loe .21 /* bit number */
inn EM_WSIZE inn EM_WSIZE
zne *2 zne *2
@ -35,10 +38,9 @@
/* Test non-existent high bit */ /* Test non-existent high bit */
.3 .3
rom 0I1, 0I1, 0I1, 0I1 rom 0, 0
rom 0I1, 0I1, 0I1, 0I1
.31 .31
rom (EM_WSIZE*8)+1 /* to defeat constant folding */ rom 8 /* to defeat constant folding */
lae .3 lae .3
loi EM_WSIZE*2 loi EM_WSIZE*2
@ -54,16 +56,7 @@
/* Test existent high bit */ /* Test existent high bit */
.4 .4
#if EM_WSIZE == 2 rom 0, 2
rom 0I1, 0I1
rom 2I1, 0I1
#elif EM_WSIZE == 4
rom 0I1, 0I1, 0I1, 0I1
rom 2I1, 0I1, 0I1, 0I1
#else
#error Unknown word size
#endif
.41 .41
rom (EM_WSIZE*8)+1 /* to defeat constant folding */ rom (EM_WSIZE*8)+1 /* to defeat constant folding */

6
tests/plat/lib/Test.def Normal file
View file

@ -0,0 +1,6 @@
(*$Foreign*)
DEFINITION MODULE Test;
PROCEDURE finished();
PROCEDURE writehex(code: LONGINT);
PROCEDURE fail(code: LONGINT);
END Test.

View file

@ -1,7 +0,0 @@
include("plat/build.lua")
acklibrary {
name = "lib",
srcs = { "./test.c" },
hdrs = { "./test.h" },
}

View file

@ -27,7 +27,7 @@ get_test_output() {
qemu-system-ppc) img="-kernel $img" ;; qemu-system-ppc) img="-kernel $img" ;;
esac esac
$timeoutprog -t $timeout -- $method -nographic $img 2>&1 > $result $timeoutprog -t $timeout -- $method -nographic $img > $result 2>&1
;; ;;
qemu-*) qemu-*)
@ -37,7 +37,7 @@ get_test_output() {
exit 0 exit 0
fi fi
$method $img 2>&1 > $result $method $img > $result 2>&1
;; ;;
*) *)

View file

@ -1,25 +1,58 @@
.TH ASLOD 1 "$Revision$" .TH AELFLOD 1 "$Revision$"
.SH NAME .SH NAME
aelflod \- ACK ELF loader aelflod \- ACK ELF loader
.SH SYNOPSIS .SH SYNOPSIS
aelflod [-h] [-v] inputfile outputfile .B aelflod
[-a\fInumber\fP] [-b] [-h] [-l] [-m\fInumber\fP] [-v] inputfile outputfile
.SH DESCRIPTION .SH DESCRIPTION
.I aelflod .I aelflod
converts an absolute ack.out file into a simple binary memory converts an absolute ack.out file into a simple binary memory
dump wrapped up in an ELF executable. It is suitable for producing dump wrapped up in an ELF executable.
executables for operating systems such as Linux. It is suitable for producing executables for operating systems
such as Linux.
.PP
.I aelflod
accepts the following flags:
.TP
.BI \-a number
Set the ABI in the ELF header to \fInumber\fP.
The default value is \fI3\fP for Linux.
.TP
.B \-b
Write a big-endian ELF file.
.TP
.B \-h
Print a help message and exit.
.TP
.B \-l
Write a little-endian ELF file.
This is the default.
.TP
.BI \-m number
Set the machine type in the ELF header to \fInumber\fP.
The default value is \fI3\fP for Intel 386 (i386).
Other values are \fI4\fP for Motorola 68000 (m68k)
and \fI20\fP for PowerPC.
.TP
.B \-v
Be verbose.
.PP
The input file must contain exactly four segments: TEXT, ROM, The input file must contain exactly four segments: TEXT, ROM,
DATA and BSS, in that order, all occupying contiguous memory. DATA and BSS, in that order, all occupying contiguous memory.
The file must have all references resolved and be linked to a The file must have all references resolved and be linked to a
fixed address. The fixed address must be at least 0x54 bytes fixed address.
greater than a page boundary, in order to make room for the ELF The fixed address must be at least 0x54 bytes greater than a
header itself. page boundary, in order to make room for the ELF header itself.
.PP
aelflod will write out an ELF header followed by each segment, in .I aelflod
order, ensuring that enough padding is inserted between each segment will write out an ELF header followed by each segment, in order,
to keep the offsets correct. The created executable will contain just ensuring that enough padding is inserted between each segment
one rwx segment, and no sections. to keep the offsets correct.
The created executable will contain just one ELF segment mapped rwx.
.PP
If the input file has symbols, then
.I aelflod
will convert the symbol table to ELF.
The output file has ELF section headers if and only if it has symbols.
.SH "SEE ALSO" .SH "SEE ALSO"
ack.out(5) ack.out(5)

View file

@ -38,7 +38,11 @@ int elfmachine = 3; /* machine = EM_386 */
struct outhead outhead; struct outhead outhead;
struct outsect outsect[S_MAX]; struct outsect outsect[S_MAX];
struct outname* outname = NULL;
char* stringarea; char* stringarea;
uint32_t ack_off_char;
int nstab = 0; /* S_STB symbol count */
int nsym = 0; /* other symbol count */
char* outputfile = NULL; /* Name of output file, or NULL */ char* outputfile = NULL; /* Name of output file, or NULL */
char* program; /* Name of current program: argv[0] */ char* program; /* Name of current program: argv[0] */
@ -49,23 +53,30 @@ FILE* output; /* Output stream */
#define readf(a, b, c) fread((a), (b), (int)(c), input) #define readf(a, b, c) fread((a), (b), (int)(c), input)
#define writef(a, b, c) fwrite((a), (b), (int)(c), output) #define writef(a, b, c) fwrite((a), (b), (int)(c), output)
/* Header and program header table of an ELF object file. */ /* Contents of an ELF object file. */
#define ELF_HEADER_SIZE 0x34 #define ELF_HEADER_SIZE 0x34
#define PROGRAM_HEADER_SIZE 0x20 #define PROGRAM_HEADER_SIZE 0x20
#define PROGRAM_HEADER_COUNT 1 #define PROGRAM_HEADER_COUNT 1
unsigned long codeoffset; #define SECTION_HEADER_SIZE 0x28
#define STAB_SYMBOL_SIZE 12
#define ELF_SYMBOL_SIZE 16
uint32_t code_offset; /* ELF segment */
uint32_t stab_offset; /* Debugger symbol table */
uint32_t symtab_offset; /* ELF symbol table */
uint32_t strtab_offset; /* String table */
uint32_t shstrtab_offset; /* Section header string table */
uint32_t sh_offset; /* ELF section headers */
int sh_count = 0; /* Number of ELF sections */
int shstrtab_nr = 0; /* Section number of .shstrtab */
int shstrtab_size;
const char elf_le_ident_string[] = { const char elf_le_ident_string[] = {
0x7F, 'E', 'L', 'F' 0x7F, 'E', 'L', 'F'
}; };
/* Output file definitions and such */
#define HDR_LENGTH 32
char hdr[HDR_LENGTH] ;
bool verbose = false; bool verbose = false;
/* Segment numbers understood by aelflod. */ /* Segment numbers understood by aelflod. */
@ -78,13 +89,33 @@ enum {
NUM_SEGMENTS NUM_SEGMENTS
}; };
#define N_EXT 040 /*
#define N_UNDEF 00 * ELF section numbers count up in the order that we write the ELF
#define N_ABS 01 * section headers. If we have no debugger symbols, we will skip
#define N_TEXT 02 * .stab and .stabstr, then subtract 2 from all later numbers.
#define N_DATA 03 */
#define N_BSS 04 enum {
#define N_FN 037 N_UNDEF = 0,
N_TEXT,
N_RODATA,
N_DATA,
N_BSS,
N_STAB,
N_STABSTR,
N_SYMTAB,
N_STRTAB,
N_SHSTRTAB,
NUM_ELF_SECTIONS,
};
const char shstrtab[] =
"\0.text\0.rodata\0.data\0.bss\0.stab\0.stabstr\0"
".symtab\0.strtab\0.shstrtab";
/* Compiler appends one more "\0". */
const int sh_name[] = {
/* Index of each name in shstrtab: */
0, 1, 7, 15, 21, 26, 32,
41, 49, 57,
};
/* Produce an error message and exit. */ /* Produce an error message and exit. */
@ -121,6 +152,80 @@ int follows(struct outsect* pa, struct outsect* pb)
return (pa->os_base >= align(pb->os_base+pb->os_size, pa->os_lign)); return (pa->os_base >= align(pb->os_base+pb->os_size, pa->os_lign));
} }
/* Convert a symbol's name index from ack.out to ELF. */
uint32_t cvname(struct outname* n)
{
if (n->on_foff) {
/* ack.out: offset from beginning of file
* ELF: index in string table
* the + 1 because we prepend a '\0' */
return n->on_foff - ack_off_char + 1;
} else
return 0; /* no name */
}
/* Convert a symbol's type and binding from ack.out to ELF. */
int cvinfo(struct outname* n)
{
int bind, type;
switch (n->on_type & S_ETC) {
case S_SCT:
type = 3; /* STT_SECTION */
break;
case S_FIL:
case S_MOD:
type = 4; /* STT_FILE */
break;
default:
switch (n->on_type & S_TYP) {
case S_MIN + TEXT:
type = 2; /* STT_FUNC */
break;
case S_MIN + ROM:
case S_MIN + DATA:
case S_MIN + BSS:
case S_MIN + NUM_SEGMENTS:
type = 1; /* STT_OBJECT */
break;
default:
type = 0; /* STT_NOTYPE */
break;
}
break;
}
if (n->on_type & S_EXT)
bind = 1; /* STB_GLOBAL */
else
bind = 0; /* STB_LOCAL */
return (bind << 4) | type;
}
/* Convert a symbol's section index from ack.out to ELF. */
int cvsect(struct outname* n)
{
switch (n->on_type & S_TYP) {
case S_ABS:
return 0xfff1; /* SHN_ABS */
case S_MIN + TEXT:
return N_TEXT;
case S_MIN + ROM:
return N_RODATA;
case S_MIN + DATA:
return N_DATA;
case S_MIN + BSS:
case S_MIN + NUM_SEGMENTS:
return N_BSS;
default:
return N_UNDEF;
}
}
/* Writes a byte. */ /* Writes a byte. */
void emit8(unsigned char value) void emit8(unsigned char value)
@ -220,6 +325,188 @@ void emitphdr(unsigned long address, unsigned long filesize,
fileoffset += filesize; fileoffset += filesize;
} }
/* The next few functions write parts of the symbol table. */
void emit_stab(void)
{
struct outname* n;
int i;
for (i = 0; i < outhead.oh_nname; i++) {
n = &outname[i];
if (n->on_type & S_STB) {
emit32(cvname(n)); /* name index */
emit8(n->on_type >> 8); /* type */
emit8(cvsect(n)); /* section */
emit16(n->on_desc); /* desc */
emit32(n->on_valu); /* value */
}
}
}
void emit_symtab(void)
{
struct outname* n;
int i;
for (i = 0; i < outhead.oh_nname; i++) {
n = &outname[i];
if (!(n->on_type & S_STB)) {
emit32(cvname(n)); /* name index */
emit32(n->on_valu); /* value */
emit32(0); /* size = unknown */
emit8(cvinfo(n)); /* info */
emit8(0); /* other */
emit16(cvsect(n)); /* section */
}
}
}
void emit_strtab(void)
{
/* We prepend a '\0' because ELF uses offset 0 for symbols
* without a name. */
emit8('\0');
writef(stringarea, outhead.oh_nchar, 1);
}
void emit_shstrtab(void)
{
if (nstab) {
writef(shstrtab, sizeof(shstrtab), 1);
} else {
/* Skip .stab and .stabstr */
int i = sh_name[N_SYMTAB];
writef(shstrtab, sh_name[N_STAB], 1);
writef(shstrtab + i, sizeof(shstrtab) - i, 1);
}
}
/* Writes out an ELF section header. */
void emit_sh(int i)
{
uint32_t name, type, flags, addr, offset, size, addralign,
link, entsize;
/* If no debugger symbols, skip .stab and .stabstr */
if (nstab == 0 && (i == N_STAB || i == N_STABSTR))
return;
name = sh_name[i];
if (nstab == 0 && i >= N_STAB)
name -= (sh_name[N_SYMTAB] - sh_name[N_STAB]);
switch (i) {
case N_TEXT:
case N_RODATA:
case N_DATA:
case N_STAB:
type = 1; /* SHT_PROGBITS */
break;
case N_BSS:
type = 8; /* SHT_NOBITS */
break;
case N_SYMTAB:
type = 2; /* SHT_SYMTAB */
break;
case N_STABSTR:
case N_STRTAB:
type = 3; /* SHT_STRTAB */
break;
default:
type = 0; /* SHT_NULL */
break;
}
switch (i) {
case N_TEXT:
flags = 4|2; /* SHF_EXECINSTR|SHF_ALLOC */
addr = outsect[TEXT].os_base;
offset = code_offset;
size = outsect[TEXT].os_size;
addralign = outsect[TEXT].os_lign;
break;
case N_RODATA:
flags = 2; /* SHF_ALLOC */
addr = outsect[ROM].os_base;
offset = code_offset + outsect[TEXT].os_size;
size = outsect[ROM].os_size;
addralign = outsect[ROM].os_lign;
break;
case N_DATA:
flags = 2|1; /* SHF_ALLOC|SHF_WRITE */
addr = outsect[DATA].os_base;
offset = code_offset + outsect[TEXT].os_size +
outsect[ROM].os_size;
size = outsect[DATA].os_size;
addralign = outsect[DATA].os_lign;
break;
case N_BSS:
flags = 2|1; /* SHF_ALLOC|SHF_WRITE */
addr = outsect[BSS].os_base;
offset = code_offset + outsect[TEXT].os_size +
outsect[ROM].os_size + outsect[DATA].os_size;
size = outsect[BSS].os_size;
addralign = outsect[BSS].os_lign;
break;
default:
flags = addr = offset = size = addralign = 0;
break;
}
entsize = 0;
switch (i) {
case N_STAB:
offset = stab_offset;
size = STAB_SYMBOL_SIZE * nstab;
entsize = STAB_SYMBOL_SIZE;
break;
case N_SYMTAB:
offset = symtab_offset;
size = ELF_SYMBOL_SIZE * nsym;
entsize = ELF_SYMBOL_SIZE;
break;
case N_STABSTR:
case N_STRTAB:
/* .stabstr, .strtab share the string area */
offset = strtab_offset;
/* the + 1 because we prepend a '\0' */
size = 1 + outhead.oh_nchar;
break;
case N_SHSTRTAB:
offset = shstrtab_offset;
size = shstrtab_size;
break;
}
/* Link .stab to .stabstr and .symtab to .strtab */
switch (i) {
case N_STAB:
link = N_STABSTR;
break;
case N_SYMTAB:
link = N_STRTAB;
if (nstab == 0)
link -= 2;
break;
default:
link = 0;
break;
}
emit32(name);
emit32(type);
emit32(flags);
emit32(addr);
emit32(offset);
emit32(size);
emit32(link);
emit32(0); /* info */
emit32(addralign);
emit32(entsize);
}
/* Macros from modules/src/object/obj.h */ /* Macros from modules/src/object/obj.h */
#define Xchar(ch) ((ch) & 0377) #define Xchar(ch) ((ch) & 0377)
#define uget2(c) (Xchar((c)[0]) | ((unsigned) Xchar((c)[1]) << 8)) #define uget2(c) (Xchar((c)[0]) | ((unsigned) Xchar((c)[1]) << 8))
@ -264,6 +551,57 @@ int rsect(FILE* f, struct outsect* sect)
return 1 ; return 1 ;
} }
/*
* Read the ack.out symbol table and string area. Count symbols.
* Seek back to the current file position.
*/
int rnames(FILE* f)
{
long told;
int i;
/* If no symbols, then do nothing successfully. */
if (outhead.oh_nname == 0)
return 1;
/* Seek to the symbol table. */
told = ftell(f);
if (told == -1)
return 0;
ack_off_char = OFF_CHAR(outhead); /* for cvname() */
if (fseek(f, OFF_NAME(outhead), SEEK_SET))
return 0;
/* Using calloc(a, b) to check if a * b would overflow. */
outname = calloc(outhead.oh_nname, sizeof(outname[0]));
if (outname == NULL)
fatal("out of memory.");
for (i = 0; i < outhead.oh_nname; i++) {
char buf[SZ_NAME], *c;
if (fread(buf, SZ_NAME, 1, f) != 1)
return 0;
c = buf;
outname[i].on_foff = get4(c); c += 4;
outname[i].on_type = uget2(c); c += 2;
outname[i].on_desc = uget2(c); c += 2;
outname[i].on_valu = get4(c);
if (outname[i].on_type & S_STB)
nstab++;
else
nsym++;
}
stringarea = malloc(outhead.oh_nchar);
if (stringarea == NULL)
fatal("out of memory.");
if (fread(stringarea, outhead.oh_nchar, 1, f) != 1)
return 0;
if (fseek(f, told, SEEK_SET))
return 0;
return 1;
}
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
/* General housecleaning and setup. */ /* General housecleaning and setup. */
@ -287,7 +625,7 @@ int main(int argc, char* argv[])
break; break;
case 'h': case 'h':
fprintf(stderr, "%s: Syntax: aelflod [-a<number>] [-b] [-h] [-l]\n\t[-m<number>] <inputfile> <outputfile>\n", fprintf(stderr, "%s: Syntax: aelflod [-a<number>] [-b] [-h] [-l]\n\t[-m<number>] [-v] <inputfile> <outputfile>\n",
program); program);
exit(0); exit(0);
@ -360,6 +698,11 @@ int main(int argc, char* argv[])
} }
} }
/* Read the symbol table, then seek back to the section data. */
if (!rnames(input))
fatal("failed to read symbol table.");
/* A few checks */ /* A few checks */
if (outsect[BSS].os_flen != 0) if (outsect[BSS].os_flen != 0)
@ -387,8 +730,8 @@ int main(int argc, char* argv[])
/* Ensure the base address doesn't overlap the file header. */ /* Ensure the base address doesn't overlap the file header. */
codeoffset = outsect[TEXT].os_base & 0x1FFF; code_offset = outsect[TEXT].os_base & 0x1FFF;
if (codeoffset < (ELF_HEADER_SIZE + PROGRAM_HEADER_SIZE*PROGRAM_HEADER_COUNT)) if (code_offset < (ELF_HEADER_SIZE + PROGRAM_HEADER_SIZE*PROGRAM_HEADER_COUNT))
fatal("base address too small --- overlaps ELF header"); fatal("base address too small --- overlaps ELF header");
/* Rationalise the memory sizes. */ /* Rationalise the memory sizes. */
@ -398,6 +741,30 @@ int main(int argc, char* argv[])
outsect[DATA].os_size = outsect[BSS ].os_base - outsect[DATA].os_base; outsect[DATA].os_size = outsect[BSS ].os_base - outsect[DATA].os_base;
outsect[BSS ].os_size = align(outsect[BSS].os_size, outsect[BSS].os_lign); outsect[BSS ].os_size = align(outsect[BSS].os_size, outsect[BSS].os_lign);
stab_offset = code_offset + outsect[TEXT].os_size +
outsect[ROM].os_size + outsect[DATA].os_size;
/* If we have symbols, then calculate some offsets. */
if (outhead.oh_nname) {
sh_count = NUM_ELF_SECTIONS;
shstrtab_nr = N_SHSTRTAB;
shstrtab_size = sizeof(shstrtab);
if (nstab == 0) {
/* Skip .stab and .stabstr */
sh_count -= 2;
shstrtab_nr -= 2;
shstrtab_size -=
(sh_name[N_SYMTAB] - sh_name[N_STAB]);
}
symtab_offset = stab_offset + STAB_SYMBOL_SIZE * nstab;
strtab_offset = symtab_offset + ELF_SYMBOL_SIZE * nsym;
/* the + 1 because we prepend a '\0' */
shstrtab_offset = strtab_offset + 1 + outhead.oh_nchar;
sh_offset = shstrtab_offset + shstrtab_size;
}
/* Write out the ELF file header. */ /* Write out the ELF file header. */
writef(elf_le_ident_string, 4, 1); writef(elf_le_ident_string, 4, 1);
@ -414,33 +781,29 @@ int main(int argc, char* argv[])
emit32(1); /* ELF version again */ emit32(1); /* ELF version again */
emit32(outsect[TEXT].os_base); /* entry point */ emit32(outsect[TEXT].os_base); /* entry point */
emit32(ELF_HEADER_SIZE); /* program header offset */ emit32(ELF_HEADER_SIZE); /* program header offset */
emit32(0); /* section header offset */ emit32(sh_offset); /* section header offset */
emit32(0); /* flags */ emit32(0); /* flags */
emit16(ELF_HEADER_SIZE); /* elf header size */ emit16(ELF_HEADER_SIZE); /* elf header size */
emit16(PROGRAM_HEADER_SIZE); /* program header entry size */ emit16(PROGRAM_HEADER_SIZE); /* program header entry size */
emit16(1); /* number of program header entries */ emit16(1); /* number of program header entries */
emit16(0x28); /* section header entry size */ emit16(SECTION_HEADER_SIZE); /* section header entry size */
emit16(0); /* number of section header entries */ emit16(sh_count); /* number of section header entries */
emit16(0); /* section header string table index = SHN_UNDEF */ emit16(shstrtab_nr); /* section header string table index */
/* Write out a single rwx section for the entire program. */ /* Write out a single rwx section for the entire program. */
{ {
unsigned long filelength = codeoffset + uint32_t filelength = stab_offset;
outsect[TEXT].os_size + uint32_t memlength = filelength + outsect[BSS].os_size;
outsect[ROM].os_size +
outsect[DATA].os_size;
unsigned long memlength = filelength + emitphdr(outsect[TEXT].os_base & ~0x1FFF,
outsect[BSS].os_size; filelength, memlength, 0, 4|2|1);
emitphdr(outsect[TEXT].os_base & ~0x1FFF, filelength, memlength,
0, 4|2|1);
} }
/* Write padding until the code start. */ /* Write padding until the code start. */
fseek(output, codeoffset, SEEK_SET); if (fseek(output, code_offset, SEEK_SET))
fatal("output seek error");
/* Write out the actual data. */ /* Write out the actual data. */
@ -448,6 +811,19 @@ int main(int argc, char* argv[])
emits(&outsect[ROM]); emits(&outsect[ROM]);
emits(&outsect[DATA]); emits(&outsect[DATA]);
/* Write out the symbol table and section headers. */
if (outhead.oh_nname) {
int i;
if (nstab)
emit_stab();
emit_symtab();
emit_strtab();
emit_shstrtab();
for (i = 0; i < NUM_ELF_SECTIONS; i++)
emit_sh(i);
}
if (ferror(output)) if (ferror(output))
fatal("output write error"); fatal("output write error");
if (outputfile) if (outputfile)
@ -459,7 +835,7 @@ int main(int argc, char* argv[])
{ {
uint32_t ss = 0; uint32_t ss = 0;
printf(" address length\n"); printf(" address length\n");
printf(" ehdr : %08"PRIx32" %08"PRIx32"\n", outsect[TEXT].os_base & ~0x1FFF, codeoffset); printf(" ehdr : %08"PRIx32" %08"PRIx32"\n", outsect[TEXT].os_base & ~0x1FFF, code_offset);
printf(" text : %08"PRIx32" %08"PRIx32"\n", outsect[TEXT].os_base, outsect[TEXT].os_size); printf(" text : %08"PRIx32" %08"PRIx32"\n", outsect[TEXT].os_base, outsect[TEXT].os_size);
printf(" rom : %08"PRIx32" %08"PRIx32"\n", outsect[ROM].os_base, outsect[ROM].os_size); printf(" rom : %08"PRIx32" %08"PRIx32"\n", outsect[ROM].os_base, outsect[ROM].os_size);
printf(" data : %08"PRIx32" %08"PRIx32"\n", outsect[DATA].os_base, outsect[DATA].os_size); printf(" data : %08"PRIx32" %08"PRIx32"\n", outsect[DATA].os_base, outsect[DATA].os_size);

View file

@ -2,19 +2,19 @@
.SH NAME .SH NAME
aslod \- ACK simple loader aslod \- ACK simple loader
.SH SYNOPSIS .SH SYNOPSIS
aslod [-h] [-v] inputfile outputfile .B aslod
[-h] [-v] inputfile outputfile
.SH DESCRIPTION .SH DESCRIPTION
.I aslod .I aslod
converts an absolute ack.out file into a simple binary memory converts an absolute ack.out file into a simple binary memory dump.
dump. It is suitable for producing RAM images, executables for It is suitable for producing RAM images, executables for
simple operating systems such as CP/M, DOS, etc. simple operating systems such as CP/M, DOS, etc.
.PP
The input file must contain exactly four segments: TEXT, ROM, The input file must contain exactly four segments: TEXT, ROM,
DATA and BSS, in that order, all occupying contiguous memory. DATA and BSS, in that order, all occupying contiguous memory.
The file must have all references resolved and be linked to a The file must have all references resolved and be linked to a
fixed address. aslod will dump the segments, in order, such fixed address. aslod will dump the segments, in order, such
that the first byte of TEXT is at offset 0 in the file that the first byte of TEXT is at offset 0 in the file
(regardless of where it is in memory). (regardless of where it is in memory).
.SH "SEE ALSO" .SH "SEE ALSO"
ack.out(5) ack.out(5)

View file

@ -45,12 +45,6 @@ FILE* output; /* Output stream */
#define readf(a, b, c) fread((a), (b), (int)(c), input) #define readf(a, b, c) fread((a), (b), (int)(c), input)
#define writef(a, b, c) fwrite((a), (b), (int)(c), output) #define writef(a, b, c) fwrite((a), (b), (int)(c), output)
/* Output file definitions and such */
#define HDR_LENGTH 32
char hdr[HDR_LENGTH] ;
bool verbose = false; bool verbose = false;
/* Segment numbers understood by aslod. */ /* Segment numbers understood by aslod. */
@ -63,14 +57,6 @@ enum {
NUM_SEGMENTS NUM_SEGMENTS
}; };
#define N_EXT 040
#define N_UNDEF 00
#define N_ABS 01
#define N_TEXT 02
#define N_DATA 03
#define N_BSS 04
#define N_FN 037
/* Produce an error message and exit. */ /* Produce an error message and exit. */
void fatal(const char* s, ...) void fatal(const char* s, ...)

View file

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