diff --git a/aeneas/src/mach/CiRuntime.v3 b/aeneas/src/mach/CiRuntime.v3 index b1517608d..266693039 100644 --- a/aeneas/src/mach/CiRuntime.v3 +++ b/aeneas/src/mach/CiRuntime.v3 @@ -14,6 +14,9 @@ component CiRuntimeModule { def DATA_END = addr("DATA_END"); def STACK_START = addr("STACK_START"); def STACK_END = addr("STACK_END"); + def RESERVED_CODE_START = addr("RESERVED_CODE_START"); + def RESERVED_CODE_END = addr("RESERVED_CODE_END"); + def RESERVED_CODE_FILE_OFFSET = addr("RESERVED_CODE_FILE_OFFSET"); // TODO: technically not a pointer // machine code stub for handling signals def SIGNAL_STUB = addr("signalStub"); def SIGNAL_RESTORER = addr("signalRestorer"); diff --git a/aeneas/src/mach/MachProgram.v3 b/aeneas/src/mach/MachProgram.v3 index 8eca96b8e..cf1eac0fa 100644 --- a/aeneas/src/mach/MachProgram.v3 +++ b/aeneas/src/mach/MachProgram.v3 @@ -541,6 +541,15 @@ class MachProgram extends TargetProgram { // encode all records into the buffer encodeRegion(dataRegion, w); } + // reserve supplemental code, if any + def reserveSupplementalCode(w: MachDataWriter) { + var reservedCodeSize = CLOptions.RESERVED_CODE_SIZE.get(); + if (reservedCodeSize > 0) { + var size = int.!(reservedCodeSize); + runtime.recordReservedCode(w.posAddr(), size, w.pos); + w.skipN(int.!(size)); + } + } private def recordDataRefs(a: Addr, off: int, size: int) { if (a.is()) { // a global field in the data section diff --git a/aeneas/src/mach/MachRuntime.v3 b/aeneas/src/mach/MachRuntime.v3 index 66fff71ea..6b79b2d81 100644 --- a/aeneas/src/mach/MachRuntime.v3 +++ b/aeneas/src/mach/MachRuntime.v3 @@ -47,6 +47,11 @@ class MachRuntime(mach: MachProgram) { def recordCodeStart(addr: int) { recordAddr(CiRuntimeModule.CODE_START, addr); } + def recordReservedCode(startAddr: int, size: int, fileOffset: int) { + recordAddr(CiRuntimeModule.RESERVED_CODE_START, startAddr); + recordAddr(CiRuntimeModule.RESERVED_CODE_END, startAddr + size); + recordAddr(CiRuntimeModule.RESERVED_CODE_FILE_OFFSET, fileOffset); + } def recordCodeEnd(addr: int) { recordAddr(CiRuntimeModule.CODE_END, addr); } diff --git a/aeneas/src/main/CLOptions.v3 b/aeneas/src/main/CLOptions.v3 index f48122046..0bf65efaf 100644 --- a/aeneas/src/main/CLOptions.v3 +++ b/aeneas/src/main/CLOptions.v3 @@ -194,6 +194,8 @@ component CLOptions { "Set the heap size of the compiled program."); def STACK_SIZE = rtOpt.newSizeOption("stack-size", 0, "Set the stack size of the compiled program, enabling robust stack overflow checking."); + def RESERVED_CODE_SIZE = wasmOpt.newSizeOption("reserved-code-size", 0, + "Set the reserved code size of the compiled program for supplemental code post-compilation."); def VM_START_ADDR = rtOpt.newAddrOption("vm-start-addr", 0x08000000, "Set the virtual memory start of all program segments."); def CODE_START_ADDR = rtOpt.newAddrOption("code-start-addr", 0, diff --git a/aeneas/src/main/Version.v3 b/aeneas/src/main/Version.v3 index aa85a3d1d..31ab97def 100644 --- a/aeneas/src/main/Version.v3 +++ b/aeneas/src/main/Version.v3 @@ -3,6 +3,6 @@ // Updated by VCS scripts. DO NOT EDIT. component Version { - def version: string = "III-7.1754"; + def version: string = "III-7.1755"; var buildData: string; } diff --git a/aeneas/src/os/Linux.v3 b/aeneas/src/os/Linux.v3 index 5f6b53e99..d2284c1ad 100644 --- a/aeneas/src/os/Linux.v3 +++ b/aeneas/src/os/Linux.v3 @@ -128,6 +128,8 @@ class LinuxTarget extends Target { rt.recordCodeStart(mach.entryStub.absolute); elf.e_entry = mach.entryStub.absolute; rt.recordCodeEnd(w.endAddr()); + + mach.reserveSupplementalCode(w); // TODO: add .reserved_code symbol mach.layoutMeta(w); mach.layoutRuntime(w); code.p_filesz = w.end(); @@ -246,7 +248,7 @@ class ElfSections(elf: ElfHeader, debugSymbol: bool) { elf.sheaders.put(text); data.index = elf.sheaders.length; elf.sheaders.put(data); - + if (debugSymbol) { for (e in DebugSection) { var section = ElfSectionHeader.new(); diff --git a/aeneas/src/x86-64/SsaX86_64Gen.v3 b/aeneas/src/x86-64/SsaX86_64Gen.v3 index 77725f3dd..5228f0354 100644 --- a/aeneas/src/x86-64/SsaX86_64Gen.v3 +++ b/aeneas/src/x86-64/SsaX86_64Gen.v3 @@ -519,9 +519,9 @@ class SsaX86_64Gen extends SsaMachGen { refmap(conv); var skip = 0; if (SsaConst.?(func)) { - var target = Address.!(SsaConst.!(func).val); + var target = Addr.!(SsaConst.!(func).val); useImm(target); - if (target == null || target.val == null || V3.isComponent(target.val.receiver)) skip = 1; + if (Address.?(target) && V3.isComponent(Address.!(target).val.receiver)) skip = 1; } else { useFixed(func, Regs.NOT_PARAM); } diff --git a/aeneas/src/x86-64/X86_64Darwin.v3 b/aeneas/src/x86-64/X86_64Darwin.v3 index a97560fa1..596a9162d 100644 --- a/aeneas/src/x86-64/X86_64Darwin.v3 +++ b/aeneas/src/x86-64/X86_64Darwin.v3 @@ -114,6 +114,7 @@ class X86_64DarwinTarget extends Target { var size = w.end(); if (size < pageAlign.size) w.skipN(pageAlign.size - size); // MacOS requires >= 4096 byte binaries rt.recordCodeEnd(w.endAddr()); + mach.reserveSupplementalCode(w); mach.layoutMeta(w); mach.layoutRuntime(w); cs.filesize = w.end(); diff --git a/aeneas/src/x86/X86CodeGen.v3 b/aeneas/src/x86/X86CodeGen.v3 index 54af279c3..4dfc69585 100644 --- a/aeneas/src/x86/X86CodeGen.v3 +++ b/aeneas/src/x86/X86CodeGen.v3 @@ -633,9 +633,9 @@ class X86CodeGen extends OldCodeGen { var inputs = call.inputs; if (SsaConst.?(func)) { // match CallAddress(#func, ...) - var target = Address.!(val(func)); + var target = Addr.!(val(func)); // don't emit the (constant) receiver for a direct component call - var skip = if(target != null && V3.isComponent(target.val.receiver), 1, 0); + var skip = if(Address.?(target) && V3.isComponent(Address.!(target).val.receiver), 1, 0); for (i = 1 + skip; i < inputs.length; i++) { // input[0] == func useFixed(inputs[i].dest, conv.calleeParam(i - 1)); } diff --git a/aeneas/src/x86/X86Darwin.v3 b/aeneas/src/x86/X86Darwin.v3 index f950396a9..9414012b3 100644 --- a/aeneas/src/x86/X86Darwin.v3 +++ b/aeneas/src/x86/X86Darwin.v3 @@ -105,6 +105,7 @@ class X86DarwinTarget extends Target { var size = w.end(); if (size < pageAlign.size) w.skipN(pageAlign.size - size); // MacOS security requires >= 4096 bytes rt.recordCodeEnd(w.endAddr()); + mach.reserveSupplementalCode(w); mach.layoutMeta(w); mach.layoutRuntime(w); cs.filesize = w.end(); diff --git a/test/rt/reserved_code.v3 b/test/rt/reserved_code.v3 new file mode 100644 index 000000000..975325fa8 --- /dev/null +++ b/test/rt/reserved_code.v3 @@ -0,0 +1,103 @@ +def RESERVED_CODE_SIZE = 4096; + +var result = 0; + +def main(args: Array) -> int { + if (args.length > 0) do_patch(args[0]); + else do_run(); + return result; +} + +def do_patch(fileName: string) { + begin("loading ", fileName); + + var data = System.fileLoad(fileName); + if (data == null || data.length == 0) return fail("could not load"); + + ok(); + + begin("code size check", null); + var gotSize = int.!(CiRuntime.RESERVED_CODE_END - CiRuntime.RESERVED_CODE_START); + if (RESERVED_CODE_SIZE == gotSize) ok(); + else return fail("size does not match"); + + var offset = CiRuntime.RESERVED_CODE_FILE_OFFSET - Pointer.NULL; + var region = data[offset ..+ RESERVED_CODE_SIZE]; + + begin("patching", null); + write(region, getMachineCode()); + ok(); + + begin("writing", null); + var fd = System.fileOpen(fileName, false); + if (fd <= 0) return fail("failed to open for writing"); + + System.write(fd, data); + System.fileClose(fd); + ok(); +} + +def write(region: Range, data: Range) { + for (i < data.length) region[i] = data[i]; +} + +def begin(str1: string, str2: string) { + System.puts("##+"); + System.puts(str1); + if (str2 != null) System.puts(str2); + System.ln(); +} + +def ok() { + System.puts("##-ok\n"); +} + +def fail(msg: string) { + System.puts("##-fail: "); + if (msg != null) System.puts(msg); + System.ln(); + result |= 1; +} + +def do_run() { + var region = CiRuntime.forgeRange(CiRuntime.RESERVED_CODE_START, + int.!(CiRuntime.RESERVED_CODE_END - CiRuntime.RESERVED_CODE_START)); + + begin("checking code", null); + var expected = getMachineCode(); + if (region.length == 0) return fail("no reserved code region"); + if (region.length < expected.length) return fail("reserved code region too small"); + for (i < expected.length) { + if (region[i] != expected[i]) return fail("machine code does not match expectation"); + } + ok(); + + begin("running", null); + var codePtr = CiRuntime.RESERVED_CODE_START; + var f: void -> int = CiRuntime.forgeClosure(codePtr, ()); + var got = f(); + if (got == 42) ok(); + else fail("got wrong value"); +} + +//========================================================================================== +//== Target-specific configuration ========================================================= +//========================================================================================== + +// Retarget this test by passing -redef-field=TARGET_=true +def TARGET_x86_linux = false; +def TARGET_x86_darwin = false; +def TARGET_x86_64_linux = false; +def TARGET_x86_64_darwin = false; + +def TARGET_x86 = TARGET_x86_linux || TARGET_x86_darwin; +def TARGET_x86_64 = TARGET_x86_64_linux || TARGET_x86_64_darwin; + +def getMachineCode() -> Array { + if (TARGET_x86 || TARGET_x86_64) return [ + 0xB8, 0x2A, 0x00, 0x00, 0x00, // mov eax, 42 + 0xc3 // ret + ]; + var x = 1/0; + return null; // no other targets supported now +} diff --git a/test/rt/reserved_code.v3.expect b/test/rt/reserved_code.v3.expect new file mode 100644 index 000000000..0bdd193a9 --- /dev/null +++ b/test/rt/reserved_code.v3.expect @@ -0,0 +1,2 @@ +##+checking code +##-fail: machine code does not match expectation diff --git a/test/rt/reserved_code.v3.flags b/test/rt/reserved_code.v3.flags new file mode 100644 index 000000000..3c28ba88a --- /dev/null +++ b/test/rt/reserved_code.v3.flags @@ -0,0 +1 @@ +-reserved-code-size=4K diff --git a/test/rt/test.bash b/test/rt/test.bash index 2aaf81a79..2e4471694 100755 --- a/test/rt/test.bash +++ b/test/rt/test.bash @@ -15,11 +15,29 @@ function compile_target_tests_with_flags() { if [ -f $test.flags ]; then FLAGS=$(cat $test.flags) fi + grep -sq 'def\ TARGET_' $test > /dev/null + if [ $? = 0 ]; then + target_field="TARGET_${target//-/_}" + FLAGS="$FLAGS -redef-field=${target_field}=true" + fi run_v3c $target $FLAGS -output=$T $test &> $C trace_test_retval $? done } +# TODO: reserved code test is special in that it needs to copy and patch a binary, integrate better +function run_reserved_code_test() { + if [ ! -x $T/reserved_code ]; then + return 0 + fi + if [ ! -x $CONFIG/run-$target ]; then # TODO: better output for skipped targets + return 0 + fi + cp $T/reserved_code $T/reserved_code2 + $T/reserved_code $T/reserved_code2 + $T/reserved_code2 +} + for target in $TEST_TARGETS; do T=$OUT/$target mkdir -p $T @@ -29,5 +47,8 @@ for target in $TEST_TARGETS; do print_compiling $target compile_target_tests_with_flags $target $TESTS | $PROGRESS run_or_skip_io_tests $target $TESTS + print_status Running $target "reserved_code" + run_reserved_code_test | $PROGRESS fi done +