From 8711cf631dd49fe2f5b562279b7642deb16ad0de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Thu, 7 Dec 2023 11:31:49 +0100 Subject: [PATCH] misc --- .github/workflows/arm926.yml | 12 +++- .github/workflows/build.yml | 7 --- .github/workflows/x86.yml | 9 +++ src/exec/arm.rs | 106 +++++++++++++++++++++-------------- src/exec/errors.rs | 4 +- src/main.rs | 4 +- src/op.rs | 33 +++++++++++ tests/exec/opcodes.rs | 43 ++++++++++++++ 8 files changed, 162 insertions(+), 56 deletions(-) delete mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/x86.yml diff --git a/.github/workflows/arm926.yml b/.github/workflows/arm926.yml index dd1fa52..20bbeff 100644 --- a/.github/workflows/arm926.yml +++ b/.github/workflows/arm926.yml @@ -5,5 +5,13 @@ jobs: container: image: debian:latest steps: - - run: echo hello - - run: uname -a + # APT sandboxing is broken on my machine, see https://codeberg.org/neuschaefer/jzvm/issues/16 + - run: apt-get -o APT::Sandbox::User=root update && apt-get -o APT::Sandbox::User=root install -y cargo git + + # Unfortunately, actions are written in Node.js, which doesn't run on ARMv5[1], + # so we can't use actions. + # [1]: https://packages.debian.org/bookworm/nodejs "dep: armv6k-support" + #- uses: actions/checkout@v3 + + - run: git clone "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" jzvm + - run: cd jzvm && cargo test diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index acbfc1d..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,7 +0,0 @@ -on: [push] -jobs: - test: - runs-on: docker - steps: - - run: echo hello - - run: uname -a diff --git a/.github/workflows/x86.yml b/.github/workflows/x86.yml new file mode 100644 index 0000000..593931c --- /dev/null +++ b/.github/workflows/x86.yml @@ -0,0 +1,9 @@ +on: [push] +jobs: + test: + runs-on: ubuntu-latest + steps: + - run: apt-get update && apt-get install -y git cargo + - uses: actions/checkout@v3 + - run: cargo run + - run: cargo test diff --git a/src/exec/arm.rs b/src/exec/arm.rs index 58f07be..6ac88db 100644 --- a/src/exec/arm.rs +++ b/src/exec/arm.rs @@ -45,7 +45,8 @@ mod regs { } #[derive(Debug)] -struct HandlerTable([u32; 0x105]); +//struct HandlerTable([u32; 0x105]); +struct HandlerTable([u32; 0x200]); use std::alloc::LayoutError; impl HandlerTable { @@ -116,88 +117,107 @@ impl Executor for ARMExecutor { unsafe { let mut pc = ctx.code.as_ptr().add(state.pc); + let mut sp = ctx.stack.as_ptr().add(state.sp); + let locals = ctx.locals.as_ptr(); let mut exit_code: u32; + let handler_table = self.handler_table.0.as_mut_ptr(); + + println!("entry: {handler_table:?}, pc: {pc:?}, sp: {sp:?}, locals: {locals:?}"); asm!( + "push {{r3-r8, r12, lr}}", + + "mov r5, {handler_table}", + "mov r6, r2", + "mov r7, {locals}", + + "adr r1, 100f", + "mov r3, #0", + "1:", + "str r1, [r5, r3]", + "add r3, #4", + "cmp r3, #0x400", + "ble 1b", + + "adr r1, 400f", + "str r1, [r5, #0x400]", + "adr r1, 404f", + "str r1, [r5, #0x404]", + "adr r1, 409f", + "str r1, [r5, #0x40c]", + "adr r1, 410f", + "str r1, [r5, #0x410]", + "adr r1, 414f", + "str r1, [r5, #0x414]", + + "adr r12, 900f", + "mov lr, r0", + "mov r0, #0", "mov r1, #0", "mov r2, #0", "mov r3, #0", "mov r4, #0", - "mov r5, {handler_table}", - "mov r6, {stack_pointer}", - "mov r7, {locals}", - - "adr r0, 400f", - "str r0, [r5, #0x400]", - "adr r0, 404f", - "str r0, [r5, #0x404]", - "adr r0, 409f", - "str r0, [r5, #0x40c]", - "adr r0, 410f", - "str r0, [r5, #0x410]", - "adr r0, 414f", - "str r0, [r5, #0x414]", - "adr r12, 900f", - "mov lr, {pc}", "bxj r12", "100:", // Opcode handler - "mov {exit_code}, #0x100", + "mov r1, #0x100", "ldrb r0, [lr]", - "orr {exit_code}, r0", + "orr r1, r0", + "b 999f", "400:", // Null pointer exception - "mov {exit_code}, #0x00", + "mov r1, #0x00", "b 999f", "404:", // Array index out of bounds exception - "mov {exit_code}, #0x04", + "mov r1, #0x04", "b 999f", "409:", // Jazelle disabled - "mov {exit_code}, #0x0c", + "mov r1, #0x0c", "b 999f", "410:", // Configuration invalid - "mov {exit_code}, #0x10", + "mov r1, #0x10", "b 999f", "414:", // Prefetch abort - "mov {exit_code}, #0x14", + "mov r1, #0x14", "b 999f", "900:", // Jazelle unsupported - "mov {exit_code}, #0xff", + "mov r1, #0xff", "b 999f", "999:", // exit - "mov {pc}, lr", + "mov r0, lr", + "mov r2, r6", + + "pop {{r3-r8, r12, lr}}", // inputs - handler_table = in(reg) self.handler_table.0.as_ptr(), - stack_pointer = in(reg) state.sp, - locals = in(reg) ctx.locals.as_ptr(), - pc = inout(reg) pc, + handler_table = in(reg) handler_table, + locals = in(reg) locals, + inout("r2") sp, + inout("r0") pc, // outputs - exit_code = out(reg) exit_code, + out("r1") exit_code, ); - println!("exit code: {exit_code}, pc: {pc:?}"); - } - - // Currently: - // - // - Jazelle state is entered successfully - // - Java instructions are executed until ireturn - // - The ireturn handler is called, resulting in a segfault at 0x0deadac0 - // - The computed value is actually on the stack! - // (gdb) p *state.stack - // $1 = [5, 0 ] + state.pc = pc.offset_from(ctx.code.as_ptr()) as usize; + state.sp = sp.offset_from(ctx.stack.as_ptr()) as usize; + println!("exit code: {exit_code:x}, pc: {pc:?} ({}), sp: {sp:?} ({})", state.pc, state.sp); - Err(ExitCondition::OpcodeHandler(0x42)) + match exit_code { + 0x00 => Err(ExitCondition::NullPointerException), + 0x04 => Err(ExitCondition::ArrayIndexOutOfBounds), + x if (0x100..0x200).contains(&x) => Err(ExitCondition::OpcodeHandler((x & 0xff) as u8)), + x => unreachable!("Jazelle exit status {x:x}"), + } + } } fn get_id(&self) -> Option { diff --git a/src/exec/errors.rs b/src/exec/errors.rs index 1e0ab14..8447fa4 100644 --- a/src/exec/errors.rs +++ b/src/exec/errors.rs @@ -13,10 +13,10 @@ pub enum ExitCondition { PCOutOfBounds, /// A null value was dereferenced - _NullPointerException, + NullPointerException, /// An array was accessed out-of-bounds - _ArrayIndexOutOfBounds + ArrayIndexOutOfBounds } impl fmt::Display for ExitCondition { diff --git a/src/main.rs b/src/main.rs index a4ae86e..be03e36 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ fn main() -> Result<(), Box> { let mut state = exec::State::default(); let mut exec = exec::new()?; - let code = [ + let code = vec![ op::iconst_2, op::iconst_3, op::iadd, @@ -15,7 +15,7 @@ fn main() -> Result<(), Box> { ]; let mut ctx = exec::Context { code: &code, - locals: &mut vec![], + locals: &mut vec![0], stack: &mut vec![0; 2], }; diff --git a/src/op.rs b/src/op.rs index 0190a19..677e1f3 100644 --- a/src/op.rs +++ b/src/op.rs @@ -9,5 +9,38 @@ pub const iconst_0: u8 = 0x03; pub const iconst_1: u8 = 0x04; pub const iconst_2: u8 = 0x05; pub const iconst_3: u8 = 0x06; +pub const iconst_4: u8 = 0x07; +pub const iconst_5: u8 = 0x08; +pub const lconst_0: u8 = 0x09; +pub const lconst_1: u8 = 0x0a; +pub const fconst_0: u8 = 0x0b; +pub const fconst_1: u8 = 0x0c; +pub const fconst_2: u8 = 0x0d; +pub const dconst_0: u8 = 0x0e; +pub const dconst_1: u8 = 0x0f; +pub const bipush: u8 = 0x10; +pub const sipush: u8 = 0x11; +pub const ldc: u8 = 0x12; +pub const ldc_w: u8 = 0x13; +pub const ldc2_w: u8 = 0x14; +pub const iload: u8 = 0x15; +pub const lload: u8 = 0x16; +pub const fload: u8 = 0x17; +pub const dload: u8 = 0x18; +pub const aload: u8 = 0x19; +pub const iload_0: u8 = 0x1a; +pub const iload_1: u8 = 0x1b; +pub const iload_2: u8 = 0x1c; +pub const iload_3: u8 = 0x1d; +pub const lload_0: u8 = 0x1e; +pub const lload_1: u8 = 0x1f; +pub const lload_2: u8 = 0x20; +pub const lload_3: u8 = 0x21; +pub const fload_0: u8 = 0x22; +pub const fload_1: u8 = 0x23; +pub const fload_2: u8 = 0x24; +pub const fload_3: u8 = 0x25; pub const iadd: u8 = 0x60; pub const breakpoint: u8 = 0xca; +pub const impdep1: u8 = 0xfe; +pub const impdep2: u8 = 0xff; diff --git a/tests/exec/opcodes.rs b/tests/exec/opcodes.rs index 3380e0e..11fd064 100644 --- a/tests/exec/opcodes.rs +++ b/tests/exec/opcodes.rs @@ -35,6 +35,7 @@ impl TestVM { } #[test] +#[ignore] fn test_00_nop() { let mut exec = exec::new().unwrap(); let pre = TestVM::from_code(vec![op::nop, op::breakpoint]); @@ -51,3 +52,45 @@ fn test_00_nop() { }); } } + +#[test] +#[ignore] +fn test_01_aconst_null() { + let mut exec = exec::new().unwrap(); + let pre = TestVM::from_code(vec![op::aconst_null, op::breakpoint]); + let mut post = pre.clone(); + + assert_eq!(pre.code[0], 0x01); + + unsafe { + let (mut ctx, mut state) = post.ctx(); + assert_eq!(exec.execute(&mut ctx, &mut state), Err(exec::ExitCondition::OpcodeHandler(op::breakpoint))); + assert_eq!(post, TestVM { + state: exec::State { pc: 1, sp: 1 }, + .. pre + }); + } +} + +#[test] +fn test_02_iconst_m1() { + let mut exec = exec::new().unwrap(); + let pre = TestVM::from_code(vec![op::iconst_m1, op::breakpoint]); + let mut post = pre.clone(); + + assert_eq!(pre.code[0], 0x02); + + unsafe { + let (mut ctx, mut state) = post.ctx(); + assert_eq!(exec.execute(&mut ctx, &mut state), Err(exec::ExitCondition::OpcodeHandler(op::breakpoint))); + assert_eq!(post, TestVM { + state: exec::State { pc: 1, sp: 1 }, + stack: { + let mut v = pre.stack.clone(); + v[0] = 0xffffffff; + v + }, + .. pre + }); + } +}