Compare commits

...

3 Commits

Author SHA1 Message Date
Benedek László 5fc36bc7f7 instruction init 2024-05-16 19:01:49 +02:00
Benedek László 0def8039e1 printing methods 2024-04-13 22:47:56 +02:00
Benedek László af118981c4 BootstrapMethods and printing 2024-04-13 22:25:38 +02:00
5 changed files with 322 additions and 121 deletions

View File

@ -1,4 +1,5 @@
#include "classfile.h"
#include "instruction.h"
#include <assert.h>
#include <memory.h>
#include <stddef.h>
@ -63,11 +64,28 @@ stack_frame_type stack_frame_type_enum(u2 frame_type) {
}
}
ATTRIBUTE_TAGS attribute_tag(char* name) {
for (size_t i = 1; i < ATTRIBUTE_TAGS_END; i++) {
if (strcmp(ATTRIBUTE_TAGS_STRINGS[i - 1], name) == 0)
return (ATTRIBUTE_TAGS)i;
}
return UNKNOWN;
}
char* constant_to_string(ClassFile* cf, cp_info info) {}
void parse_attribute(ClassFile* cf, attribute_info* base, FILE* file) {
char* name = (char*)cf->constant_pool[base->attribute_name_index - 1].info.utf8_info.bytes;
if (strcmp("ConstantValue", name) == 0) {
base->_tag = attribute_tag(name);
switch (base->_tag) {
case UNKNOWN:
printf("unhandled attribute type: %s\n", name);
fseek(file, base->attribute_length, SEEK_CUR);
break;
case CONSTANT_VALUE:
read_u2(&(base->info.constant_value_attribute.constantvalue_index), 1, file);
} else if (strcmp("Code", name) == 0) {
break;
case CODE:
read_u2(&(base->info.code_attribute.max_stack), 2, file);
read_u4(&(base->info.code_attribute.code_length), 1, file);
base->info.code_attribute.code = (u1*)malloc(base->info.code_attribute.code_length);
@ -85,7 +103,8 @@ void parse_attribute(ClassFile* cf, attribute_info* base, FILE* file) {
read_u4(&(attr->attribute_length), 1, file);
parse_attribute(cf, attr, file);
}
} else if (strcmp("StackMapTable", name) == 0) {
break;
case STACK_MAP_TABLE:
read_u2(&(base->info.stack_map_table_attribute.number_of_entries), 1, file);
base->info.stack_map_table_attribute.entries = malloc(sizeof(stack_map_frame) * base->info.stack_map_table_attribute.number_of_entries);
for (u2 i = 0; i < base->info.stack_map_table_attribute.number_of_entries; i++) {
@ -156,19 +175,36 @@ void parse_attribute(ClassFile* cf, attribute_info* base, FILE* file) {
break;
}
}
} else if (strcmp("NestHost", name) == 0) {
break;
case BOOTSTRAP_METHODS: {
BootstrapMethods_attribute* attr = &base->info.bootstrap_methods_attribute;
read_u2(&(attr->num_bootstrap_methods), 1, file);
attr->bootstrap_methods = malloc(sizeof(BootstrapMethod) * attr->num_bootstrap_methods);
for (u2 i = 0; i < attr->num_bootstrap_methods; i++) {
BootstrapMethod* method = &attr->bootstrap_methods[i];
read_u2(&(method->bootstrap_method_ref), 2, file);
method->bootstrap_arguments = malloc(sizeof(u2) * method->num_bootstrap_arguments);
read_u2(method->bootstrap_arguments, method->num_bootstrap_arguments, file);
}
break;
}
case NEST_HOST:
read_u2(&(base->info.nest_host_attribute.host_class_index), 1, file);
} else if (strcmp("NestMembers", name) == 0) {
break;
case NEST_MEMBERS:
read_u2(&(base->info.nest_members_attribute.number_of_classes), 1, file);
base->info.nest_members_attribute.classes = malloc(sizeof(u2) * base->info.nest_members_attribute.number_of_classes);
read_u2(base->info.nest_members_attribute.classes, base->info.nest_members_attribute.number_of_classes, file);
} else if (strcmp("PermittedSubclasses", name) == 0) {
break;
case PERMITTED_SUBCLASSES:
read_u2(&(base->info.permitted_subclasses_attribute.number_of_classes), 1, file);
base->info.permitted_subclasses_attribute.classes = malloc(sizeof(u2) * base->info.permitted_subclasses_attribute.number_of_classes);
read_u2(base->info.permitted_subclasses_attribute.classes, base->info.permitted_subclasses_attribute.number_of_classes, file);
} else {
printf("unhandled attribute type: %s\n", name);
fseek(file, base->attribute_length, SEEK_CUR);
break;
case ATTRIBUTE_TAGS_END:
assert(0);
}
}
@ -324,18 +360,18 @@ ClassFile* ClassFile_load(const char* path) {
}
void ClassFile_info(const ClassFile* cf) {
printf(
"magic=%X\n"
"minor_version=%hu\n"
"major_version=%hu\n"
"constant_pool_count=%hu\n"
"access_flags=%hx\n"
"this_class=%hu\n"
"super_class=%hu\n"
"interfaces_count=%hu\n"
"fields_count=%hu\n"
"methods_count=%hu\n"
"attributes_count=%hu\n",
printf("Metadata:\n"
"\tmagic=%X\n"
"\tminor_version=%hu\n"
"\tmajor_version=%hu\n"
"\tconstant_pool_count=%hu\n"
"\taccess_flags=%hx\n"
"\tthis_class=%hu\n"
"\tsuper_class=%hu\n"
"\tinterfaces_count=%hu\n"
"\tfields_count=%hu\n"
"\tmethods_count=%hu\n"
"\tattributes_count=%hu\n\n",
cf->magic, cf->minor_version, cf->major_version, cf->constant_pool_count, cf->access_flags, cf->this_class, cf->super_class, cf->interfaces_count,
cf->fields_count, cf->methods_count, cf->attributes_count);
@ -362,10 +398,12 @@ void ClassFile_info(const ClassFile* cf) {
case CONSTANT_Long:
merged = merge_bytes(cf->constant_pool[i].info.long_info.high_bytes, cf->constant_pool[i].info.long_info.low_bytes);
printf("\t%ld\n", *(long*)&merged);
i++;
break;
case CONSTANT_Double:
merged = merge_bytes(cf->constant_pool[i].info.double_info.high_bytes, cf->constant_pool[i].info.double_info.low_bytes);
printf("\t%lf\n", *(double*)&merged);
i++;
break;
case CONSTANT_Class:
printf("\t#%hu\t\t\t// %s\n", cf->constant_pool[i].info.class_info.name_index,
@ -389,15 +427,69 @@ void ClassFile_info(const ClassFile* cf) {
buffer);
free(buffer);
default:
putchar('\n');
printf("\n");
}
}
// class
char* this_name = (char*)cf->constant_pool[cf->constant_pool[cf->this_class - 1].info.class_info.name_index - 1].info.utf8_info.bytes;
char* super_name =
cf->super_class != 0 ? (char*)cf->constant_pool[cf->constant_pool[cf->super_class - 1].info.class_info.name_index - 1].info.utf8_info.bytes : NULL;
char* type = (cf->access_flags & ACC_INTERFACE ? "interface" : (cf->access_flags & ACC_ENUM ? "enum" : (cf->access_flags & ACC_MODULE ? "module" : "class")));
printf("\n%s%s%s%s %s%s%s ", cf->access_flags & ACC_PUBLIC ? "public " : "", cf->access_flags & ACC_FINAL ? "final " : "",
cf->access_flags & ACC_ABSTRACT ? "abstract " : "", type, this_name, super_name ? " extends " : "", super_name ? super_name : "");
// interfaces
if (cf->interfaces_count > 0)
printf("implements ");
for (u2 i = 0; i < cf->interfaces_count; i++) {
printf("%s ", cf->constant_pool[cf->interfaces[i] - 1].info.utf8_info.bytes);
}
printf("{\n");
// fields
for (u2 i = 0; i < cf->fields_count; i++) {
u2 flags = cf->fields[i].access_flags;
char* access = flags & ACC_PUBLIC ? "public" : flags & ACC_PRIVATE ? "private" : flags & ACC_PROTECTED ? "protected" : "";
char* descriptor = (char*)cf->constant_pool[cf->fields[i].descriptor_index - 1].info.utf8_info.bytes;
char* name = (char*)cf->constant_pool[cf->fields[i].name_index - 1].info.utf8_info.bytes;
printf("\t%s %s%s%s%s %s", access, flags & ACC_STATIC ? "static " : "", flags & ACC_FINAL ? "final " : "", flags & ACC_VOLATLE ? "volatile " : "",
descriptor, name);
// TODO: print field attributes
for (u2 j = 0; j < cf->fields[i].attributes_count; j++) {
}
printf(";\n");
}
// methods
for (u2 i = 0; i < cf->methods_count; i++) {
u2 flags = cf->methods[i].access_flags;
char* access = flags & ACC_PUBLIC ? "public" : flags & ACC_PRIVATE ? "private" : flags & ACC_PROTECTED ? "protected" : "";
char* descriptor = (char*)cf->constant_pool[cf->methods[i].descriptor_index - 1].info.utf8_info.bytes;
char* name = (char*)cf->constant_pool[cf->methods[i].name_index - 1].info.utf8_info.bytes;
printf("\t%s %s%s%s%s %s", access, flags & ACC_STATIC ? "static " : "", flags & ACC_FINAL ? "final " : "", flags & ACC_VOLATLE ? "volatile " : "",
descriptor, name);
// TODO: print field attributes
for (u2 j = 0; j < cf->methods[i].attributes_count; j++) {
}
printf("{\n");
// code
for (u2 j = 0; j < cf->methods[i].attributes_count; j++) {
if (cf->methods[i].attributes[j]._tag == CODE) {
Instruction* code = Instruction_parse(cf->methods[i].attributes[j].info.code_attribute.code_length, cf->methods[i].attributes[j].info.code_attribute.code);
Instruction_print_code(code);
// TODO: free code
}
}
printf("\t}\n\n");
}
}
char* ClassFile_resolve_NameAnyType(const ClassFile* cf, CONSTANT_NameAndType_info info) {

View File

@ -8,6 +8,7 @@
typedef uint8_t u1;
typedef uint16_t u2;
typedef uint32_t u4;
typedef uint64_t u8;
typedef enum {
ACC_PUBLIC = 0x0001,
@ -261,12 +262,14 @@ typedef struct {
} StackMapTable_attribute;
typedef struct {
u2 num_bootstrap_methods;
struct {
u2 bootstrap_method_ref;
u2 num_bootstrap_arguments;
u2* bootstrap_arguments;
} * bootstrap_methods;
} BootstrapMethod;
typedef struct {
u2 num_bootstrap_methods;
BootstrapMethod* bootstrap_methods;
} BootstrapMethods_attribute;
typedef struct {
@ -283,9 +286,26 @@ typedef struct {
u2* classes;
} PermittedSubclasses_attribute;
typedef enum {
UNKNOWN,
CONSTANT_VALUE,
CODE,
STACK_MAP_TABLE,
BOOTSTRAP_METHODS,
NEST_HOST,
NEST_MEMBERS,
PERMITTED_SUBCLASSES,
ATTRIBUTE_TAGS_END
} ATTRIBUTE_TAGS;
static const char* ATTRIBUTE_TAGS_STRINGS[] = {
"ConstantValue", "Code", "StackMapTable", "BootstrapMethods", "NestHost", "NestMembers", "PermittedSubclasses",
};
typedef struct {
u2 attribute_name_index;
u4 attribute_length;
ATTRIBUTE_TAGS _tag;
union {
ConstantValue_attribute constant_value_attribute;
Code_attribute code_attribute;

65
instruction.c Normal file
View File

@ -0,0 +1,65 @@
#include "instruction.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "classfile.h"
Instruction* Instruction_parse(uint32_t bytes_length, uint8_t* bytes) {
Instruction* start = (Instruction*)malloc(sizeof(Instruction));
Instruction* current = start;
u4 i = 0, n = 0;
while (i < bytes_length) {
current->opcode = bytes[i++];
switch (current->opcode) {
case ALOAD_0:
case RETURN:
current->operands = NULL;
break;
case LDC:
*(u1*)&current->operands = bytes[i++];
break;
case GETSTATIC:
case INVOKESPECIAL:
case INVOKEVIRTUAL:
// current->operands = malloc(sizeof(u2));
*(u2*)&current->operands = (*(u2*)&bytes[i] << 8) | bytes[i + 1];
i += 2;
break;
}
if (i < bytes_length) {
current->next = (Instruction*)malloc(sizeof(Instruction));
current = current->next;
} else {
current->next = NULL;
}
}
return start;
}
void Instruction_print_code(Instruction* code) {
u4 i = 0;
while (code) {
printf("\t\t%hx ", (u1)code[i].opcode);
switch (code[i].opcode) {
case ALOAD_0:
case RETURN:
putchar('\n');
break;
case LDC:
printf("#%hhu\n", *(u1*)&code[i].operands);
break;
case GETSTATIC:
case INVOKESPECIAL:
case INVOKEVIRTUAL:
printf("#%hu\n", *(u2*)&code[i].operands);
break;
}
code = code->next;
}
}

24
instruction.h Normal file
View File

@ -0,0 +1,24 @@
#include <stddef.h>
#include <stdint.h>
#pragma pack(1)
typedef enum {
ALOAD_0 = 0x2a,
INVOKESPECIAL = 0xb7,
RETURN = 0xb1,
GETSTATIC = 0xb2,
LDC = 0x12,
INVOKEVIRTUAL = 0xb6,
} OPCODE;
typedef struct Instruction {
OPCODE opcode;
uint8_t* operands;
struct Instruction* next;
} Instruction;
Instruction* Instruction_parse(uint32_t bytes_length, uint8_t* bytes);
void Instruction_print_code(Instruction* code);
#pragma pack()

2
main.c
View File

@ -1,6 +1,6 @@
#include "classfile.h"
int main(int argc, char **argv) {
ClassFile *cf = ClassFile_load("test/Main.class");
ClassFile *cf = ClassFile_load("Main.class");
ClassFile_info(cf);
}