Skip to content

Commit

Permalink
Implement -reserved-code-size=<size> option to reserve code space tha…
Browse files Browse the repository at this point in the history
…t can be patched in the binary
  • Loading branch information
titzer committed Aug 11, 2024
1 parent 07429a2 commit ec98328
Show file tree
Hide file tree
Showing 14 changed files with 156 additions and 6 deletions.
3 changes: 3 additions & 0 deletions aeneas/src/mach/CiRuntime.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
9 changes: 9 additions & 0 deletions aeneas/src/mach/MachProgram.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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<IrField>()) {
// a global field in the data section
Expand Down
5 changes: 5 additions & 0 deletions aeneas/src/mach/MachRuntime.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
2 changes: 2 additions & 0 deletions aeneas/src/main/CLOptions.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion aeneas/src/main/Version.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
4 changes: 3 additions & 1 deletion aeneas/src/os/Linux.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions aeneas/src/x86-64/SsaX86_64Gen.v3
Original file line number Diff line number Diff line change
Expand Up @@ -519,9 +519,9 @@ class SsaX86_64Gen extends SsaMachGen {
refmap(conv);
var skip = 0;
if (SsaConst.?(func)) {
var target = Address<IrMethod>.!(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<IrMethod>.?(target) && V3.isComponent(Address<IrMethod>.!(target).val.receiver)) skip = 1;
} else {
useFixed(func, Regs.NOT_PARAM);
}
Expand Down
1 change: 1 addition & 0 deletions aeneas/src/x86-64/X86_64Darwin.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions aeneas/src/x86/X86CodeGen.v3
Original file line number Diff line number Diff line change
Expand Up @@ -633,9 +633,9 @@ class X86CodeGen extends OldCodeGen {
var inputs = call.inputs;
if (SsaConst.?(func)) {
// match CallAddress(#func, ...)
var target = Address<IrMethod>.!(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<IrMethod>.?(target) && V3.isComponent(Address<IrMethod>.!(target).val.receiver), 1, 0);
for (i = 1 + skip; i < inputs.length; i++) { // input[0] == func
useFixed(inputs[i].dest, conv.calleeParam(i - 1));
}
Expand Down
1 change: 1 addition & 0 deletions aeneas/src/x86/X86Darwin.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
103 changes: 103 additions & 0 deletions test/rt/reserved_code.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
def RESERVED_CODE_SIZE = 4096;

var result = 0;

def main(args: Array<string>) -> 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<byte>, data: Range<byte>) {
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<byte>(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<void, void, int>(codePtr, ());
var got = f();
if (got == 42) ok();
else fail("got wrong value");
}

//==========================================================================================
//== Target-specific configuration =========================================================
//==========================================================================================

// Retarget this test by passing -redef-field=TARGET_<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<byte> {
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
}
2 changes: 2 additions & 0 deletions test/rt/reserved_code.v3.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
##+checking code
##-fail: machine code does not match expectation
1 change: 1 addition & 0 deletions test/rt/reserved_code.v3.flags
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-reserved-code-size=4K
21 changes: 21 additions & 0 deletions test/rt/test.bash
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

0 comments on commit ec98328

Please sign in to comment.