diff --git a/build.lua b/build.lua index 506011370..f7b0ca1ea 100644 --- a/build.lua +++ b/build.lua @@ -9,6 +9,8 @@ vars.plats = { "linux386", "linux68k", "linuxppc", + "osx386", + "osxppc", "qemuppc", "pc86", "rpi", diff --git a/first/testsummary.sh b/first/testsummary.sh index 6301cb977..6c2d3e39a 100755 --- a/first/testsummary.sh +++ b/first/testsummary.sh @@ -3,9 +3,15 @@ echo "" succeeding="$(find "$@" -size 0)" notsucceeding="$(find "$@" ! -size 0)" -skipped="$(grep -l @@SKIPPED $notsucceeding)" -timedout="$(grep -l @@TIMEDOUT $notsucceeding)" -failed="$(grep -l @@FAIL $notsucceeding)" +if [ "$notsucceeding" != "" ]; then + skipped="$(grep -l @@SKIPPED $notsucceeding)" + timedout="$(grep -l @@TIMEDOUT $notsucceeding)" + failed="$(grep -l @@FAIL $notsucceeding)" +else + skipped= + timedout= + failed= +fi for a in $failed $timedout; do echo "**** $a" @@ -13,11 +19,11 @@ for a in $failed $timedout; do echo "" done -echo "$(echo $succeeding | wc -w) tests passed" -echo "$(echo $notsucceeding | wc -w) tests failed to pass" -echo "$(echo $skipped | wc -w) were skipped (see build log for details)" -echo "$(echo $timedout | wc -w) timed out" -echo "$(echo $failed | wc -w) failed" +echo "$(echo "$succeeding" | wc -w) tests passed" +echo "$(echo "$notsucceeding" | wc -w) tests failed to pass" +echo "$(echo "$skipped" | wc -w) were skipped (see build log for details)" +echo "$(echo "$timedout" | wc -w) timed out" +echo "$(echo "$failed" | wc -w) failed" echo "" if [ "$failed" != "" -o "$timedout" != "" ]; then diff --git a/lang/b/lib/build.lua b/lang/b/lib/build.lua index a45ab78a0..62ae88966 100644 --- a/lang/b/lib/build.lua +++ b/lang/b/lib/build.lua @@ -11,8 +11,8 @@ for _, plat in ipairs(vars.plats) do deps = { "./*.h", "h+emheaders", - "lang/cem/libcc.ansi/headers+headers", - "plat/"..plat.."/include+headers", + "lang/cem/libcc.ansi/headers+pkg", + "plat/"..plat.."/include+pkg", }, vars = { plat = plat } } diff --git a/lang/basic/lib/build.lua b/lang/basic/lib/build.lua index 71710a542..cff3b57ca 100644 --- a/lang/basic/lib/build.lua +++ b/lang/basic/lib/build.lua @@ -10,8 +10,8 @@ for _, plat in ipairs(vars.plats) do hdrs = {}, -- must be empty deps = { "h+emheaders", - "lang/cem/libcc.ansi/headers+headers", - "plat/"..plat.."/include+headers", + "lang/cem/libcc.ansi/headers+pkg", + "plat/"..plat.."/include+pkg", }, vars = { plat = plat } } diff --git a/lang/cem/libcc.ansi/build.lua b/lang/cem/libcc.ansi/build.lua index 601a50de4..20591f803 100644 --- a/lang/cem/libcc.ansi/build.lua +++ b/lang/cem/libcc.ansi/build.lua @@ -51,8 +51,8 @@ for _, plat in ipairs(vars.plats) do }, hdrs = {}, -- must be empty deps = { - "lang/cem/libcc.ansi/headers+headers", - "plat/"..plat.."/include+headers", + "lang/cem/libcc.ansi/headers+pkg", + "plat/"..plat.."/include+pkg", "./malloc/malloc.h", "./math/localmath.h", "./stdio/loc_incl.h", diff --git a/lang/m2/libm2/build.lua b/lang/m2/libm2/build.lua index d0861b966..a1a9e5c0a 100644 --- a/lang/m2/libm2/build.lua +++ b/lang/m2/libm2/build.lua @@ -29,8 +29,8 @@ for _, plat in ipairs(vars.plats) do }, hdrs = {}, -- must be empty deps = { - "lang/cem/libcc.ansi/headers+headers", - "plat/"..plat.."/include+headers", + "lang/cem/libcc.ansi/headers+pkg", + "plat/"..plat.."/include+pkg", "h+emheaders", }, vars = { plat = plat } diff --git a/lang/pc/libpc/build.lua b/lang/pc/libpc/build.lua index 7845991e5..215f0c745 100644 --- a/lang/pc/libpc/build.lua +++ b/lang/pc/libpc/build.lua @@ -17,8 +17,8 @@ for _, plat in ipairs(vars.plats) do }, hdrs = {}, -- must be empty deps = { - "lang/cem/libcc.ansi/headers+headers", - "plat/"..plat.."/include+headers", + "lang/cem/libcc.ansi/headers+pkg", + "plat/"..plat.."/include+pkg", "h+emheaders", }, vars = { plat = plat } diff --git a/mach/powerpc/as/mach2.c b/mach/powerpc/as/mach2.c index 96d6690df..480c5faa3 100644 --- a/mach/powerpc/as/mach2.c +++ b/mach/powerpc/as/mach2.c @@ -88,4 +88,4 @@ %type c %type e16 u8 u7 u6 u5 u4 u2 u1 -%type nb ds bda bdl lia lil +%type nb ds bda bdl lia lil spr_num diff --git a/mach/powerpc/as/mach4.c b/mach/powerpc/as/mach4.c index 3f79ca86c..e1fb3fdf0 100644 --- a/mach/powerpc/as/mach4.c +++ b/mach/powerpc/as/mach4.c @@ -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_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_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_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); } @@ -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_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_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_SI u5 ',' GPR ',' e16 { emit4($1 | ($2<<21) | ($4<<16) | $6); } | OP_LEV u7 { emit4($1 | ($2<<5)); } @@ -237,4 +237,14 @@ lia $$ = target & 0x03FFFFFD; } ; - + +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); + } + ; diff --git a/mach/powerpc/libem/and.s b/mach/powerpc/libem/and.s new file mode 100644 index 000000000..4a1a81c04 --- /dev/null +++ b/mach/powerpc/libem/and.s @@ -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 diff --git a/mach/powerpc/libem/build.lua b/mach/powerpc/libem/build.lua index 318be381d..786be4e11 100644 --- a/mach/powerpc/libem/build.lua +++ b/mach/powerpc/libem/build.lua @@ -7,7 +7,7 @@ for _, plat in ipairs(vars.plats) do acklibrary { name = "lib_"..plat, srcs = { - "./*.s", + "./*.s", -- zer.s "./*.e", }, vars = { plat = plat }, diff --git a/mach/powerpc/libem/cms.s b/mach/powerpc/libem/cms.s new file mode 100644 index 000000000..53cb65691 --- /dev/null +++ b/mach/powerpc/libem/cms.s @@ -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 diff --git a/mach/powerpc/libem/com.s b/mach/powerpc/libem/com.s new file mode 100644 index 000000000..8b7082332 --- /dev/null +++ b/mach/powerpc/libem/com.s @@ -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 diff --git a/mach/powerpc/libem/inn.s b/mach/powerpc/libem/inn.s index f5ae4c63e..9770ac094 100644 --- a/mach/powerpc/libem/inn.s +++ b/mach/powerpc/libem/inn.s @@ -13,10 +13,10 @@ lwz r4, 4(sp) /* r4 = bit number */ addi r5, sp, 8 /* r5 = base address of bit set */ - srawi r6, r4, 3 /* r6 = byte address into set */ - andi. r7, r4, 7 /* r7 = bit within byte */ + rlwinm r6, r4, 29, 3, 29 /* r6 = byte index of word in set */ + 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 rlwinm r8, r8, 0, 31, 31 diff --git a/mach/powerpc/libem/ior.s b/mach/powerpc/libem/ior.s new file mode 100644 index 000000000..61e099934 --- /dev/null +++ b/mach/powerpc/libem/ior.s @@ -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 diff --git a/mach/powerpc/libem/set.s b/mach/powerpc/libem/set.s new file mode 100644 index 000000000..18ad877e8 --- /dev/null +++ b/mach/powerpc/libem/set.s @@ -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 diff --git a/mach/powerpc/libem/xor.s b/mach/powerpc/libem/xor.s new file mode 100644 index 000000000..9d4bc76b9 --- /dev/null +++ b/mach/powerpc/libem/xor.s @@ -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 diff --git a/mach/powerpc/libem/zer.s b/mach/powerpc/libem/zer.s new file mode 100644 index 000000000..ba978ba3e --- /dev/null +++ b/mach/powerpc/libem/zer.s @@ -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 diff --git a/mach/powerpc/ncg/table b/mach/powerpc/ncg/table index 77fdaedf1..adb0db2c8 100644 --- a/mach/powerpc/ncg/table +++ b/mach/powerpc/ncg/table @@ -10,7 +10,7 @@ INT64 = 8 FP_OFFSET = 0 /* Offset of saved FP 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) @@ -48,12 +48,12 @@ PROPERTIES FSREG /* any allocatable single-precision FPR */ SPR /* any SPR */ CR /* any CR */ - + GPR0 GPRSP GPRFP GPR3 GPR4 GPR5 GPR6 GPR7 GPR8 GPR9 GPR10 GPR11 GPR12 GPR13 GPR14 GPR15 GPR16 GPR17 GPR18 GPR19 GPR20 GPR21 GPR22 GPR23 GPR24 GPR25 GPR26 GPR27 GPR28 GPR29 GPR30 GPR31 - + CR0 CR1 FPR0 FPR1 FPR2 FPR3 FPR4 FPR5 FPR6 FPR7 @@ -64,7 +64,7 @@ PROPERTIES REGISTERS /* Reverse order to encourage ncg to allocate them from r31 down */ - + R31("r31") : GPR, REG, GPR31 regvar. R30("r30") : GPR, REG, GPR30 regvar. R29("r29") : GPR, REG, GPR29 regvar. @@ -195,17 +195,17 @@ TOKENS SUM_RIS = { GPR reg; INT offhi; } 4. SUM_RC = { GPR reg; INT off; } 4. SUM_RR = { GPR reg1; GPR reg2; } 4. - + TRISTATE_RC_S = { GPR reg; INT val; } 4. TRISTATE_RC_U = { GPR reg; INT val; } 4. TRISTATE_RR_S = { GPR reg1; GPR reg2; } 4. TRISTATE_RR_U = { GPR reg1; GPR reg2; } 4. - + TRISTATE_FF = { FPR reg1; FPR reg2; } 4. - + SEX_B = { GPR reg; } 4. SEX_H = { GPR reg; } 4. - + IND_RC_B = { GPR reg; INT off; } 4. IND_RR_B = { GPR reg1; GPR reg2; } 4. IND_RC_H = { GPR reg; INT off; } 4. @@ -216,9 +216,9 @@ TOKENS IND_RR_W = { GPR reg1; GPR reg2; } 4. IND_RC_D = { GPR reg; INT off; } 8. IND_RR_D = { GPR reg1; GPR reg2; } 8. - + NOT_R = { GPR reg; } 4. - + AND_RR = { GPR reg1; GPR reg2; } 4. OR_RR = { GPR reg1; GPR reg2; } 4. OR_RIS = { GPR reg; INT valhi; } 4. @@ -241,12 +241,12 @@ SETS CONST_8000 + CONST_8001_FFFF + CONST_HZ + CONST_HL. SUM_ALL = SUM_RC + SUM_RR. - + TRISTATE_ALL = TRISTATE_RC_S + TRISTATE_RC_U + TRISTATE_RR_S + TRISTATE_RR_U + TRISTATE_FF. - + SEX_ALL = SEX_B + SEX_H. - + LOGICAL_ALL = NOT_R + AND_RR + OR_RR + OR_RC + XOR_RR + XOR_RC. @@ -371,7 +371,7 @@ INSTRUCTIONS comment "!" LABEL:ro cost(0, 0). - + MOVES from GPR to GPR @@ -381,15 +381,15 @@ MOVES /* GPRE exists solely to allow us to use regvar() (which can only be used in an expression) as a register constant. */ - + from GPR to GPRE gen COMMENT("move GPR->GPRE") or %2.reg, %1, %1 - + /* Constants */ - from CONST_ALL smalls(%val) to GPR + from CONST_ALL + CONST smalls(%val) to GPR gen COMMENT("move CONST_ALL->GPR smalls") addi %2, R0, {CONST, %1.val} @@ -406,19 +406,19 @@ MOVES gen COMMENT("move LABEL->GPR") li32 %2, {LABEL, %1.adr} - + /* Sign extension */ from SEX_B to GPR gen COMMENT("move SEX_B->GPR") extsb %2, %1.reg - + from SEX_H to GPR gen COMMENT("move SEX_H->GPR") extsh %2, %1.reg - + /* Register + something */ from SUM_RIS to GPR @@ -575,38 +575,38 @@ MOVES from TRISTATE_RR_S to CR0 gen cmp %2, {CONST, 0}, %1.reg1, %1.reg2 - + from TRISTATE_RR_U to CR0 gen cmpl %2, {CONST, 0}, %1.reg1, %1.reg2 - + from TRISTATE_RC_S to CR0 gen COMMENT("move TRISTATE_RC_S->CR0 large") move {CONST, %1.val}, RSCRATCH cmp %2, {CONST, 0}, %1.reg, RSCRATCH - + from TRISTATE_RC_U smallu(%val) to CR0 gen COMMENT("move TRISTATE_RC_U->CR0 small") cmpli %2, {CONST, 0}, %1.reg, {CONST, %1.val} - + from TRISTATE_RC_U to CR0 gen COMMENT("move TRISTATE_RC_U->CR0") move {CONST, %1.val}, RSCRATCH cmpl %2, {CONST, 0}, %1.reg, RSCRATCH - + from TRISTATE_FF to CR0 gen COMMENT("move TRISTATE_FF->CR0") fcmpo %2, %1.reg1, %1.reg2 - + from GPR to CR0 gen COMMENT("move GPR->CR0") orX RSCRATCH, %1, %1 /* alas, can't call test */ - + from TRISTATE_RR_S + TRISTATE_RC_S + TRISTATE_FF to GPR gen COMMENT("move TRISTATE_R*_S->GPR") @@ -671,9 +671,9 @@ MOVES gen move %1, %2.reg - + TESTS - + to test GPR gen orX RSCRATCH, %1, %1 @@ -709,36 +709,36 @@ STACKINGRULES COMMENT("stack SEX_B") extsb RSCRATCH, %1.reg stwu RSCRATCH, {GPRINDIRECT, SP, 0-4} - + from SEX_H to STACK gen COMMENT("stack SEX_H") extsh RSCRATCH, %1.reg stwu RSCRATCH, {GPRINDIRECT, SP, 0-4} - + from SUM_ALL + TRISTATE_ALL + LOGICAL_ALL to STACK gen COMMENT("stack SUM_ALL + TRISTATE_ALL + LOGICAL_ALL") move %1, RSCRATCH stwu RSCRATCH, {GPRINDIRECT, SP, 0-4} - + from IND_ALL_BHW to STACK gen COMMENT("stack IND_ALL_BHW") move %1, RSCRATCH stwu RSCRATCH, {GPRINDIRECT, SP, 0-4} - + from IND_ALL_D to STACK gen COMMENT("stack IND_ALL_D") move %1, FSCRATCH stfdu FSCRATCH, {GPRINDIRECT, SP, 0-8} - + from FREG to STACK gen COMMENT("stack FPR") stfdu %1, {GPRINDIRECT, SP, 0-8} - + from FSREG to STACK gen COMMENT("stack FSREG") @@ -761,14 +761,14 @@ COERCIONS COMMENT("coerce CONST_ALL->REG") move %1, %a yields %a - + from LABEL uses REG gen COMMENT("coerce LABEL->REG") move %1, %a yields %a - + from STACK uses REG gen @@ -792,32 +792,32 @@ COERCIONS COMMENT("coerce SEX_B->REG") extsb %a, %1.reg yields %a - + from SEX_H uses REG gen COMMENT("coerce SEX_H->REG") extsh %a, %1.reg yields %a - + from SUM_ALL + TRISTATE_ALL + LOGICAL_ALL uses REG gen move %1, %a yields %a - + from FSREG uses FSREG gen fmr %a, %1 yields %a - + from FREG uses FREG gen fmr %a, %1 yields %a - + from STACK uses FREG gen @@ -884,17 +884,17 @@ PATTERNS yields %1 %1 with FSREG yields %1 %1 - + pat dup $1==INT64 /* Duplicate double-word on top of stack */ with REG REG yields %2 %1 %2 %1 with FREG yields %1 %1 - + pat exg $1==INT32 /* Exchange top two words on stack */ with REG REG yields %1 %2 - + pat stl lol $1==$2 /* Store then load local */ leaving dup 4 @@ -910,13 +910,13 @@ PATTERNS dup INT32 lal $1 sti $2 - + pat ste loe $1==$2 /* Store then load external */ leaving dup 4 ste $1 - - + + /* Type conversions */ pat loc loc cii loc loc cii $1==$4 && $2==$5 /* madness, generated by the C compiler */ @@ -924,19 +924,19 @@ PATTERNS loc $1 loc $2 cii - + pat loc loc cii loc loc cii $2==INT32 && $5==INT32 && $4<$2 /* madness, generated by the C compiler */ leaving loc $4 loc $5 cii - + pat loc loc ciu /* signed X -> unsigned X */ leaving loc $1 loc $2 cuu - + pat loc loc cuu $1==$2 /* unsigned X -> unsigned X */ /* nop */ @@ -945,25 +945,25 @@ PATTERNS pat loc loc cui $1==$2 /* unsigned X -> signed X */ /* nop */ - + pat loc loc cui $1==INT8 && $2==INT32 /* unsigned char -> signed int */ /* nop */ - + pat loc loc cui $1==INT16 && $2==INT32 /* unsigned short -> signed int */ /* nop */ - + pat loc loc cii $1==INT8 && $2==INT32 /* signed char -> signed int */ with GPR yields {SEX_B, %1} - + pat loc loc cii $1==2 && $2==4 /* signed char -> signed short */ with GPR yields {SEX_H, %1} - - - - + + + + /* Local variables */ pat lal smalls($1) /* Load address of local */ @@ -975,7 +975,7 @@ PATTERNS pat lol inreg($1)>0 /* Load from local */ yields {LOCAL, $1} - + pat lol /* Load from local */ leaving lal $1 @@ -985,34 +985,34 @@ PATTERNS leaving lal $1 loi INT32*2 - + pat stl inreg($1)>0 /* Store to local */ with CONST_ALL + LABEL + GPR + OP_ALL_W kills regvar($1), LOCAL %off==$1 gen move %1, {GPRE, regvar($1)} - + pat stl /* Store to local */ leaving lal $1 sti INT32 - + pat sdl /* Store double-word to local */ leaving lal $1 sti INT32*2 - + pat lil inreg($1)>0 /* Load from indirected local */ uses REG gen lwz %a, {GPRINDIRECT, regvar($1), 0} yields %a - + pat lil /* Load from indirected local */ leaving lol $1 loi INT32 - + pat sil /* Save to indirected local */ leaving lol $1 @@ -1022,14 +1022,14 @@ PATTERNS leaving loc 0 stl $1 - + pat inl /* Increment local */ leaving lol $1 loc 1 adi 4 stl $1 - + pat del /* Decrement local */ leaving lol $1 @@ -1039,14 +1039,14 @@ PATTERNS /* Global variables */ - + pat lpi /* Load address of external function */ leaving lae $1 - + pat lae /* Load address of external */ yields {LABEL, $1} - + pat loe /* Load word external */ leaving lae $1 @@ -1056,36 +1056,36 @@ PATTERNS leaving lae $1 sti INT32 - + pat lde /* Load double-word external */ leaving lae $1 loi INT64 - + pat sde /* Store double-word external */ leaving lae $1 sti INT64 - + pat zre /* Zero external */ leaving loc 0 ste $1 - + pat ine /* Increment external */ uses REG={LABEL, $1}, REG gen lwz %b, {GPRINDIRECT, %a, 0} addi %b, %b, {CONST, 1} stw %b, {GPRINDIRECT, %a, 0} - + pat dee /* Decrement external */ uses REG={LABEL, $1}, REG gen lwz %b, {GPRINDIRECT, %a, 0} addi %b, %b, {CONST, 0-1} stw %b, {GPRINDIRECT, %a, 0} - + /* Structures */ @@ -1094,22 +1094,22 @@ PATTERNS leaving adp $1 loi INT32 - + pat ldf /* Load double-word offsetted */ leaving adp $1 loi INT64 - + pat stf /* Store word offsetted */ leaving adp $1 sti INT32 - + pat sdf /* Store double-word offsetted */ leaving adp $1 sti INT64 - + /* Loads and stores */ @@ -1159,8 +1159,8 @@ PATTERNS leaving loc $1 los INT32 - - pat los /* Load arbitrary size */ + + pat los $1==INT32 /* Load arbitrary size */ with GPR3 GPR4 STACK kills ALL gen @@ -1283,22 +1283,22 @@ PATTERNS loc $1 sts INT32 - pat sts /* Store arbitrary size */ + pat sts $1==INT32 /* Store arbitrary size */ with GPR3 GPR4 STACK kills ALL gen bl {LABEL, ".sts"} - + /* Arithmetic wrappers */ pat ads $1==4 /* Add var to pointer */ leaving adi $1 - + pat sbs $1==4 /* Subtract var from pointer */ leaving sbi $1 - + pat adp /* Add constant to pointer */ leaving loc $1 @@ -1307,41 +1307,41 @@ PATTERNS pat adu /* Add unsigned */ leaving adi $1 - + pat sbu /* Subtract unsigned */ leaving sbi $1 - + pat inc /* Add 1 */ leaving loc 1 adi 4 - + pat dec /* Subtract 1 */ leaving loc 1 sbi 4 - + pat loc mlu $2==2 /* Unsigned multiply by constant */ leaving loc $1 mli 4 - + pat mlu /* Unsigned multiply by var */ leaving mli $1 - + pat loc slu /* Shift left unsigned by constant amount */ leaving loc $1 sli $2 - + pat slu /* Shift left unsigned by variable amount */ leaving sli $1 - - + + /* Word arithmetic */ pat adi $1==4 /* Add word (second + top) */ @@ -1389,21 +1389,21 @@ PATTERNS gen neg %a, %1 yields %a - + pat mli $1==4 /* Multiply word (second * top) */ with REG REG uses reusing %2, REG gen mullw %a, %2, %1 yields %a - + pat dvi $1==4 /* Divide word (second / top) */ with REG REG uses reusing %2, REG gen divw %a, %2, %1 yields %a - + pat dvu $1==4 /* Divide unsigned word (second / top) */ with REG REG uses reusing %2, REG @@ -1419,7 +1419,7 @@ PATTERNS mullw %a, %a, %1 subf %a, %a, %2 yields %a - + pat rmu $1==4 /* Remainder unsigned word (second % top) */ with REG REG uses REG @@ -1463,9 +1463,11 @@ PATTERNS andisX %a, %2, {CONST, hi(%1.val)} yields %a - pat and !defined($1) /* AND set */ + pat and defined($1) /* AND set */ with STACK + kills ALL gen + move {CONST, $1}, R3 bl {LABEL, ".and"} pat ior $1==4 /* OR word */ @@ -1498,8 +1500,17 @@ PATTERNS uses reusing %2, REG={OR_RIS, %2, hi(%1.val)} yields {OR_RC, %2, lo(%1.val)} - pat ior !defined($1) /* OR set */ + pat ior defined($1) /* OR set */ 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 bl {LABEL, ".ior"} @@ -1523,11 +1534,13 @@ PATTERNS uses reusing %2, REG={XOR_RIS, %2, hi(%1.val)} yields {XOR_RC, %2, lo(%1.val)} - pat xor !defined($1) /* XOR set */ + pat xor defined($1) /* XOR set */ with STACK + kills ALL gen + move {CONST, $1}, R3 bl {LABEL, ".xor"} - + pat com $1==INT32 /* NOT word */ with AND_RR uses REG @@ -1546,12 +1559,24 @@ PATTERNS yields %a with GPR yields {NOT_R, %1} - - pat com !defined($1) /* NOT set */ + + pat com defined($1) /* NOT set */ with STACK gen + move {CONST, $1}, R3 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) */ with CONST_ALL GPR uses reusing %2, REG @@ -1563,7 +1588,7 @@ PATTERNS gen slw %a, %2, %1 yields %a - + pat sri $1==4 /* Shift right signed (second >> top) */ with CONST_ALL GPR uses reusing %2, REG @@ -1587,29 +1612,30 @@ PATTERNS gen srw %a, %2, %1 yields %a - + /* Arrays */ pat aar $1==INT32 /* Index array */ with GPR3 GPR4 GPR5 + kills ALL gen bl {LABEL, ".aar4"} yields R3 - + pat lae lar $2==INT32 && nicesize(rom($1, 3)) /* Load array */ leaving lae $1 aar INT32 loi rom($1, 3) - + pat lar $1==INT32 /* Load array */ with GPR3 GPR4 GPR5 STACK kills ALL gen bl {LABEL, ".lar4"} - + pat lae sar $2==INT32 && nicesize(rom($1, 3)) /* Store array */ leaving lae $1 @@ -1621,24 +1647,26 @@ PATTERNS kills ALL gen bl {LABEL, ".sar4"} - - + /* Sets */ - pat set defined($1) /* Create word with set bit */ - leaving - loc 1 - exg INT32 - sli INT32 - - pat set !defined($1) /* Create structure with set bit (variable) */ + pat set defined($1) /* Create singleton set */ + with GPR4 STACK + kills ALL + gen + move {CONST, $1}, R3 + bl {LABEL, ".set"} + + /* Create set (variable), used in lang/m2/libm2/LtoUset.e */ + pat set !defined($1) with GPR3 GPR4 STACK + kills ALL gen bl {LABEL, ".set"} - - pat inn /* Test for set bit */ + + pat inn defined($1) /* Test for set bit */ with STACK kills ALL uses REG @@ -1646,9 +1674,8 @@ PATTERNS li32 %a, {CONST, $1} stwu %a, {GPRINDIRECT, SP, 0-4} bl {LABEL, ".inn"} - - - + + /* Boolean resolutions */ pat teq /* top = (top == 0) */ @@ -1660,7 +1687,7 @@ PATTERNS move {LABEL, ".teq_table"}, %a lwzx %a, %a, RSCRATCH yields %a - + pat tne /* top = (top != 0) */ with TRISTATE_ALL + GPR uses reusing %1, REG @@ -1670,7 +1697,7 @@ PATTERNS move {LABEL, ".tne_table"}, %a lwzx %a, %a, RSCRATCH yields %a - + pat tlt /* top = (top < 0) */ with TRISTATE_ALL + GPR uses reusing %1, REG @@ -1680,7 +1707,7 @@ PATTERNS move {LABEL, ".tlt_table"}, %a lwzx %a, %a, RSCRATCH yields %a - + pat tle /* top = (top <= 0) */ with TRISTATE_ALL + GPR uses reusing %1, REG @@ -1690,7 +1717,7 @@ PATTERNS move {LABEL, ".tle_table"}, %a lwzx %a, %a, RSCRATCH yields %a - + pat tgt /* top = (top > 0) */ with TRISTATE_ALL + GPR uses reusing %1, REG @@ -1710,7 +1737,7 @@ PATTERNS move {LABEL, ".tge_table"}, %a lwzx %a, %a, RSCRATCH yields %a - + @@ -1726,7 +1753,7 @@ PATTERNS leaving cmi INT32 zeq $1 - + pat zne /* Branch if signed top != 0 */ with TRISTATE_ALL+GPR STACK gen @@ -1737,7 +1764,7 @@ PATTERNS leaving cmi INT32 zne $1 - + pat zgt /* Branch if signed top > 0 */ with TRISTATE_ALL+GPR STACK gen @@ -1748,7 +1775,7 @@ PATTERNS leaving cmi INT32 zgt $1 - + pat zge /* Branch if signed top >= 0 */ with TRISTATE_ALL+GPR STACK gen @@ -1759,7 +1786,7 @@ PATTERNS leaving cmi INT32 zge $1 - + pat zlt /* Branch if signed top < 0 */ with TRISTATE_ALL+GPR STACK gen @@ -1770,7 +1797,7 @@ PATTERNS leaving cmi INT32 zlt $1 - + pat zle /* Branch if signed top >= 0 */ with TRISTATE_ALL+GPR STACK gen @@ -1781,32 +1808,39 @@ PATTERNS leaving cmi INT32 zle $1 - + /* Compare and jump */ - pat cmi /* Signed tristate compare */ + pat cmi $1==INT32 /* Signed tristate compare */ with CONST_ALL GPR yields {TRISTATE_RC_S, %2, %1.val} with GPR GPR yields {TRISTATE_RR_S, %2, %1} - - pat cmu /* Unsigned tristate compare */ + + pat cmu $1==INT32 /* Unsigned tristate compare */ with CONST_ALL GPR yields {TRISTATE_RC_U, %2, %1.val} with GPR GPR yields {TRISTATE_RR_U, %2, %1} - + pat cmp /* Compare pointers */ leaving cmu INT32 - + pat cms $1==INT32 /* Compare blocks (word sized) */ leaving cmi INT32 - - - + + pat cms defined($1) + with STACK + kills ALL + gen + move {CONST, $1}, R3 + bl {LABEL, ".cms"} + yields R3 + + /* Other branching and labelling */ @@ -1814,31 +1848,31 @@ PATTERNS gen labeldef $1 yields R3 - + pat lab topeltsize($1)==4 && fallthrough($1) with GPR3 gen labeldef $1 yields %1 - + pat lab topeltsize($1)!=4 with STACK kills ALL gen labeldef $1 - + pat bra topeltsize($1)==4 /* Unconditional jump with TOS GPRister */ with GPR3 STACK gen b {LABEL, $1} - + pat bra topeltsize($1)!=4 /* Unconditional jump without TOS GPRister */ with STACK gen b {LABEL, $1} - - - + + + /* Miscellaneous */ pat cal /* Call procedure */ @@ -1853,18 +1887,18 @@ PATTERNS gen mtspr CTR, %1 bcctrl ALWAYS, {CONST, 0}, {CONST, 0} - + pat lfr $1==INT32 /* Load function result, word */ yields R3 - + pat lfr $1==INT64 /* Load function result, double-word */ yields R4 R3 - + pat ret $1==0 /* Return from procedure */ gen return b {LABEL, ".ret"} - + pat ret $1==INT32 /* Return from procedure, word */ with GPR3 gen @@ -1887,7 +1921,7 @@ PATTERNS stwu %1, {GPRINDIRECT, SP, 0-4} bl {LABEL, "_memmove"} addi SP, SP, {CONST, 12} - + pat bls /* Block move variable length */ with GPR GPR GPR STACK gen @@ -1896,18 +1930,18 @@ PATTERNS stwu %2, {GPRINDIRECT, SP, 0-4} bl {LABEL, "_memmove"} addi SP, SP, {CONST, 12} - + pat csa /* Array-lookup switch */ with STACK gen b {LABEL, ".csa"} - + pat csb /* Table-lookup switch */ with STACK gen b {LABEL, ".csb"} - + /* EM specials */ @@ -1915,7 +1949,7 @@ PATTERNS leaving lae $1 ste "hol0+4" - + pat lin /* Set current line number */ leaving loc $1 @@ -1924,37 +1958,37 @@ PATTERNS pat lni /* Increment line number */ leaving ine "hol0" - + pat lim /* Load EM trap ignore mask */ leaving lde ".ignmask" - + pat sim /* Store EM trap ignore mask */ leaving ste ".ignmask" - + pat trp /* Raise EM trap */ with GPR3 gen bl {LABEL, ".trap"} - + pat sig /* Set trap handler */ leaving ste ".trppc" - + pat rtt /* Return from trap */ leaving ret 0 - + pat lxl $1==0 /* Load FP */ leaving lor 0 - + pat lxl $1==1 /* Load caller's FP */ leaving lxl 0 dch - + pat dch /* FP -> caller FP */ with GPR uses reusing %1, REG @@ -1965,12 +1999,12 @@ PATTERNS pat lpb /* Convert FP to argument address */ leaving adp EM_BSIZE - + pat lxa /* Load caller's SP */ leaving lxl $1 lpb - + pat gto /* longjmp */ uses REG gen @@ -1986,39 +2020,31 @@ PATTERNS gen move FP, %a yields %a - + pat lor $1==1 /* Load SP */ uses REG gen move SP, %a yields %a - - pat lor $1==2 /* Load HP */ - leaving - loe ".reghp" - + pat str $1==0 /* Store FP */ with GPR gen move %1, FP - + pat str $1==1 /* Store SP */ with GPR gen move %1, SP - - pat str $1==2 /* Store HP */ - leaving - ste ".reghp" - pat loc ass $1==4 /* Drop 4 bytes from stack */ + pat loc ass $1==4 && $2==4 /* Drop 4 bytes from stack */ with exact GPR /* nop */ with STACK gen addi SP, SP, {CONST, 4} - pat ass /* Adjust stack by variable amount */ + pat ass $1==4 /* Adjust stack by variable amount */ with CONST2 STACK gen move {SUM_RC, SP, %1.val}, SP @@ -2032,39 +2058,39 @@ PATTERNS with GPR STACK gen move {SUM_RR, SP, %1}, SP - + pat asp /* Adjust stack by constant amount */ leaving loc $1 - ass - - - + ass 4 + + + /* Floating point support */ /* All very cheap and nasty --- this needs to be properly integrated into * the code generator. ncg doesn't like having separate FPU registers. */ /* Single-precision */ - + pat zrf $1==INT32 /* Push zero */ leaving loe ".fs_00000000" - + pat adf $1==INT32 /* Add single */ with FSREG FSREG uses reusing %1, FSREG gen fadds %a, %2, %1 yields %a - + pat sbf $1==INT32 /* Subtract single */ with FSREG FSREG uses reusing %1, FSREG gen fsubs %a, %2, %1 yields %a - + pat mlf $1==INT32 /* Multiply single */ with FSREG FSREG uses reusing %1, FSREG @@ -2089,56 +2115,56 @@ PATTERNS pat cmf $1==INT32 /* Compare single */ with FSREG FSREG yields {TRISTATE_FF, %2.1, %1.1} - + pat loc loc cff $1==INT32 && $2==INT64 /* Convert single to double */ with FSREG yields %1.1 - + pat loc loc cfu $1==INT32 && $2==INT32 /* Convert single to unsigned int */ with STACK gen bl {LABEL, ".cfu4"} - + pat loc loc cfi $1==INT32 && $2==INT32 /* Convert single to signed int */ with STACK gen bl {LABEL, ".cfi4"} - + pat loc loc cif $1==INT32 && $2==INT32 /* Convert integer to single */ with STACK gen bl {LABEL, ".cif4"} - + pat loc loc cuf $1==INT32 && $2==INT32 /* Convert unsigned int to single */ with STACK gen bl {LABEL, ".cuf4"} - + pat fef $1==INT32 /* Split single */ with STACK gen bl {LABEL, ".fef4"} - + /* Double-precision */ - + pat zrf $1==INT64 /* Push zero */ leaving lde ".fd_00000000" - + pat adf $1==INT64 /* Add double */ with FREG FREG uses FREG gen fadd %a, %2, %1 yields %a - + pat sbf $1==INT64 /* Subtract double */ with FREG FREG uses FREG gen fsub %a, %2, %1 yields %a - + pat mlf $1==INT64 /* Multiply double */ with FREG FREG uses reusing %1, FREG @@ -2163,30 +2189,30 @@ PATTERNS pat cmf $1==INT64 /* Compare double */ with FREG FREG yields {TRISTATE_FF, %2, %1} - + pat loc loc cff $1==INT64 && $2==INT32 /* Convert double to single */ with FREG uses reusing %1, FSREG gen frsp %a, %1 yields %a - + pat loc loc cfu $1==INT64 && $2==INT32 /* Convert double to unsigned int */ with STACK gen bl {LABEL, ".cfu8"} - + pat loc loc cfi $1==INT64 && $2==INT32 /* Convert double to signed int */ with STACK gen bl {LABEL, ".cfi8"} - + pat loc loc cif $1==INT32 && $2==INT64 /* Convert integer to double */ with STACK kills ALL gen bl {LABEL, ".cif8"} - + pat loc loc cuf $1==INT32 && $2==INT64 /* Convert unsigned int to double */ with STACK gen diff --git a/modules/src/object/build.lua b/modules/src/object/build.lua index c30c7e54a..6a8bea04e 100644 --- a/modules/src/object/build.lua +++ b/modules/src/object/build.lua @@ -1,6 +1,7 @@ clibrary { name = "lib", srcs = { "./*.c" }, + hdrs = { "./object.h" }, deps = { "modules+headers", "h+local", diff --git a/modules/src/object/object.h b/modules/src/object/object.h index 68a5ea99f..d6c218a2a 100644 --- a/modules/src/object/object.h +++ b/modules/src/object/object.h @@ -4,42 +4,46 @@ * See the copyright notice in the ACK home directory, in the file "Copyright". */ -#include - #ifndef __OBJECT_INCLUDED__ #define __OBJECT_INCLUDED__ -_PROTOTYPE(int wr_open, (char *f)); -_PROTOTYPE(void wr_close, (void)); -_PROTOTYPE(void wr_ohead, (struct outhead *h)); -_PROTOTYPE(void wr_sect, (struct outsect *s, unsigned int c)); -_PROTOTYPE(void wr_outsect, (int sectno)); -_PROTOTYPE(void wr_emit, (char *b, long c)); -_PROTOTYPE(void wr_putc, (int c)); -_PROTOTYPE(void wr_relo, (struct outrelo *r, unsigned int c)); -_PROTOTYPE(void wr_name, (struct outname *n, unsigned int c)); -_PROTOTYPE(void wr_string, (char *s, long c)); -_PROTOTYPE(void wr_arhdr, (int fd, struct ar_hdr *a)); -_PROTOTYPE(void wr_ranlib, (int fd, struct ranlib *r, long cnt)); -_PROTOTYPE(void wr_int2, (int fd, int i)); -_PROTOTYPE(void wr_long, (int fd, long l)); -_PROTOTYPE(void wr_bytes, (int fd, char *buf, long l)); -_PROTOTYPE(int rd_open, (char *f)); -_PROTOTYPE(int rd_fdopen, (int f)); -_PROTOTYPE(void rd_close, (void)); -_PROTOTYPE(void rd_ohead, (struct outhead *h)); -_PROTOTYPE(void rd_sect, (struct outsect *s, unsigned int c)); -_PROTOTYPE(void rd_outsect, (int sectno)); -_PROTOTYPE(void rd_emit, (char *b, long c)); -_PROTOTYPE(void rd_relo, (struct outrelo *r, unsigned int c)); -_PROTOTYPE(void rd_rew_relo, (struct outhead *head)); -_PROTOTYPE(void rd_name, (struct outname *n, unsigned int c)); -_PROTOTYPE(void rd_string, (char *s, long c)); -_PROTOTYPE(int rd_arhdr, (int fd, struct ar_hdr *a)); -_PROTOTYPE(void rd_ranlib, (int fd, struct ranlib *r, long cnt)); -_PROTOTYPE(int rd_int2, (int fd)); -_PROTOTYPE(long rd_long, (int fd)); -_PROTOTYPE(void rd_bytes, (int fd, char *buf, long l)); -_PROTOTYPE(int rd_fd, (void)); +struct ar_hdr; +struct outhead; +struct outrelo; +struct outsect; +struct ranlib; + +int wr_open(char *f); +void wr_close(void); +void wr_ohead(struct outhead *h); +void wr_sect(struct outsect *s, unsigned int c); +void wr_outsect(int sectno); +void wr_emit(char *b, long c); +void wr_putc(int c); +void wr_relo(struct outrelo *r, unsigned int c); +void wr_name(struct outname *n, unsigned int c); +void wr_string(char *s, long c); +void wr_arhdr(int fd, struct ar_hdr *a); +void wr_ranlib(int fd, struct ranlib *r, long cnt); +void wr_int2(int fd, int i); +void wr_long(int fd, long l); +void wr_bytes(int fd, char *buf, long l); +int rd_open(char *f); +int rd_fdopen(int f); +void rd_close(void); +void rd_ohead(struct outhead *h); +void rd_sect(struct outsect *s, unsigned int c); +void rd_outsect(int sectno); +void rd_emit(char *b, long c); +void rd_relo(struct outrelo *r, unsigned int c); +void rd_rew_relo(struct outhead *head); +void rd_name(struct outname *n, unsigned int c); +void rd_string(char *s, long c); +int rd_arhdr(int fd, struct ar_hdr *a); +void rd_ranlib(int fd, struct ranlib *r, long cnt); +int rd_int2(int fd); +long rd_long(int fd); +void rd_bytes(int fd, char *buf, long l); +int rd_fd(void); #endif /* __OBJECT_INCLUDED__ */ diff --git a/plat/osx/cvmach/build.lua b/plat/osx/cvmach/build.lua new file mode 100644 index 000000000..8076546c1 --- /dev/null +++ b/plat/osx/cvmach/build.lua @@ -0,0 +1,15 @@ +cprogram { + name = "cvmach", + srcs = { "./cvmach.c" }, + deps = { + "h+emheaders", + "modules/src/object+lib", + } +} + +installable { + name = "pkg", + map = { + ["$(PLATDEP)/cvmach"] = "+cvmach", + } +} diff --git a/plat/osx/cvmach/cvmach.6 b/plat/osx/cvmach/cvmach.6 new file mode 100644 index 000000000..30d4aa2d7 --- /dev/null +++ b/plat/osx/cvmach/cvmach.6 @@ -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. diff --git a/plat/osx/cvmach/cvmach.c b/plat/osx/cvmach/cvmach.c new file mode 100644 index 000000000..5f8315ddd --- /dev/null +++ b/plat/osx/cvmach/cvmach.c @@ -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 +#include +#include +#include +#include +#include + +#include +#include + +/* 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 \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; +} diff --git a/plat/osx/include/ack/config.h b/plat/osx/include/ack/config.h new file mode 100644 index 000000000..9f58a3941 --- /dev/null +++ b/plat/osx/include/ack/config.h @@ -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 diff --git a/plat/osx/include/build.lua b/plat/osx/include/build.lua new file mode 100644 index 000000000..ff7c87a4d --- /dev/null +++ b/plat/osx/include/build.lua @@ -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 +} diff --git a/plat/osx/include/sys/dirent.h b/plat/osx/include/sys/dirent.h new file mode 100644 index 000000000..073c84588 --- /dev/null +++ b/plat/osx/include/sys/dirent.h @@ -0,0 +1,17 @@ +#ifndef _SYS_DIRENT_H +#define _SYS_DIRENT_H + +#include + +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 diff --git a/plat/osx/include/sys/mman.h b/plat/osx/include/sys/mman.h new file mode 100644 index 000000000..5a844c4b6 --- /dev/null +++ b/plat/osx/include/sys/mman.h @@ -0,0 +1,20 @@ +#ifndef _SYS_MMAN_H +#define _SYS_MMAN_H + +#include + +#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 diff --git a/plat/osx/include/sys/stat.h b/plat/osx/include/sys/stat.h new file mode 100644 index 000000000..6cb24902f --- /dev/null +++ b/plat/osx/include/sys/stat.h @@ -0,0 +1,49 @@ +#ifndef _SYS_STAT_H +#define _SYS_STAT_H + +#include +#include /* 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 diff --git a/plat/osx/include/sys/types.h b/plat/osx/include/sys/types.h new file mode 100644 index 000000000..b4561b7b3 --- /dev/null +++ b/plat/osx/include/sys/types.h @@ -0,0 +1,17 @@ +#ifndef _SYS_TYPES_H +#define _SYS_TYPES_H + +#include /* 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 diff --git a/plat/osx/include/unistd.h b/plat/osx/include/unistd.h new file mode 100644 index 000000000..bafa2a6c4 --- /dev/null +++ b/plat/osx/include/unistd.h @@ -0,0 +1,137 @@ +#ifndef _UNISTD_H +#define _UNISTD_H + +#include + +/* + * 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 */ +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 diff --git a/plat/osx/libsys/brk.c b/plat/osx/libsys/brk.c new file mode 100644 index 000000000..9b58ca1d8 --- /dev/null +++ b/plat/osx/libsys/brk.c @@ -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 +#include +#include + +/* + * 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; +} diff --git a/plat/osx/libsys/creat.c b/plat/osx/libsys/creat.c new file mode 100644 index 000000000..3a350e357 --- /dev/null +++ b/plat/osx/libsys/creat.c @@ -0,0 +1,6 @@ +#include + +int creat(const char *path, mode_t mode) +{ + return open(path, O_CREAT | O_TRUNC | O_WRONLY, mode); +} diff --git a/plat/osx/libsys/isatty.c b/plat/osx/libsys/isatty.c new file mode 100644 index 000000000..1da6509df --- /dev/null +++ b/plat/osx/libsys/isatty.c @@ -0,0 +1,7 @@ +#include + +int isatty(int fd) +{ + int line_disc; + return 0 <= ioctl(fd, TIOCGETD, &line_disc); +} diff --git a/plat/osx/libsys/signal.c b/plat/osx/libsys/signal.c new file mode 100644 index 000000000..0c1e12624 --- /dev/null +++ b/plat/osx/libsys/signal.c @@ -0,0 +1,16 @@ +#include + +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; +} diff --git a/plat/osx386/README b/plat/osx386/README new file mode 100644 index 000000000..8c34134ff --- /dev/null +++ b/plat/osx386/README @@ -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 +2016-12-03 diff --git a/plat/osx386/boot.s b/plat/osx386/boot.s new file mode 100644 index 000000000..932a716e9 --- /dev/null +++ b/plat/osx386/boot.s @@ -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 diff --git a/plat/osx386/build-pkg.lua b/plat/osx386/build-pkg.lua new file mode 100644 index 000000000..6dfe1561c --- /dev/null +++ b/plat/osx386/build-pkg.lua @@ -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", + } +} diff --git a/plat/osx386/build-tools.lua b/plat/osx386/build-tools.lua new file mode 100644 index 000000000..a1a9a8e2b --- /dev/null +++ b/plat/osx386/build-tools.lua @@ -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", + } +} diff --git a/plat/osx386/descr b/plat/osx386/descr new file mode 100644 index 000000000..a6f021878 --- /dev/null +++ b/plat/osx386/descr @@ -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 diff --git a/plat/osx386/include/build.lua b/plat/osx386/include/build.lua new file mode 100644 index 000000000..c2a1050c2 --- /dev/null +++ b/plat/osx386/include/build.lua @@ -0,0 +1,4 @@ +installable { + name = "pkg", + map = { "plat/osx/include+pkg" } +} diff --git a/plat/osx386/libsys/_exit.s b/plat/osx386/libsys/_exit.s new file mode 100644 index 000000000..80d30134b --- /dev/null +++ b/plat/osx386/libsys/_exit.s @@ -0,0 +1,5 @@ +.sect .text +.define __exit +__exit: + mov eax, 1 + int 0x80 diff --git a/plat/osx386/libsys/build.lua b/plat/osx386/libsys/build.lua new file mode 100644 index 000000000..23e491f7a --- /dev/null +++ b/plat/osx386/libsys/build.lua @@ -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" + } +} diff --git a/plat/osx386/libsys/close.s b/plat/osx386/libsys/close.s new file mode 100644 index 000000000..295b90ad1 --- /dev/null +++ b/plat/osx386/libsys/close.s @@ -0,0 +1,7 @@ +.sect .text +.define _close +_close: + mov eax, 6 + int 0x80 + jb .set_errno + ret diff --git a/plat/osx386/libsys/fstat.s b/plat/osx386/libsys/fstat.s new file mode 100644 index 000000000..1b49ae0b7 --- /dev/null +++ b/plat/osx386/libsys/fstat.s @@ -0,0 +1,7 @@ +.sect .text +.define _fstat +_fstat: + mov eax, 189 + int 0x80 + jb .set_errno + ret diff --git a/plat/osx386/libsys/getdirentries.s b/plat/osx386/libsys/getdirentries.s new file mode 100644 index 000000000..9c59c627c --- /dev/null +++ b/plat/osx386/libsys/getdirentries.s @@ -0,0 +1,7 @@ +.sect .text +.define _getdirentries +_getdirentries: + mov eax, 196 + int 0x80 + jb .set_errno + ret diff --git a/plat/osx386/libsys/getpid.s b/plat/osx386/libsys/getpid.s new file mode 100644 index 000000000..150791bb4 --- /dev/null +++ b/plat/osx386/libsys/getpid.s @@ -0,0 +1,7 @@ +.sect .text +.define _getpid +_getpid: + mov eax, 20 + int 0x80 + jb .set_errno + ret diff --git a/plat/osx386/libsys/gettimeofday.s b/plat/osx386/libsys/gettimeofday.s new file mode 100644 index 000000000..43aff5fa9 --- /dev/null +++ b/plat/osx386/libsys/gettimeofday.s @@ -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 diff --git a/plat/osx386/libsys/ioctl.s b/plat/osx386/libsys/ioctl.s new file mode 100644 index 000000000..5b13e2cfb --- /dev/null +++ b/plat/osx386/libsys/ioctl.s @@ -0,0 +1,7 @@ +.sect .text +.define _ioctl +_ioctl: + mov eax, 54 + int 0x80 + jb .set_errno + ret diff --git a/plat/osx386/libsys/kill.s b/plat/osx386/libsys/kill.s new file mode 100644 index 000000000..8fdce95fd --- /dev/null +++ b/plat/osx386/libsys/kill.s @@ -0,0 +1,7 @@ +.sect .text +.define _kill +_kill: + mov eax, 37 + int 0x80 + jb .set_errno + ret diff --git a/plat/osx386/libsys/lseek.s b/plat/osx386/libsys/lseek.s new file mode 100644 index 000000000..22543b2eb --- /dev/null +++ b/plat/osx386/libsys/lseek.s @@ -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 diff --git a/plat/osx386/libsys/lstat.s b/plat/osx386/libsys/lstat.s new file mode 100644 index 000000000..a492cc3cb --- /dev/null +++ b/plat/osx386/libsys/lstat.s @@ -0,0 +1,7 @@ +.sect .text +.define _lstat +_lstat: + mov eax, 190 + int 0x80 + jb .set_errno + ret diff --git a/plat/osx386/libsys/mmap.s b/plat/osx386/libsys/mmap.s new file mode 100644 index 000000000..e39ea777a --- /dev/null +++ b/plat/osx386/libsys/mmap.s @@ -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 diff --git a/plat/osx386/libsys/mprotect.s b/plat/osx386/libsys/mprotect.s new file mode 100644 index 000000000..641173a16 --- /dev/null +++ b/plat/osx386/libsys/mprotect.s @@ -0,0 +1,7 @@ +.sect .text +.define _mprotect +_mprotect: + mov eax, 74 + int 0x80 + jb .set_errno + ret diff --git a/plat/osx386/libsys/open.s b/plat/osx386/libsys/open.s new file mode 100644 index 000000000..154c00506 --- /dev/null +++ b/plat/osx386/libsys/open.s @@ -0,0 +1,7 @@ +.sect .text +.define _open +_open: + mov eax, 5 + int 0x80 + jb .set_errno + ret diff --git a/plat/osx386/libsys/read.s b/plat/osx386/libsys/read.s new file mode 100644 index 000000000..ccd3f8162 --- /dev/null +++ b/plat/osx386/libsys/read.s @@ -0,0 +1,7 @@ +.sect .text +.define _read +_read: + mov eax, 3 + int 0x80 + jb .set_errno + ret diff --git a/plat/osx386/libsys/set_errno.s b/plat/osx386/libsys/set_errno.s new file mode 100644 index 000000000..ff0823483 --- /dev/null +++ b/plat/osx386/libsys/set_errno.s @@ -0,0 +1,6 @@ +.sect .text +.define .set_errno +.set_errno: + mov (_errno), eax + mov eax, -1 + ret diff --git a/plat/osx386/libsys/sigaction.s b/plat/osx386/libsys/sigaction.s new file mode 100644 index 000000000..2909cbe02 --- /dev/null +++ b/plat/osx386/libsys/sigaction.s @@ -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 diff --git a/plat/osx386/libsys/stat.s b/plat/osx386/libsys/stat.s new file mode 100644 index 000000000..858d84db0 --- /dev/null +++ b/plat/osx386/libsys/stat.s @@ -0,0 +1,7 @@ +.sect .text +.define _stat +_stat: + mov eax, 188 + int 0x80 + jb .set_errno + ret diff --git a/plat/osx386/libsys/write.s b/plat/osx386/libsys/write.s new file mode 100644 index 000000000..a32d4d78a --- /dev/null +++ b/plat/osx386/libsys/write.s @@ -0,0 +1,7 @@ +.sect .text +.define _write +_write: + mov eax, 4 + int 0x80 + jb .set_errno + ret diff --git a/plat/osxppc/README b/plat/osxppc/README new file mode 100644 index 000000000..dd9a4d49c --- /dev/null +++ b/plat/osxppc/README @@ -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 +2016-12-03 diff --git a/plat/osxppc/boot.s b/plat/osxppc/boot.s new file mode 100644 index 000000000..e96198eb4 --- /dev/null +++ b/plat/osxppc/boot.s @@ -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 diff --git a/plat/osxppc/build-pkg.lua b/plat/osxppc/build-pkg.lua new file mode 100644 index 000000000..c94ad6ef0 --- /dev/null +++ b/plat/osxppc/build-pkg.lua @@ -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", + } +} diff --git a/plat/osxppc/build-tools.lua b/plat/osxppc/build-tools.lua new file mode 100644 index 000000000..eddeea85a --- /dev/null +++ b/plat/osxppc/build-tools.lua @@ -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", + } +} diff --git a/plat/osxppc/descr b/plat/osxppc/descr new file mode 100644 index 000000000..f659ff0ef --- /dev/null +++ b/plat/osxppc/descr @@ -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 diff --git a/plat/osxppc/include/build.lua b/plat/osxppc/include/build.lua new file mode 100644 index 000000000..0fe204ece --- /dev/null +++ b/plat/osxppc/include/build.lua @@ -0,0 +1,4 @@ +installable { + name = "pkg", + map = { "plat/osx/include+pkg" } +} \ No newline at end of file diff --git a/plat/osxppc/libsys/_exit.s b/plat/osxppc/libsys/_exit.s new file mode 100644 index 000000000..6ffe502d5 --- /dev/null +++ b/plat/osxppc/libsys/_exit.s @@ -0,0 +1,6 @@ +.sect .text +.define __exit +__exit: + addi r0, r0, 1 ! _exit + lwz r3, 0(sp) ! status + sc 0 diff --git a/plat/osxppc/libsys/build.lua b/plat/osxppc/libsys/build.lua new file mode 100644 index 000000000..072730b7a --- /dev/null +++ b/plat/osxppc/libsys/build.lua @@ -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" + } +} diff --git a/plat/osxppc/libsys/close.s b/plat/osxppc/libsys/close.s new file mode 100644 index 000000000..a799b5e9d --- /dev/null +++ b/plat/osxppc/libsys/close.s @@ -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 diff --git a/plat/osxppc/libsys/fstat.s b/plat/osxppc/libsys/fstat.s new file mode 100644 index 000000000..d641cbd91 --- /dev/null +++ b/plat/osxppc/libsys/fstat.s @@ -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 diff --git a/plat/osxppc/libsys/getdirentries.s b/plat/osxppc/libsys/getdirentries.s new file mode 100644 index 000000000..d038c5977 --- /dev/null +++ b/plat/osxppc/libsys/getdirentries.s @@ -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 diff --git a/plat/osxppc/libsys/getpid.s b/plat/osxppc/libsys/getpid.s new file mode 100644 index 000000000..36525cf43 --- /dev/null +++ b/plat/osxppc/libsys/getpid.s @@ -0,0 +1,7 @@ +.sect .text +.define _getpid +_getpid: + addi r0, r0, 20 ! getpid + sc 0 + b .set_errno + bclr 20, 0, 0 diff --git a/plat/osxppc/libsys/gettimeofday.s b/plat/osxppc/libsys/gettimeofday.s new file mode 100644 index 000000000..178d17fdd --- /dev/null +++ b/plat/osxppc/libsys/gettimeofday.s @@ -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 diff --git a/plat/osxppc/libsys/ioctl.s b/plat/osxppc/libsys/ioctl.s new file mode 100644 index 000000000..685ba8499 --- /dev/null +++ b/plat/osxppc/libsys/ioctl.s @@ -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 diff --git a/plat/osxppc/libsys/kill.s b/plat/osxppc/libsys/kill.s new file mode 100644 index 000000000..4c7d5566f --- /dev/null +++ b/plat/osxppc/libsys/kill.s @@ -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 diff --git a/plat/osxppc/libsys/lseek.s b/plat/osxppc/libsys/lseek.s new file mode 100644 index 000000000..129b08a8d --- /dev/null +++ b/plat/osxppc/libsys/lseek.s @@ -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 diff --git a/plat/osxppc/libsys/lstat.s b/plat/osxppc/libsys/lstat.s new file mode 100644 index 000000000..24d7c44ab --- /dev/null +++ b/plat/osxppc/libsys/lstat.s @@ -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 diff --git a/plat/osxppc/libsys/mmap.s b/plat/osxppc/libsys/mmap.s new file mode 100644 index 000000000..f2ec5e28b --- /dev/null +++ b/plat/osxppc/libsys/mmap.s @@ -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 diff --git a/plat/osxppc/libsys/mprotect.s b/plat/osxppc/libsys/mprotect.s new file mode 100644 index 000000000..fbea70251 --- /dev/null +++ b/plat/osxppc/libsys/mprotect.s @@ -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 diff --git a/plat/osxppc/libsys/open.s b/plat/osxppc/libsys/open.s new file mode 100644 index 000000000..1d066c7ad --- /dev/null +++ b/plat/osxppc/libsys/open.s @@ -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 diff --git a/plat/osxppc/libsys/read.s b/plat/osxppc/libsys/read.s new file mode 100644 index 000000000..849da5932 --- /dev/null +++ b/plat/osxppc/libsys/read.s @@ -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 diff --git a/plat/osxppc/libsys/set_errno.s b/plat/osxppc/libsys/set_errno.s new file mode 100644 index 000000000..e406865a6 --- /dev/null +++ b/plat/osxppc/libsys/set_errno.s @@ -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 diff --git a/plat/osxppc/libsys/sigaction.s b/plat/osxppc/libsys/sigaction.s new file mode 100644 index 000000000..f330c88a5 --- /dev/null +++ b/plat/osxppc/libsys/sigaction.s @@ -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 diff --git a/plat/osxppc/libsys/stat.s b/plat/osxppc/libsys/stat.s new file mode 100644 index 000000000..ab8422cda --- /dev/null +++ b/plat/osxppc/libsys/stat.s @@ -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 diff --git a/plat/osxppc/libsys/write.s b/plat/osxppc/libsys/write.s new file mode 100644 index 000000000..48f59cd72 --- /dev/null +++ b/plat/osxppc/libsys/write.s @@ -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 diff --git a/plat/qemuppc/boot.s b/plat/qemuppc/boot.s index 2dd9a4c5c..ac3227dc9 100644 --- a/plat/qemuppc/boot.s +++ b/plat/qemuppc/boot.s @@ -13,22 +13,14 @@ .sect .text begtext: - ! This code is placed at the beginning of the ELF executable and is the - ! first thing that runs. + ! This code is the first thing that runs. The booloader + ! 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+(4*argc) env (X quads) - ! sp+4+(4*argc) NULL - ! sp+4 argv (argc quads) + ! sp+8 environment pointer + ! sp+4 argv as a pointer ! sp argc - ! - ! The ACK actually expects: - ! - ! sp+8 argc - ! sp+4 ptr to argv - ! sp ptr to env li32 r3, __openfirmware_ptr stw r5, 0(r3) @@ -47,15 +39,23 @@ begtext: ! falls through .define __exit -.extern __exit .define EXIT -.extern 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 -.extern _openfirmware_call _openfirmware_call: lwz r3, 0(sp) li32 r4, __openfirmware_ptr @@ -66,15 +66,10 @@ _openfirmware_call: ! Define symbols at the beginning of our various segments, so that we can find ! them. (Except .text, which has already been done.) -.sect .data; begdata: .sect .rom; begrom: +.sect .data; begdata: .sect .bss; begbss: -! Some magic data. All EM systems need these. - -.define _errno -.comm _errno, 4 ! Posix errno storage - ! The argv and env arrays. .sect .rom @@ -82,6 +77,12 @@ argv: .data4 exename, 0 envp: .data4 0 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 .comm .trppc, 4 ! ptr to user trap handler .comm .ignmask, 4 ! user trap ignore mask diff --git a/tests/plat/_dummy.c b/tests/plat/_dummy_e.c similarity index 100% rename from tests/plat/_dummy.c rename to tests/plat/_dummy_e.c diff --git a/tests/plat/bugs/bug-22-inn_mod.mod b/tests/plat/bugs/bug-22-inn_mod.mod new file mode 100644 index 000000000..53fe93568 --- /dev/null +++ b/tests/plat/bugs/bug-22-inn_mod.mod @@ -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. \ No newline at end of file diff --git a/tests/plat/build.lua b/tests/plat/build.lua index f5885a190..3f43fb95b 100644 --- a/tests/plat/build.lua +++ b/tests/plat/build.lua @@ -11,20 +11,24 @@ definerule("plat_testsuite", local testfiles = filenamesof( "tests/plat/*.c", "tests/plat/*.e", - "tests/plat/*.p" + "tests/plat/*.p", + "tests/plat/bugs/*.mod" ) acklibrary { name = "lib", 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 }, } local tests = {} for _, f in ipairs(testfiles) do - local fs = replace(basename(f), "%..$", "") - local _, _, lang = fs:find("_(.)$") + local fs = replace(basename(f), "%.[^.]+$", "") + local _, _, lang = fs:find("_([^_]+)$") if not lang then lang = "e" end @@ -49,7 +53,7 @@ definerule("plat_testsuite", "util/build+testrunner" }, 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 diff --git a/tests/plat/inn_e.e b/tests/plat/inn_e.e index 7d27abf42..a5aee02f5 100644 --- a/tests/plat/inn_e.e +++ b/tests/plat/inn_e.e @@ -7,10 +7,10 @@ /* Test non-existent bit */ .1 - rom 0I1, 0I1, 0I1, 0I1 + rom 0I4 loe .1 loc 1 /* bit number */ - inn EM_WSIZE + inn 4 zeq *1 loc __LINE__ @@ -21,9 +21,12 @@ /* Test existent bit */ .2 - rom 2I1, 0I1, 0I1, 0I1 + rom 16384 +.21 + rom 14 /* to defeat constant folding */ + loe .2 - loc 1 /* bit number */ + loe .21 /* bit number */ inn EM_WSIZE zne *2 @@ -35,10 +38,9 @@ /* Test non-existent high bit */ .3 - rom 0I1, 0I1, 0I1, 0I1 - rom 0I1, 0I1, 0I1, 0I1 + rom 0, 0 .31 - rom (EM_WSIZE*8)+1 /* to defeat constant folding */ + rom 8 /* to defeat constant folding */ lae .3 loi EM_WSIZE*2 @@ -54,16 +56,7 @@ /* Test existent high bit */ .4 -#if EM_WSIZE == 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 - + rom 0, 2 .41 rom (EM_WSIZE*8)+1 /* to defeat constant folding */ diff --git a/tests/plat/lib/Test.def b/tests/plat/lib/Test.def new file mode 100644 index 000000000..cab31f2e8 --- /dev/null +++ b/tests/plat/lib/Test.def @@ -0,0 +1,6 @@ +(*$Foreign*) +DEFINITION MODULE Test; + PROCEDURE finished(); + PROCEDURE writehex(code: LONGINT); + PROCEDURE fail(code: LONGINT); +END Test. diff --git a/tests/plat/lib/build.lua b/tests/plat/lib/build.lua deleted file mode 100644 index be9928c84..000000000 --- a/tests/plat/lib/build.lua +++ /dev/null @@ -1,7 +0,0 @@ -include("plat/build.lua") - -acklibrary { - name = "lib", - srcs = { "./test.c" }, - hdrs = { "./test.h" }, -} diff --git a/tests/plat/testdriver.sh b/tests/plat/testdriver.sh index 5e8e5b899..384c83b47 100755 --- a/tests/plat/testdriver.sh +++ b/tests/plat/testdriver.sh @@ -27,7 +27,7 @@ get_test_output() { qemu-system-ppc) img="-kernel $img" ;; esac - $timeoutprog -t $timeout -- $method -nographic $img 2>&1 > $result + $timeoutprog -t $timeout -- $method -nographic $img > $result 2>&1 ;; qemu-*) @@ -37,7 +37,7 @@ get_test_output() { exit 0 fi - $method $img 2>&1 > $result + $method $img > $result 2>&1 ;; *) diff --git a/util/amisc/aelflod.1 b/util/amisc/aelflod.1 index 24001ba2d..808b95429 100644 --- a/util/amisc/aelflod.1 +++ b/util/amisc/aelflod.1 @@ -1,25 +1,58 @@ -.TH ASLOD 1 "$Revision$" +.TH AELFLOD 1 "$Revision$" .SH NAME aelflod \- ACK ELF loader .SH SYNOPSIS -aelflod [-h] [-v] inputfile outputfile +.B aelflod +[-a\fInumber\fP] [-b] [-h] [-l] [-m\fInumber\fP] [-v] inputfile outputfile .SH DESCRIPTION .I aelflod converts an absolute ack.out file into a simple binary memory -dump wrapped up in an ELF executable. It is suitable for producing -executables for operating systems such as Linux. - +dump wrapped up in an ELF executable. +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, DATA and BSS, in that order, all occupying contiguous memory. The file must have all references resolved and be linked to a -fixed address. The fixed address must be at least 0x54 bytes -greater than a page boundary, in order to make room for the ELF -header itself. - -aelflod will write out an ELF header followed by each segment, in -order, ensuring that enough padding is inserted between each segment -to keep the offsets correct. The created executable will contain just -one rwx segment, and no sections. - +fixed address. +The fixed address must be at least 0x54 bytes greater than a +page boundary, in order to make room for the ELF header itself. +.PP +.I aelflod +will write out an ELF header followed by each segment, in order, +ensuring that enough padding is inserted between each segment +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" ack.out(5) diff --git a/util/amisc/aelflod.c b/util/amisc/aelflod.c index e3692ac01..d5a9c0df3 100644 --- a/util/amisc/aelflod.c +++ b/util/amisc/aelflod.c @@ -35,10 +35,14 @@ int elfabi = 3; /* abi = Linux */ int elfmachine = 3; /* machine = EM_386 */ /* Header and section table of an ack object file. */ - + struct outhead outhead; struct outsect outsect[S_MAX]; +struct outname* outname = NULL; 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* 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 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 PROGRAM_HEADER_SIZE 0x20 #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[] = { 0x7F, 'E', 'L', 'F' }; -/* Output file definitions and such */ - -#define HDR_LENGTH 32 - -char hdr[HDR_LENGTH] ; - bool verbose = false; /* Segment numbers understood by aelflod. */ @@ -78,13 +89,33 @@ enum { 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 +/* + * ELF section numbers count up in the order that we write the ELF + * section headers. If we have no debugger symbols, we will skip + * .stab and .stabstr, then subtract 2 from all later numbers. + */ +enum { + 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. */ @@ -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)); } +/* 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. */ void emit8(unsigned char value) @@ -220,6 +325,188 @@ void emitphdr(unsigned long address, unsigned long 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 */ #define Xchar(ch) ((ch) & 0377) #define uget2(c) (Xchar((c)[0]) | ((unsigned) Xchar((c)[1]) << 8)) @@ -264,6 +551,57 @@ int rsect(FILE* f, struct outsect* sect) 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[]) { /* General housecleaning and setup. */ @@ -287,7 +625,7 @@ int main(int argc, char* argv[]) break; case 'h': - fprintf(stderr, "%s: Syntax: aelflod [-a] [-b] [-h] [-l]\n\t[-m] \n", + fprintf(stderr, "%s: Syntax: aelflod [-a] [-b] [-h] [-l]\n\t[-m] [-v] \n", program); 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 */ 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. */ - codeoffset = outsect[TEXT].os_base & 0x1FFF; - if (codeoffset < (ELF_HEADER_SIZE + PROGRAM_HEADER_SIZE*PROGRAM_HEADER_COUNT)) + code_offset = outsect[TEXT].os_base & 0x1FFF; + if (code_offset < (ELF_HEADER_SIZE + PROGRAM_HEADER_SIZE*PROGRAM_HEADER_COUNT)) fatal("base address too small --- overlaps ELF header"); /* 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[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. */ writef(elf_le_ident_string, 4, 1); @@ -414,33 +781,29 @@ int main(int argc, char* argv[]) emit32(1); /* ELF version again */ emit32(outsect[TEXT].os_base); /* entry point */ emit32(ELF_HEADER_SIZE); /* program header offset */ - emit32(0); /* section header offset */ + emit32(sh_offset); /* section header offset */ emit32(0); /* flags */ emit16(ELF_HEADER_SIZE); /* elf header size */ emit16(PROGRAM_HEADER_SIZE); /* program header entry size */ emit16(1); /* number of program header entries */ - emit16(0x28); /* section header entry size */ - emit16(0); /* number of section header entries */ - emit16(0); /* section header string table index = SHN_UNDEF */ + emit16(SECTION_HEADER_SIZE); /* section header entry size */ + emit16(sh_count); /* number of section header entries */ + emit16(shstrtab_nr); /* section header string table index */ /* Write out a single rwx section for the entire program. */ { - unsigned long filelength = codeoffset + - outsect[TEXT].os_size + - outsect[ROM].os_size + - outsect[DATA].os_size; - - unsigned long memlength = filelength + - outsect[BSS].os_size; - - emitphdr(outsect[TEXT].os_base & ~0x1FFF, filelength, memlength, - 0, 4|2|1); + uint32_t filelength = stab_offset; + uint32_t memlength = filelength + outsect[BSS].os_size; + + emitphdr(outsect[TEXT].os_base & ~0x1FFF, + filelength, memlength, 0, 4|2|1); } /* 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. */ @@ -448,6 +811,19 @@ int main(int argc, char* argv[]) emits(&outsect[ROM]); 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)) fatal("output write error"); if (outputfile) @@ -459,7 +835,7 @@ int main(int argc, char* argv[]) { uint32_t ss = 0; 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(" 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); diff --git a/util/amisc/aslod.1 b/util/amisc/aslod.1 index a786b13e6..f81590739 100644 --- a/util/amisc/aslod.1 +++ b/util/amisc/aslod.1 @@ -2,19 +2,19 @@ .SH NAME aslod \- ACK simple loader .SH SYNOPSIS -aslod [-h] [-v] inputfile outputfile +.B aslod +[-h] [-v] inputfile outputfile .SH DESCRIPTION .I aslod -converts an absolute ack.out file into a simple binary memory -dump. It is suitable for producing RAM images, executables for +converts an absolute ack.out file into a simple binary memory dump. +It is suitable for producing RAM images, executables for simple operating systems such as CP/M, DOS, etc. - +.PP The input file must contain exactly four segments: TEXT, ROM, DATA and BSS, in that order, all occupying contiguous memory. The file must have all references resolved and be linked to a fixed address. aslod will dump the segments, in order, such that the first byte of TEXT is at offset 0 in the file (regardless of where it is in memory). - .SH "SEE ALSO" ack.out(5) diff --git a/util/amisc/aslod.c b/util/amisc/aslod.c index afc8f8c3e..72fbc8ce4 100644 --- a/util/amisc/aslod.c +++ b/util/amisc/aslod.c @@ -45,12 +45,6 @@ FILE* output; /* Output stream */ #define readf(a, b, c) fread((a), (b), (int)(c), input) #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; /* Segment numbers understood by aslod. */ @@ -63,14 +57,6 @@ enum { 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. */ void fatal(const char* s, ...) diff --git a/util/ego/descr/build.lua b/util/ego/descr/build.lua index 034ffa3ce..e9a008cc5 100644 --- a/util/ego/descr/build.lua +++ b/util/ego/descr/build.lua @@ -22,6 +22,7 @@ end build_descr("i386") build_descr("i86") build_descr("m68020") +build_descr("powerpc") installable { name = "pkg", diff --git a/util/ego/descr/powerpc.descr b/util/ego/descr/powerpc.descr new file mode 100644 index 000000000..5138cc44b --- /dev/null +++ b/util/ego/descr/powerpc.descr @@ -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 diff --git a/util/led/main.c b/util/led/main.c index 2ec6ca2f8..4ea0b0d05 100644 --- a/util/led/main.c +++ b/util/led/main.c @@ -11,6 +11,7 @@ static char rcsid[] = "$Id$"; */ #include +#include #include #include "const.h" #include "debug.h" @@ -29,15 +30,16 @@ int Verbose = 0; static initializations(); static first_pass(); -static long number(); -static setlign(); -static setbase(); +static uint32_t number(const char *); +static void setlign(int, uint32_t); +static void setbase(int, uint32_t); static struct outname *makename(); static pass1(); static evaluate(); -static void norm_commons(); +static void norm_commons(); static complete_sections(); -static void change_names(); +static void change_names(); +static bool setbit(); static bool tstbit(); static second_pass(); static pass2(); @@ -251,12 +253,11 @@ first_pass(argv) * else if it starts with 0, it's octal, * else it's decimal. */ -static long -number(s) - register char *s; +static uint32_t +number(const char *s) { register int digit; - register long value = 0; + register uint32_t value = 0; register int radix = 10; if (*s == '0') { @@ -291,22 +292,17 @@ number(s) * not. Only one base may be given. The same applies for alignments. */ static char basemap[MAXSECT / WIDTH]; -static long sect_base[MAXSECT]; +static uint32_t sect_base[MAXSECT]; static char lignmap[MAXSECT / WIDTH]; -static long sect_lign[MAXSECT]; +static uint32_t sect_lign[MAXSECT]; -/* /* * Set the alignment of section `sectno' to `lign', if this doesn't * conflict with earlier alignment. */ -static -setlign(sectno, lign) - register int sectno; - register long lign; +static void +setlign(int sectno, uint32_t lign) { - extern bool setbit(); - if (setbit(sectno, lignmap) && sect_lign[sectno] != lign) fatal("section has different alignments"); if (lign == (long)0) @@ -318,13 +314,9 @@ setlign(sectno, lign) * Set the base of section `sectno' to `base', if no other base has been * given yet. */ -static -setbase(sectno, base) - register int sectno; - register long base; +static void +setbase(int sectno, uint32_t base) { - extern bool setbit(); - if (setbit(sectno, basemap) && sect_base[sectno] != base) fatal("section has different bases"); sect_base[sectno] = base; @@ -459,8 +451,8 @@ struct orig relorig[MAXSECT]; static complete_sections() { - register long base = 0; - register long foff; + register uint32_t base = 0; + register uint32_t foff; register struct outsect *sc; register int sectindex;