reads complete class file

This commit is contained in:
Sander Hautvast 2023-09-19 21:21:32 +02:00
commit ecf7158e13
8 changed files with 327 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
/.idea

7
Cargo.lock generated Normal file
View file

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "classfile_reader"
version = "0.1.0"

8
Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "classfile_reader"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

BIN
MetaField.class Normal file

Binary file not shown.

20
MetaField.java Normal file
View file

@ -0,0 +1,20 @@
package com.github.shautvast.reflective;
public class MetaField {
private final String name;
private final int modifiers;
public MetaField(String name, int modifiers) {
this.name = name;
this.modifiers = modifiers;
}
public String getName() {
return name;
}
public int getModifiers() {
return modifiers;
}
}

209
src/lib.rs Normal file
View file

@ -0,0 +1,209 @@
pub mod types;
use crate::types::{CpEntry, Class, Field, Attribute, Method};
pub fn get_class(bytecode: Vec<u8>) -> Option<Class> {
check_magic(&bytecode);
let constant_pool_count = get_u16(&bytecode, 8);
let mut index = 10;
let mut constant_pool: Vec<CpEntry> = vec![];
for _ in 0..constant_pool_count - 1 {
constant_pool.push(read_constant_pool_entry(&mut index, &bytecode));
}
let access_flags = get_u16(&bytecode, index);
let this_class = get_u16(&bytecode, index + 2);
let super_class = get_u16(&bytecode, index + 4);
let interfaces_count = get_u16(&bytecode, index + 6);
index += 8;
let mut interfaces = vec![];
for _ in 0..interfaces_count {
interfaces.push(get_u16(&bytecode, index));
index += 2;
}
let fields_count = get_u16(&bytecode, index);
index += 2;
let mut fields = vec![];
for _ in 0..fields_count {
fields.push(read_field(&mut index, &bytecode));
}
let methods_count = get_u16(&bytecode, index);
index += 2;
let mut methods = vec![];
for _ in 0..methods_count {
methods.push(read_method(&mut index, &bytecode));
}
let attributes_count = get_u16(&bytecode, index);
index += 2;
let mut attributes = vec![];
for _ in 0..attributes_count {
attributes.push(read_attribute(&bytecode, &mut index));
}
Some(Class {
minor_version: get_u16(&bytecode, 4),
major_version: get_u16(&bytecode, 6),
constant_pool,
access_flags,
this_class,
super_class,
interfaces,
fields,
methods,
attributes,
})
}
fn check_magic(bytecode: &Vec<u8>) {
if &bytecode[0..4] != [0xCA, 0xFE, 0xBA, 0xBE] {
panic!(); //must never happen
}
}
fn read_constant_pool_entry(index: &mut usize, bytecode: &Vec<u8>) -> CpEntry {
let tag = bytecode[*index];
match tag {
1 => {
let len = get_u16(bytecode, *index + 1) as usize;
let utf: Vec<u8> = Vec::from(&bytecode[*index + 3..*index + 3 + len]);
*index += len + 3;
CpEntry::Utf8(String::from_utf8(utf).unwrap())
}
3 => {
let value = get_i32(bytecode, *index + 1);
*index += 5;
CpEntry::Integer(value)
}
4 => {
let value = get_f32(bytecode, *index + 1);
*index += 5;
CpEntry::Float(value)
}
5 => {
let value = get_i64(bytecode, *index + 1);
*index += 9;
CpEntry::Long(value)
}
6 => {
let value = get_f64(bytecode, *index + 1);
*index += 9;
CpEntry::Double(value)
}
7 => {
let name_index = get_u16(bytecode, *index + 1);
*index += 3;
CpEntry::Class(name_index)
}
8 => {
let string_index = get_u16(bytecode, *index + 1);
*index += 3;
CpEntry::String(string_index)
}
9 => {
let class_index = get_u16(bytecode, *index + 1);
let name_and_type_index = get_u16(bytecode, *index + 3);
*index += 5;
CpEntry::Fieldref(class_index, name_and_type_index)
}
10 => {
let class_index = get_u16(bytecode, *index + 1);
let name_and_type_index = get_u16(bytecode, *index + 3);
*index += 5;
CpEntry::MethodRef(class_index, name_and_type_index)
}
11 => {
let class_index = get_u16(bytecode, *index + 1);
let name_and_type_index = get_u16(bytecode, *index + 3);
*index += 5;
CpEntry::InterfaceMethodref(class_index, name_and_type_index)
}
12 => {
let name_index = get_u16(bytecode, *index + 1);
let descriptor_index = get_u16(bytecode, *index + 3);
*index += 5;
CpEntry::NameAndType(name_index, descriptor_index)
}
_ => panic!()
}
}
fn read_field(index: &mut usize, bytecode: &Vec<u8>) -> Field {
let access_flags = get_u16(bytecode, *index);
let name_index = get_u16(bytecode, *index + 2);
let descriptor_index = get_u16(bytecode, *index + 4);
let attributes_count = get_u16(bytecode, *index + 6);
*index += 8;
let mut attributes = vec![];
for _ in 0..attributes_count {
attributes.push(read_attribute(bytecode, index));
}
Field {
access_flags,
name_index,
descriptor_index,
attributes_count,
attributes,
}
}
fn read_method(index: &mut usize, bytecode: &Vec<u8>) -> Method {
let access_flags = get_u16(bytecode, *index);
let name_index = get_u16(bytecode, *index + 2);
let descriptor_index = get_u16(bytecode, *index + 4);
let attributes_count = get_u16(bytecode, *index + 6);
*index += 8;
let mut attributes = vec![];
for _ in 0..attributes_count {
attributes.push(read_attribute(bytecode, index));
}
Method {
access_flags,
name_index,
descriptor_index,
attributes_count,
attributes,
}
}
fn read_attribute(bytecode: &Vec<u8>, index: &mut usize) -> Attribute {
let attribute_name_index = get_u16(bytecode, *index);
*index += 2;
let attribute_length = read_u32(bytecode, *index) as usize;
*index += 4;
let info: Vec<u8> = Vec::from(&bytecode[*index..*index + attribute_length]);
*index += attribute_length;
Attribute {
attribute_name_index,
info,
}
}
fn get_u16(data: &Vec<u8>, pos: usize) -> u16 {
u16::from_be_bytes(data[pos..pos + 2].try_into().expect("slice with incorrect length"))
}
fn get_i32(data: &Vec<u8>, pos: usize) -> i32 {
i32::from_be_bytes(data[pos..pos + 4].try_into().expect("slice with incorrect length"))
}
fn read_u32(data: &Vec<u8>, pos: usize) -> u32 {
u32::from_be_bytes(data[pos..pos + 4].try_into().expect("slice with incorrect length"))
}
fn get_f32(data: &Vec<u8>, pos: usize) -> f32 {
f32::from_be_bytes(data[pos..pos + 4].try_into().expect("slice with incorrect length"))
}
fn get_i64(data: &Vec<u8>, pos: usize) -> i64 {
i64::from_be_bytes(data[pos..pos + 8].try_into().expect("slice with incorrect length"))
}
fn get_f64(data: &Vec<u8>, pos: usize) -> f64 {
f64::from_be_bytes(data[pos..pos + 8].try_into().expect("slice with incorrect length"))
}

17
src/main.rs Normal file
View file

@ -0,0 +1,17 @@
use std::fs::{self, File};
use std::io::Read;
fn main() {
let bytecode = read_class_file("/Users/FJ19WK/RustroverProjects/classfile_reader/MetaField.class");
if let Some(class) = classfile_reader::get_class(bytecode){
println!("{:?}", class);
}
}
fn read_class_file(name: &str) -> Vec<u8> {
let mut f = File::open(name).expect("no file found");
let metadata = fs::metadata(name).expect("unable to read metadata");
let mut buffer = vec![0; metadata.len() as usize];
f.read(&mut buffer).expect("buffer overflow");
buffer
}

64
src/types.rs Normal file
View file

@ -0,0 +1,64 @@
#[derive(Debug)]
//TODO create factory function
pub struct Class {
pub minor_version: u16,
pub major_version: u16,
pub constant_pool: Vec<CpEntry>,
pub access_flags: u16,
pub this_class: u16,
pub super_class: u16,
pub interfaces: Vec<u16>,
pub fields: Vec<Field>,
pub methods: Vec<Method>,
pub attributes: Vec<Attribute>,
}
#[derive(Debug)]
pub enum CpEntry {
Utf8(String),
//1
Integer(i32),
//3
Float(f32),
//4
Long(i64),
//5
Double(f64),
//6
Class(u16),
//7
String(u16),
//8
Fieldref(u16, u16),
//9
MethodRef(u16, u16),
//10
InterfaceMethodref(u16, u16),
//11
NameAndType(u16, u16), //12
}
#[derive(Debug)]
pub struct Field {
pub access_flags: u16,
pub name_index: u16,
pub descriptor_index: u16,
pub attributes_count: u16,
pub attributes: Vec<Attribute>,
}
#[derive(Debug)]
pub struct Attribute {
pub attribute_name_index: u16,
pub info: Vec<u8>,
}
#[derive(Debug)]
pub struct Method{
pub access_flags: u16,
pub name_index: u16,
pub descriptor_index: u16,
pub attributes_count: u16,
pub attributes: Vec<Attribute>,
}