From f5428c79b2b10c132a8db024549a9563bc0f036f Mon Sep 17 00:00:00 2001 From: VegOwOtenks Date: Tue, 12 Nov 2024 13:29:01 +0100 Subject: [PATCH 1/2] Implemented catchall in ::EntryPoint --- src/classfile.rs | 43 ++++++++++++++++++++++++++--- src/jvm.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++--- src/stackframe.rs | 4 +++ 3 files changed, 109 insertions(+), 8 deletions(-) diff --git a/src/classfile.rs b/src/classfile.rs index 8e0cf65..f6399f3 100644 --- a/src/classfile.rs +++ b/src/classfile.rs @@ -391,6 +391,23 @@ impl JavaClassFile { }, } } + + pub fn is_method_bytecode_protected(&self, method: &MethodInfo, instruction_pointer: u16, exception: crate::heap_area::ObjectReference) -> bool { + let code_attribute = method.get_code_attribute().unwrap(); + + for exception_entry in &code_attribute.exception_table { + if exception_entry.start_pc <= instruction_pointer && exception_entry.end_pc > instruction_pointer { + if exception_entry.catch_type == 0 { + return true; + } else { + // Check catch-type + todo!() + } + } + } + + false + } } #[derive(Debug)] @@ -500,10 +517,10 @@ impl LineNumberTableAttributeData { #[derive(Debug)] pub struct ExceptionTableEntry { - start_pc: u16, - end_pc: u16, - handler_pc: u16, - catch_type: u16, + pub start_pc: u16, + pub end_pc: u16, + pub handler_pc: u16, + pub catch_type: u16, } impl ExceptionTableEntry { @@ -517,6 +534,10 @@ impl ExceptionTableEntry { } ) } + + fn catches_in(&self, instruction_pointer: u16) -> bool { + return self.start_pc <= instruction_pointer && self.end_pc > instruction_pointer + } } #[derive(Debug)] @@ -1128,6 +1149,20 @@ impl MethodInfo { None }; } + + pub fn is_native(&self) -> bool { + return self.access_flags & MethodAccessFlag::Native; + } + + pub fn get_protected_handler_pc(&self, instruction_pointer: u16) -> Option { + for exception_entry in &self.get_code_attribute()?.exception_table { + if exception_entry.catches_in(instruction_pointer) { + return Some(exception_entry.handler_pc) + } + } + + None + } } diff --git a/src/jvm.rs b/src/jvm.rs index 3f24b01..32ab7aa 100644 --- a/src/jvm.rs +++ b/src/jvm.rs @@ -1,3 +1,4 @@ +use crate::classfile::ExceptionTableEntry; use core::fmt::{Display, Formatter}; use std::collections::VecDeque; @@ -9,7 +10,7 @@ use crate::classfile; use crate::classfile::{ JavaClassFile, FieldInfo, MethodInfo, MethodDescriptor, AbstractTypeDescription, AbstractTypeKind, AttributeInfo, AttributeData, CodeAttributeData, ConstantValueAttributeData }; use crate::classstore; use crate::classstore::ClassStore; -use crate::constantpool::{ ConstantPoolInfo, ConstantClassInfo, ConstantUtf8Info, ConstantMethodRefInfo, ConstantNameAndTypeInfo}; +use crate::constantpool::{ ConstantClassInfo, ConstantInterfaceMethodRefInfo, ConstantMethodRefInfo, ConstantNameAndTypeInfo, ConstantPoolInfo, ConstantUtf8Info}; use crate::heap_area::{ HeapArea, FieldValue, ObjectReference, CompartmentEntry }; use crate::iterators::{ ClassMethodIterator, ClassFieldIterator }; use crate::native_methods; @@ -259,7 +260,12 @@ impl JVM { ConstantPoolInfo::Class(ConstantClassInfo { name_index: 18 }), ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/System".to_string() }), ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 20, descriptor_index: 13 }), - ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "initPhase1".to_string() }), + ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "initPhase1".to_string() }), // 20 + ConstantPoolInfo::InterfaceMethodRef(ConstantInterfaceMethodRefInfo { class_index: 22, name_and_type_index: 24}), + ConstantPoolInfo::Class(ConstantClassInfo { name_index: 23 }), + ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/Throwable".to_string() }), + ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 25, descriptor_index: 13 }), + ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "printStackTrace".to_string() }), // 25 ] ), access_flags: ClassAccessFlagMask { mask: ClassAccessFlag::Super.discriminant() }, @@ -309,9 +315,24 @@ impl JVM { 0xB8_u8.to_be(), // invokestatic 0x04_u16.to_be_bytes()[0], // index 4 into the constant 0x04_u16.to_be_bytes()[1], // pool + + 0xB1_u8.to_be(), // returnvoid + // index 14 + + 0xB9_u8.to_be(), // invokeinterface + 21_u16.to_be_bytes()[0], // index 21 constant + 21_u16.to_be_bytes()[1], // index 21 constant + 0x00_u8.to_be(), // constant 0 ]), }, - exception_table: Box::new([]), + exception_table: Box::new([ + ExceptionTableEntry { + start_pc: 0, + end_pc: u16::MAX, + handler_pc: 14, + catch_type: 0, // Catchall + } + ]), attributes: Box::new([]), } ) @@ -460,6 +481,43 @@ impl JVM { JVMCallbackOperation::MakeArrayClass(component_class_ref, component_descriptor) => { self.make_array_class(component_class_ref, component_descriptor); } + + JVMCallbackOperation::ThrowException(exception) => { + let mut is_handler_found = false; + while ! is_handler_found { + is_handler_found = { + let frame = { + let frame_index = self.stack_frames.len() - 1; + &mut self.stack_frames[frame_index] + }; + let class = self.class_store.class_file_from_idx(frame.class_index).unwrap(); + let method = &class.methods[frame.method_index as usize]; + if method.is_native() { + false + } else { + class.is_method_bytecode_protected(method, frame.instruction_pointer as u16, exception) + } + }; + + if ! is_handler_found { + self.stack_frames.pop(); + } + } + + if is_handler_found { + let frame = { + let frame_index = self.stack_frames.len() - 1; + &mut self.stack_frames[frame_index] + }; + let class = self.class_store.class_file_from_idx(frame.class_index).unwrap(); + let method = &class.methods[frame.method_index as usize]; + frame.operand_stack.clear(); + wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(exception)))?; + frame.instruction_pointer = method.get_protected_handler_pc(frame.instruction_pointer as u16).unwrap() as u32; + } else { + unreachable!() + } + } } } @@ -2281,7 +2339,10 @@ impl JVM { Instruction::ThrowException() => { let exception = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; if exception == ObjectReference::NULL { - + // TODO: Throw NullPointerException + } else { + // TODO: Check throwable instance + return Ok(JVMCallbackOperation::ThrowException(exception)); } } @@ -2312,6 +2373,7 @@ pub enum JVMCallbackOperation { LoadClass(String), InitClass(String), MakeArrayClass(ObjectReference, AbstractTypeDescription), + ThrowException(ObjectReference), } fn load_local_reference(class: &JavaClassFile, method: &MethodInfo, frame: &mut StackFrame, index: usize) -> Result<(), Error> { diff --git a/src/stackframe.rs b/src/stackframe.rs index 2c31173..56b84cc 100644 --- a/src/stackframe.rs +++ b/src/stackframe.rs @@ -47,6 +47,10 @@ impl OperandStack { } } + pub fn clear(&mut self) -> () { + self.depth = 0 + } + pub fn push(&mut self, value: StackValue) -> Result<(), Error> { if self.depth as usize == self.stack.len() { return Err(Error::PushError(format!("Trying to push onto full operand stack, capacity: {}, value: {:?}", self.depth, value))) From 8e5b6bb2b831b422f7fa6ed5e25de4a2cabcc879 Mon Sep 17 00:00:00 2001 From: VegOwOtenks Date: Tue, 12 Nov 2024 14:34:42 +0100 Subject: [PATCH 2/2] Implemented Exception Throwing --- src/bytecode.rs | 2 + src/classfile.rs | 9 ++ src/jvm.rs | 18 +++- src/native_methods.rs | 187 ++++++++++++++++++++++++++++++++++++++++++ src/stackframe.rs | 17 ++-- 5 files changed, 224 insertions(+), 9 deletions(-) diff --git a/src/bytecode.rs b/src/bytecode.rs index c188b84..7e41d6a 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -102,6 +102,7 @@ impl Bytecode { 0x6D => (Instruction::DivideLong(), 1), 0x6E => (Instruction::DivideFloat(), 1), + 0x70 => (Instruction::ModuloInt(), 1), 0x74 => (Instruction::NegateInt(), 1), 0x75 => (Instruction::NegateLong(), 1), 0x78 => (Instruction::ArithmeticShiftIntLeft(), 1), @@ -386,6 +387,7 @@ pub enum Instruction { DivideLong() = 0x6D, // long division DivideFloat() = 0x6E, // float division + ModuloInt() = 0x70, // modulo NegateInt() = 0x74, // arithmetic negation NegateLong() = 0x75, // arithmetic negation ArithmeticShiftIntLeft() = 0x78, // shift int left, preserve sign diff --git a/src/classfile.rs b/src/classfile.rs index f6399f3..4ce88d9 100644 --- a/src/classfile.rs +++ b/src/classfile.rs @@ -375,6 +375,15 @@ impl JavaClassFile { return Ok((class_name, method_name, method_descriptor)); } + + pub fn gather_methodref_compatible(&self, methodref_index: u16) -> Result<(&String, &String, &String), Error> { + return match self.pool_entry(methodref_index) { + Ok(ConstantPoolInfo::MethodRef(_)) => self.gather_methodref(methodref_index), + Ok(ConstantPoolInfo::InterfaceMethodRef(_)) => self.gather_interfacemethodref(methodref_index), + Err(i) => Err(i), + _ => unreachable!() + } + } pub fn sourcefile(&self) -> Result, Error> { match (&self.attributes).into_iter() diff --git a/src/jvm.rs b/src/jvm.rs index 32ab7aa..558a734 100644 --- a/src/jvm.rs +++ b/src/jvm.rs @@ -1403,7 +1403,7 @@ impl JVM { }, Instruction::InvokeStatic(methodref_index) => { - let (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref(methodref_index)?; + let (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref_compatible(methodref_index)?; if ! self.class_store.have_class(supplied_class_name) { // rewind the bytecode offset, I'll need to execute this instruction again @@ -1842,6 +1842,15 @@ impl JVM { wrap_stackframe_error(class, method, frame.operand_stack.push_long(result))?; } + Instruction::ModuloInt() => { + let int_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; + let int_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; + + let modulo = int_2.wrapping_rem(int_1); + + wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(modulo)))?; + } + Instruction::NegateInt() => { let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let negated = -int; @@ -2020,6 +2029,7 @@ impl JVM { match expected_field_descriptor.as_str() { "Z" => FieldValue::Boolean(i != 0), "B" => FieldValue::Byte(i as i8), + "C" => FieldValue::Char(i as u16), _ => FieldValue::Int(i) } } @@ -2126,7 +2136,7 @@ impl JVM { _ => return Err(Error::OpcodeError(format!("Found opcode '{:?}' on method returning '{:?}'", instruction, method.descriptor.return_type))) } - let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; + let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int_compatible(0))?; return Ok(JVMCallbackOperation::ReturnFrame(FieldValue::Int(int))); } @@ -2429,8 +2439,8 @@ fn fill_arguments(class: &JavaClassFile, method: &MethodInfo, arguments: &mut Ve wrap_stackframe_error( class, method, - stack.pop_char(0) - )? as i32 + stack.pop_int_compatible(0) + )? ) ) }, diff --git a/src/native_methods.rs b/src/native_methods.rs index 350fb9d..f49ffd8 100644 --- a/src/native_methods.rs +++ b/src/native_methods.rs @@ -461,6 +461,14 @@ impl JavaLangThrowable { } } +struct JdkInternalMiscCDS {} + +impl JdkInternalMiscCDS { + fn get_cds_config_status(_jvm: &mut JVM) -> Result { + return Ok(JVMCallbackOperation::ReturnFrame(FieldValue::Int(0))); + } +} + struct JdkInternalMiscUnsafe {} impl JdkInternalMiscUnsafe { @@ -2949,6 +2957,185 @@ pub fn function_for(class_name: &str, m: &crate::classfile::MethodInfo) -> Resul }, JavaLangThrowable::fill_in_stacktrace ), + + ( + "java/lang/invoke/MethodHandle", + "invokeExact", + MethodDescriptor { + argument_types: Box::new([ + AbstractTypeDescription { array_level: 1, kind: AbstractTypeKind::Classname("java/lang/Object".to_string())}, + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/Object".to_string())}, + }, + todo_call + ), + + ( + "java/lang/invoke/MethodHandle", + "invoke", + MethodDescriptor { + argument_types: Box::new([ + AbstractTypeDescription { array_level: 1, kind: AbstractTypeKind::Classname("java/lang/Object".to_string())}, + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/Object".to_string())}, + }, + todo_call + ), + + ( + "java/lang/invoke/MethodHandle", + "invokeBasic", + MethodDescriptor { + argument_types: Box::new([ + AbstractTypeDescription { array_level: 1, kind: AbstractTypeKind::Classname("java/lang/Object".to_string())}, + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/Object".to_string())}, + }, + todo_call + ), + + ( + "java/lang/invoke/MethodHandle", + "linkToVirtual", + MethodDescriptor { + argument_types: Box::new([ + AbstractTypeDescription { array_level: 1, kind: AbstractTypeKind::Classname("java/lang/Object".to_string())}, + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/Object".to_string())}, + }, + todo_call + ), + + ( + "java/lang/invoke/MethodHandle", + "linkToStatic", + MethodDescriptor { + argument_types: Box::new([ + AbstractTypeDescription { array_level: 1, kind: AbstractTypeKind::Classname("java/lang/Object".to_string())}, + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/Object".to_string())}, + }, + todo_call + ), + + ( + "java/lang/invoke/MethodHandle", + "linkToSpecial", + MethodDescriptor { + argument_types: Box::new([ + AbstractTypeDescription { array_level: 1, kind: AbstractTypeKind::Classname("java/lang/Object".to_string())}, + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/Object".to_string())}, + }, + todo_call + ), + + ( + "java/lang/invoke/MethodHandle", + "linkToInterface", + MethodDescriptor { + argument_types: Box::new([ + AbstractTypeDescription { array_level: 1, kind: AbstractTypeKind::Classname("java/lang/Object".to_string())}, + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/Object".to_string())}, + }, + todo_call + ), + + ( + "java/lang/invoke/MethodHandle", + "linkToNative", + MethodDescriptor { + argument_types: Box::new([ + AbstractTypeDescription { array_level: 1, kind: AbstractTypeKind::Classname("java/lang/Object".to_string())}, + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/Object".to_string())}, + }, + todo_call + ), + + ( + "jdk/internal/misc/CDS", + "getCDSConfigStatus", + MethodDescriptor { + argument_types: Box::new([ + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Int() }, + }, + JdkInternalMiscCDS::get_cds_config_status + ), + + ( + "jdk/internal/misc/CDS", + "logLambdaFormInvoker", + MethodDescriptor { + argument_types: Box::new([ + AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/String".to_string())}, + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Void() }, + }, + todo_call + ), + + ( + "jdk/internal/misc/CDS", + "initializeFromArchive", + MethodDescriptor { + argument_types: Box::new([ + AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/Class".to_string())}, + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Void() }, + }, + ignore_call // TODO: idk + ), + + ( + "jdk/internal/misc/CDS", + "defineArchivedModules", + MethodDescriptor { + argument_types: Box::new([ + AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/ClassLoader".to_string())}, + AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/ClassLoader".to_string())}, + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Void() }, + }, + todo_call + ), + + ( + "jdk/internal/misc/CDS", + "getRandomSeedForDumping", + MethodDescriptor { + argument_types: Box::new([ + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Long() }, + }, + todo_call + ), + + ( + "jdk/internal/misc/CDS", + "dumpClassList", + MethodDescriptor { + argument_types: Box::new([ + AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/String".to_string())}, + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Void() }, + }, + todo_call + ), + + ( + "jdk/internal/misc/CDS", + "dumpDynamicArchive", + MethodDescriptor { + argument_types: Box::new([ + AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/String".to_string())}, + ]), + return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Void() }, + }, + todo_call + ), ]; for (classname, methodname, methoddescriptor, binding) in native_mappings { diff --git a/src/stackframe.rs b/src/stackframe.rs index 56b84cc..a18f82b 100644 --- a/src/stackframe.rs +++ b/src/stackframe.rs @@ -8,7 +8,7 @@ pub enum StackValue { Boolean(bool), Byte(i8), Char(u16), - Short(u16), + Short(i16), Int(i32), Float(f32), Reference(ObjectReference), @@ -102,9 +102,15 @@ impl OperandStack { self.push_long(l) } - _ => { - println!("{value:?}"); - todo!(); + FieldValue::Char(c) => { + self.push(StackValue::Char(c)) + } + + FieldValue::Short(s) => { + self.push(StackValue::Short(s)) + } + FieldValue::Double(d) => { + self.push_double(d) } } } @@ -191,7 +197,7 @@ impl OperandStack { } } - pub fn pop_short(&mut self, index: usize) -> Result { + pub fn pop_short(&mut self, index: usize) -> Result { let absolute_index = self.depth as usize - 1 - index; let value = self.stack[absolute_index]; self.depth -= 1; @@ -218,6 +224,7 @@ impl OperandStack { match value { StackValue::Int(i) => Ok(i), StackValue::Byte(b) => Ok(b as i32), + StackValue::Char(c) => Ok(c as i32), _ => Err(Error::LocalError(format!("Mismatched type at index {} of the function operand stack, expected Int-compatible but found '{:?}'", index, value))) } }