From bc88c9c62ee3bacf9ae32675cb468791182d45bd Mon Sep 17 00:00:00 2001 From: Robert Pengelly Date: Fri, 26 Sep 2025 20:11:06 +0100 Subject: [PATCH] Initial commit --- LICENSE | 24 ++ Makefile.unix | 28 ++ Makefile.w32 | 19 ++ README.md | 23 ++ class.c | 352 ++++++++++++++++++++ class.h | 43 +++ constant.c | 795 ++++++++++++++++++++++++++++++++++++++++++++++ constant.h | 193 +++++++++++ cstr.c | 69 ++++ cstr.h | 20 ++ field.c | 69 ++++ hashtab.c | 217 +++++++++++++ hashtab.h | 36 +++ interface.c | 32 ++ java_lib.c | 76 +++++ jvm.c | 124 ++++++++ jvm.h | 25 ++ lib.c | 295 +++++++++++++++++ lib.h | 17 + method.c | 208 ++++++++++++ method.h | 43 +++ report.c | 150 +++++++++ report.h | 29 ++ stack.c | 100 ++++++ stack.h | 42 +++ vector.c | 54 ++++ vector.h | 19 ++ vm.c | 864 ++++++++++++++++++++++++++++++++++++++++++++++++++ vm.h | 26 ++ 29 files changed, 3992 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile.unix create mode 100644 Makefile.w32 create mode 100644 README.md create mode 100644 class.c create mode 100644 class.h create mode 100644 constant.c create mode 100644 constant.h create mode 100755 cstr.c create mode 100755 cstr.h create mode 100644 field.c create mode 100755 hashtab.c create mode 100755 hashtab.h create mode 100644 interface.c create mode 100644 java_lib.c create mode 100644 jvm.c create mode 100755 jvm.h create mode 100755 lib.c create mode 100755 lib.h create mode 100644 method.c create mode 100644 method.h create mode 100644 report.c create mode 100644 report.h create mode 100644 stack.c create mode 100644 stack.h create mode 100755 vector.c create mode 100755 vector.h create mode 100644 vm.c create mode 100644 vm.h diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/Makefile.unix b/Makefile.unix new file mode 100644 index 0000000..36fb29f --- /dev/null +++ b/Makefile.unix @@ -0,0 +1,28 @@ +#****************************************************************************** +# @file Makefile.unix +#****************************************************************************** +SRCDIR ?= $(CURDIR) + +CC := gcc +CFLAGS := -D_FILE_OFFSET_BITS=64 -Wall -Werror -Wextra -std=c90 + +CSRC := $(shell find $(SRCDIR) -type f -name '*.c') + +ifeq ($(OS), Windows_NT) +all: lightjvm.exe + +lightjvm.exe: $(CSRC) + + $(CC) $(CFLAGS) -o $@ $^ +else +all: lightjvm + +lightjvm: $(CSRC) + + $(CC) $(CFLAGS) -o $@ $^ +endif + +clean: + + if [ -f lightjvm.exe ]; then rm -rf lightjvm.exe; fi + if [ -f lightjvm ]; then rm -rf lightjvm; fi diff --git a/Makefile.w32 b/Makefile.w32 new file mode 100644 index 0000000..582d6c8 --- /dev/null +++ b/Makefile.w32 @@ -0,0 +1,19 @@ +#****************************************************************************** +# @file Makefile.w32 +#****************************************************************************** +SRCDIR ?= $(CURDIR) + +CC := gcc +CFLAGS := -D_FILE_OFFSET_BITS=64 -Wall -Werror -Wextra -std=c90 + +CSRC := $(subst \,/,$(shell dir /s /b "$(SRCDIR)\\*.c")) + +all: lightjvm.exe + +clean: + + if exist lightjvm.exe ( del /q lightjvm.exe ) + if exist lightjvm ( del /q lightjvm ) + +lightjvm.exe: $(CSRC) + $(CC) $(CFLAGS) -o $@ $^ diff --git a/README.md b/README.md new file mode 100644 index 0000000..7ffb46d --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +All source code is Public Domain. + +## Obtain the source code + + git clone https://git.candlhat.org/lightjvm.git + +## Building + + BSD: + + Make sure you have gcc and gmake installed then run gmake -f Makefile.unix. + + Linux: + + Make sure you have gcc and make installed then run make -f Makefile.unix. + + macOS: + + Make sure you have xcode command line tools installed then run make -f Makefile.unix. + + Windows: + + Make sure you have mingw installed and the location within your PATH variable then run mingw32-make.exe -f Makefile.w32. diff --git a/class.c b/class.c new file mode 100644 index 0000000..7b2c15a --- /dev/null +++ b/class.c @@ -0,0 +1,352 @@ +/****************************************************************************** + * @file class.c + *****************************************************************************/ +#include +#include +#include + +#include "class.h" +#include "constant.h" +#include "hashtab.h" +#include "jvm.h" +#include "lib.h" +#include "method.h" +#include "report.h" + +static struct hashtab hashtab_class_names = { 0 }; + +struct java_class *parse_java_class (const char *class_name) { + + unsigned long magic; + long i; + + struct java_class *java_class; + struct hashtab_name *key; + + FILE *fp = 0; + + struct class_file_format *cff; + char *path; + + struct vector *method_pool; + void *constant_pool; + + if ((key = hashtab_get_key (&hashtab_class_names, class_name))) { + return hashtab_get (&hashtab_class_names, key); + } + + path = xmalloc (strlen (class_name) + 7); + sprintf (path, "%s.class", class_name); + + if (!(fp = fopen (path, "r+b"))) { + + free (path); + + for (i = 0; i < state->nb_class_paths; i++) { + + path = xmalloc (strlen (state->class_paths[i]) + 1 + strlen (class_name) + 7); +#if defined (unix) || defined (__unix) || defined (__unix__) || defined (__APPLE__) + sprintf (path, "%s/%s.class", state->class_paths[i], class_name); +#elif defined (_WIN32) + sprintf (path, "%s\\%s.class", state->class_paths[i], class_name); +#endif + + if (!(fp = fopen (path, "r+b"))) { + + free (path); + continue; + + } + + break; + + } + + } + + if (!fp) { + + report_at (program_name, 0, REPORT_ERROR, "failed to open class '%s'", class_name); + return 0; + + } + + if (!(java_class = malloc (sizeof (*java_class)))) { + + report_at (program_name, 0, REPORT_ERROR, "not enough memory for the java class"); + + free (path); + fclose (fp); + + return 0; + + } + + cff = &java_class->cff; + + if (fread (cff->magic_number, 1, 4, fp) != 4) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", path); + + free (java_class); + free (path); + + fclose (fp); + return 0; + + } + + if (((magic = array_to_integer (cff->magic_number, 4, 1))) != 0xCAFEBABE) { + + report_at (program_name, 0, REPORT_ERROR, "%s is not a valid .class (%04x)", path, magic); + + free (java_class); + free (path); + + fclose (fp); + return 0; + + } + + if (fread (cff->minor_version, 1, 2, fp) != 2) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", path); + + free (java_class); + free (path); + + fclose (fp); + return 0; + + } + + if (fread (cff->major_version, 1, 2, fp) != 2) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", path); + + free (java_class); + free (path); + + fclose (fp); + return 0; + + } + + if (array_to_integer (cff->major_version, 2, 1) > 52) { + + report_at (program_name, 0, REPORT_ERROR, "currently only up to Java 8 works"); + + free (java_class); + free (path); + + fclose (fp); + return 0; + + } + + if (fread (cff->constant_pool_count, 1, 2, fp) != 2) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", path); + + free (java_class); + free (path); + + fclose (fp); + return 0; + + } + + if (!(constant_pool = parse_constant_pool (fp, array_to_integer (cff->constant_pool_count, 2, 1)))) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", path); + + free (java_class); + free (path); + + fclose (fp); + return 0; + + } + + if (fread (cff->access_flags, 1, 2, fp) != 2) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", path); + + free_constant_pool (constant_pool); + free (constant_pool); + + free (java_class); + free (path); + + fclose (fp); + return 0; + + } + + if (fread (cff->this_class, 1, 2, fp) != 2) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", path); + + free_constant_pool (constant_pool); + free (constant_pool); + + free (java_class); + free (path); + + fclose (fp); + return 0; + + } + + if (fread (cff->super_class, 1, 2, fp) != 2) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", path); + + free_constant_pool (constant_pool); + free (constant_pool); + + free (java_class); + free (path); + + fclose (fp); + return 0; + + } + + if (fread (cff->interface_count, 1, 2, fp) != 2) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", path); + + free_constant_pool (constant_pool); + free (constant_pool); + + free (java_class); + free (path); + + fclose (fp); + return 0; + + } + + if (parse_interface_pool (fp, array_to_integer (cff->interface_count, 2, 1))) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", path); + + free_constant_pool (constant_pool); + free (constant_pool); + + free (java_class); + free (path); + + fclose (fp); + return 0; + + } + + if (fread (cff->fields_count, 1, 2, fp) != 2) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", path); + + free_constant_pool (constant_pool); + free (constant_pool); + + free (java_class); + free (path); + + fclose (fp); + return 0; + + } + + if (parse_field_pool (fp, array_to_integer (cff->fields_count, 2, 1))) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", path); + + free_constant_pool (constant_pool); + free (constant_pool); + + free (java_class); + free (path); + + fclose (fp); + return 0; + + } + + if (fread (cff->methods_count, 1, 2, fp) != 2) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", path); + + free_constant_pool (constant_pool); + free (constant_pool); + + free (java_class); + free (path); + + fclose (fp); + return 0; + + } + + if (!(method_pool = parse_method_pool (fp, array_to_integer (cff->methods_count, 2, 1)))) { + + report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", path); + + free_constant_pool (constant_pool); + free (constant_pool); + + free (java_class); + free (path); + + fclose (fp); + return 0; + + } + + free (path); + fclose (fp); + + java_class->class_name = xstrdup (class_name); + + java_class->constant_pool = constant_pool; + java_class->method_pool = method_pool; + + if ((key = hashtab_alloc_name (class_name))) { + hashtab_put (&hashtab_class_names, key, java_class); + } + + return java_class; + +} + +void free_classes (void) { + + struct hashtab_entry *entry; + struct java_class *class; + + int i; + + for (i = 0; i < hashtab_class_names.capacity; i++) { + + if ((entry = &hashtab_class_names.entries[i])) { + + if (entry->key && entry->value) { + + class = entry->value; + + free_constant_pool (class->constant_pool); + free (class->constant_pool); + + free_method_pool (class->method_pool); + free (class->method_pool); + + free (class); + hashtab_remove (&hashtab_class_names, entry->key); + + } + + } + + } + +} diff --git a/class.h b/class.h new file mode 100644 index 0000000..14a5010 --- /dev/null +++ b/class.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * @file class.h + *****************************************************************************/ +#ifndef _CLASS_H +#define _CLASS_H + +struct class_file_format { + + unsigned char magic_number[4]; + + unsigned char minor_version[2]; + unsigned char major_version[2]; + + unsigned char constant_pool_count[2]; + unsigned char access_flags[2]; + + unsigned char this_class[2]; + unsigned char super_class[2]; + + unsigned char interface_count[2]; + unsigned char fields_count[2]; + unsigned char methods_count[2]; + unsigned char attributes_count[2]; + +}; + +#include "constant.h" +#include "vector.h" + +struct java_class { + + const char *class_name; + struct class_file_format cff; + + struct constant_pool *constant_pool; + struct vector *method_pool; + +}; + +struct java_class *parse_java_class (const char *class_name); +void free_classes (void); + +#endif /* _CLASS_H */ diff --git a/constant.c b/constant.c new file mode 100644 index 0000000..af574aa --- /dev/null +++ b/constant.c @@ -0,0 +1,795 @@ +/****************************************************************************** + * @file java_constant_pool_parser.c + *****************************************************************************/ +#include +#include +#include + +#include "constant.h" +#include "jvm.h" +#include "lib.h" +#include "report.h" +#include "vector.h" + +#define CONSTANT_UTF8 1 +#define CONSTANT_INTEGER 3 +#define CONSTANT_FLOAT 4 +#define CONSTANT_LONG 5 +#define CONSTANT_DOUBLE 6 +#define CONSTANT_CLASS 7 +#define CONSTANT_STRING_REF 8 +#define CONSTANT_FIELD_REF 9 +#define CONSTANT_METHOD_REF 10 +#define CONSTANT_INTERFACE_REF 11 +#define CONSTANT_NAME_AND_TYPE 12 +#define CONSTANT_METHOD_HANDLER 15 +#define CONSTANT_INVOKE_DYNAMIC 18 + +static const int tag_additional_byte_size[13] = { 0, 2, 0, 4, 4, 8, 8, 2, 2, 4, 4, 4, 4 }; + +/* Parse UTF-8 String. */ +static int parse_string (FILE *fp, struct vector *constant_pool, int index) { + + struct constant_utf8 *ptr; + + if (!(ptr = malloc (sizeof (*ptr)))) { + return -1; + } + + ptr->tag = CONSTANT_UTF8; + ptr->index = index; + + if (fread (ptr->string_size, 1, 2, fp) != 2) { + + free (ptr); + return -1; + + } + + ptr->additional_byte_size = tag_additional_byte_size[CONSTANT_UTF8] + array_to_integer (ptr->string_size, 2, 1); + + if (!(ptr->ptr = malloc (array_to_integer (ptr->string_size, 2, 1) + 1))) { + + free (ptr); + return -1; + + } + + memset (ptr->ptr, 0, array_to_integer (ptr->string_size, 2, 1)); + + if (fread (ptr->ptr, 1, array_to_integer (ptr->string_size, 2, 1), fp) != array_to_integer (ptr->string_size, 2, 1)) { + + free (ptr->ptr); + free (ptr); + + return -1; + + } + + vec_push (constant_pool, ptr); + return 0; + +} + +static int parse_integer (FILE *fp, struct vector *constant_pool, int index) { + + struct constant_integer *ptr; + + if (!(ptr = malloc (sizeof (*ptr)))) { + return -1; + } + + ptr->tag = CONSTANT_INTEGER; + ptr->index = index; + + if (fread (ptr->value, 1, 4, fp) != 4) { + + free (ptr); + return -1; + + } + + ptr->additional_byte_size = tag_additional_byte_size[CONSTANT_INTEGER]; + vec_push (constant_pool, ptr); + + return 0; + +} + +static int parse_float (FILE *fp, struct vector *constant_pool, int index) { + + struct constant_float *ptr; + + if (!(ptr = malloc (sizeof (*ptr)))) { + return -1; + } + + ptr->tag = CONSTANT_FLOAT; + ptr->index = index; + + if (fread (ptr->value, 1, 4, fp) != 4) { + + free (ptr); + return -1; + + } + + ptr->additional_byte_size = tag_additional_byte_size[CONSTANT_FLOAT]; + vec_push (constant_pool, ptr); + + return 0; + +} + +static int parse_long (FILE *fp, struct vector *constant_pool, int index) { + + struct constant_long *ptr; + + if (!(ptr = malloc (sizeof (*ptr)))) { + return -1; + } + + ptr->tag = CONSTANT_LONG; + ptr->index = index; + + if (fread (ptr->value, 1, 8, fp) != 8) { + + free (ptr); + return -1; + + } + + ptr->additional_byte_size = tag_additional_byte_size[CONSTANT_LONG]; + vec_push (constant_pool, ptr); + + return 0; + +} + +static int parse_double (FILE *fp, struct vector *constant_pool, int index) { + + struct constant_double *ptr; + + if (!(ptr = malloc (sizeof (*ptr)))) { + return -1; + } + + ptr->tag = CONSTANT_DOUBLE; + ptr->index = index; + + if (fread (ptr->value, 1, 8, fp) != 8) { + + free (ptr); + return -1; + + } + + ptr->additional_byte_size = tag_additional_byte_size[CONSTANT_DOUBLE]; + vec_push (constant_pool, ptr); + + return 0; + +} + +static int parse_class_ref (FILE *fp, struct vector *constant_pool, int index) { + + struct constant_class_ref *ptr; + + if (!(ptr = malloc (sizeof (*ptr)))) { + return -1; + } + + ptr->tag = CONSTANT_CLASS; + ptr->index = index; + + if (fread (ptr->string_index, 1, 2, fp) != 2) { + + free (ptr); + return -1; + + } + + ptr->additional_byte_size = tag_additional_byte_size[CONSTANT_CLASS]; + vec_push (constant_pool, ptr); + + return 0; + +} + +static int parse_string_ref (FILE *fp, struct vector *constant_pool, int index) { + + struct constant_string_ref *ptr; + + if (!(ptr = malloc (sizeof (*ptr)))) { + return -1; + } + + ptr->tag = CONSTANT_STRING_REF; + ptr->index = index; + + if (fread (ptr->string_index, 1, 2, fp) != 2) { + + free (ptr); + return -1; + + } + + ptr->additional_byte_size = tag_additional_byte_size[CONSTANT_STRING_REF]; + vec_push (constant_pool, ptr); + + return 0; + +} + +static int parse_field_ref (FILE *fp, struct vector *constant_pool, int index) { + + struct constant_field_ref *ptr; + + if (!(ptr = malloc (sizeof (*ptr)))) { + return -1; + } + + ptr->tag = CONSTANT_FIELD_REF; + ptr->index = index; + + if (fread (ptr->class_index, 1, 2, fp) != 2) { + + free (ptr); + return -1; + + } + + ptr->additional_byte_size = tag_additional_byte_size[CONSTANT_FIELD_REF]; + + if (fread (ptr->nat_index, 1, 2, fp) != 2) { + + free (ptr); + return -1; + + } + + vec_push (constant_pool, ptr); + return 0; + +} + +static int parse_method_ref (FILE *fp, struct vector *constant_pool, int index) { + + struct constant_method_ref *ptr; + + if (!(ptr = malloc (sizeof (*ptr)))) { + return -1; + } + + ptr->tag = CONSTANT_METHOD_REF; + ptr->index = index; + + if (fread (ptr->class_index, 1, 2, fp) != 2) { + + free (ptr); + return -1; + + } + + ptr->additional_byte_size = tag_additional_byte_size[CONSTANT_METHOD_REF]; + + if (fread (ptr->nat_index, 1, 2, fp) != 2) { + + free (ptr); + return -1; + + } + + vec_push (constant_pool, ptr); + return 0; + +} + +static int parse_interface_ref (FILE *fp, struct vector *constant_pool, int index) { + + struct constant_interface_ref *ptr; + + if (!(ptr = malloc (sizeof (*ptr)))) { + return -1; + } + + ptr->tag = CONSTANT_INTERFACE_REF; + ptr->index = index; + + if (fread (ptr->class_index, 1, 2, fp) != 2) { + + free (ptr); + return -1; + + } + + ptr->additional_byte_size = tag_additional_byte_size[CONSTANT_INTERFACE_REF]; + + if (fread (ptr->nat_index, 1, 2, fp) != 2) { + + free (ptr); + return -1; + + } + + vec_push (constant_pool, ptr); + return 0; + +} + +static int parse_name_and_type (FILE *fp, struct vector *constant_pool, int index) { + + struct constant_nat *ptr; + + if (!(ptr = malloc (sizeof (*ptr)))) { + return -1; + } + + ptr->tag = CONSTANT_NAME_AND_TYPE; + ptr->index = index; + + if (fread (ptr->name_index, 1, 2, fp) != 2) { + + free (ptr); + return -1; + + } + + ptr->additional_byte_size = tag_additional_byte_size[CONSTANT_NAME_AND_TYPE]; + + if (fread (ptr->type_index, 1, 2, fp) != 2) { + + free (ptr); + return -1; + + } + + vec_push (constant_pool, ptr); + return 0; + +} + +static int parse_method_handler (FILE *fp, struct vector *constant_pool, int index) { + + struct constant_method_handler *ptr; + + if (!(ptr = malloc (sizeof (*ptr)))) { + return -1; + } + + ptr->tag = CONSTANT_METHOD_HANDLER; + ptr->index = index; + + if (fread (&ptr->reference_kind, 1, 1, fp) != 1) { + + free (ptr); + return -1; + + } + + /*ptr->additional_byte_size = tag_additional_byte_size[CONSTANT_INTERFACE_REF];*/ + + if (fread (ptr->reference_index, 1, 2, fp) != 2) { + + free (ptr); + return -1; + + } + + vec_push (constant_pool, ptr); + return 0; + +} + +static int parse_invoke_dynamic (FILE *fp, struct vector *constant_pool, int index) { + + struct constant_invoke_dynamic *ptr; + + if (!(ptr = malloc (sizeof (*ptr)))) { + return -1; + } + + ptr->tag = CONSTANT_INVOKE_DYNAMIC; + ptr->index = index; + + if (fread (ptr->bootstrap_method_attr_index, 1, 2, fp) != 2) { + + free (ptr); + return -1; + + } + + /*ptr->additional_byte_size = tag_additional_byte_size[CONSTANT_INTERFACE_REF];*/ + + if (fread (ptr->nat_index, 1, 2, fp) != 2) { + + free (ptr); + return -1; + + } + + vec_push (constant_pool, ptr); + return 0; + +} + +struct constant_pool *parse_constant_pool (FILE *fp, int count) { + + struct constant_pool *constant_pool; + + unsigned char tag = 0; + int ret, i; + + if (!(constant_pool = malloc (sizeof (*constant_pool)))) { + return 0; + } + + memset (constant_pool, 0, sizeof (*constant_pool)); + + for (i = 1; i < count; i++) { + + fread (&tag, 1, 1, fp); + + switch (tag) { + + case CONSTANT_UTF8: + + ret = parse_string (fp, &constant_pool->vec_strings, i); + break; + + case CONSTANT_INTEGER: + + ret = parse_integer (fp, &constant_pool->vec_integers, i); + break; + + case CONSTANT_FLOAT: + + ret = parse_float (fp, &constant_pool->vec_floats, i); + break; + + case CONSTANT_LONG: + + ret = parse_long (fp, &constant_pool->vec_longs, i); + + i++; + break; + + case CONSTANT_DOUBLE: + + ret = parse_double (fp, &constant_pool->vec_doubles, i); + + i++; + break; + + case CONSTANT_STRING_REF: + + ret = parse_string_ref (fp, &constant_pool->vec_string_refs, i); + break; + + case CONSTANT_CLASS: + + ret = parse_class_ref (fp, &constant_pool->vec_class_refs, i); + break; + + case CONSTANT_FIELD_REF: + + ret = parse_field_ref (fp, &constant_pool->vec_field_refs, i); + break; + + case CONSTANT_METHOD_REF: + + ret = parse_method_ref (fp, &constant_pool->vec_method_refs, i); + break; + + case CONSTANT_INTERFACE_REF: + + ret = parse_interface_ref (fp, &constant_pool->vec_interface_refs, i); + break; + + case CONSTANT_NAME_AND_TYPE: + + ret = parse_name_and_type (fp, &constant_pool->vec_nats, i); + break; + + case CONSTANT_METHOD_HANDLER: + + ret = parse_method_handler (fp, &constant_pool->vec_method_handlers, i); + break; + + case CONSTANT_INVOKE_DYNAMIC: + + ret = parse_invoke_dynamic (fp, &constant_pool->vec_invoke_dynamic, i); + break; + + default: + + report_at (program_name, 0, REPORT_ERROR, "unknown tag = 0x%02x", tag); + + free_constant_pool (constant_pool); + free (constant_pool); + + return 0; + + } + + if (ret) { + + free_constant_pool (constant_pool); + free (constant_pool); + + return 0; + + } + + } + + return constant_pool; + +} + +void free_constant_pool (struct constant_pool *constant_pool) { + + struct constant_utf8 *ptr; + int i; + + for (i = 0; i < constant_pool->vec_strings.length; i++) { + + if ((ptr = constant_pool->vec_strings.data[i])) { + + free (ptr->ptr); + free (ptr); + + } + + } + + for (i = 0; i < constant_pool->vec_integers.length; i++) { + free (constant_pool->vec_integers.data[i]); + } + + for (i = 0; i < constant_pool->vec_floats.length; i++) { + free (constant_pool->vec_floats.data[i]); + } + + for (i = 0; i < constant_pool->vec_longs.length; i++) { + free (constant_pool->vec_longs.data[i]); + } + + for (i = 0; i < constant_pool->vec_doubles.length; i++) { + free (constant_pool->vec_doubles.data[i]); + } + + for (i = 0; i < constant_pool->vec_class_refs.length; i++) { + free (constant_pool->vec_class_refs.data[i]); + } + + for (i = 0; i < constant_pool->vec_string_refs.length; i++) { + free (constant_pool->vec_string_refs.data[i]); + } + + for (i = 0; i < constant_pool->vec_field_refs.length; i++) { + free (constant_pool->vec_field_refs.data[i]); + } + + for (i = 0; i < constant_pool->vec_method_refs.length; i++) { + free (constant_pool->vec_method_refs.data[i]); + } + + for (i = 0; i < constant_pool->vec_interface_refs.length; i++) { + free (constant_pool->vec_interface_refs.data[i]); + } + + for (i = 0; i < constant_pool->vec_nats.length; i++) { + free (constant_pool->vec_nats.data[i]); + } + +} + + +struct constant_utf8 *find_utf8 (struct constant_pool *constant_pool, int index) { + + struct vector *arr = &constant_pool->vec_strings; + + struct constant_utf8 *p; + int i; + + for (i = 0; i < arr->length; i++) { + + if ((p = arr->data[i])) { + + if (p->index == index) { + return p; + } + + } + + } + + return 0; + +} + +struct constant_class_ref *find_class_ref (struct constant_pool *constant_pool, int index) { + + struct vector *arr = &constant_pool->vec_class_refs; + + struct constant_class_ref *p; + int i; + + for (i = 0; i < arr->length; i++) { + + if ((p = arr->data[i])) { + + if (p->index == index) { + return p; + } + + } + + } + + return 0; + +} + +struct constant_string_ref *find_string_ref (struct constant_pool *constant_pool, int index) { + + struct vector *arr = &constant_pool->vec_string_refs; + + struct constant_string_ref *p; + int i; + + for (i = 0; i < arr->length; i++) { + + if ((p = arr->data[i])) { + + if (p->index == index) { + return p; + } + + } + + } + + return 0; + +} + +struct constant_field_ref *find_field_ref (struct constant_pool *constant_pool, int index) { + + struct vector *arr = &constant_pool->vec_field_refs; + + struct constant_field_ref *p; + int i; + + for (i = 0; i < arr->length; i++) { + + if ((p = arr->data[i])) { + + if (p->index == index) { + return p; + } + + } + + } + + return 0; + +} + +struct constant_method_ref *find_method_ref (struct constant_pool *constant_pool, int index) { + + struct vector *arr = &constant_pool->vec_method_refs; + + struct constant_method_ref *p; + int i; + + for (i = 0; i < arr->length; i++) { + + if ((p = arr->data[i])) { + + if (p->index == index) { + return p; + } + + } + + } + + return 0; + +} + +struct constant_nat *find_nat (struct constant_pool *constant_pool, int index) { + + struct vector *arr = &constant_pool->vec_nats; + + struct constant_nat *p; + int i; + + for (i = 0; i < arr->length; i++) { + + if ((p = arr->data[i])) { + + if (p->index == index) { + return p; + } + + } + + } + + return 0; + +} + +struct constant_invoke_dynamic *find_invoke_dynamic (struct constant_pool *constant_pool, int index) { + + struct vector *arr = &constant_pool->vec_invoke_dynamic; + + struct constant_invoke_dynamic *p; + int i; + + for (i = 0; i < arr->length; i++) { + + if ((p = arr->data[i])) { + + if (p->index == index) { + return p; + } + + } + + } + + return 0; + +} + +double get_double_from_constant_pool (struct constant_pool *p, int index) { + + struct constant_double *entry; + unsigned char temp[8]; + + double value = 0; + int i, j, k; + + for (i = 0; i < p->vec_doubles.length; i++) { + + entry = p->vec_doubles.data[i]; + + if (entry->index == (index)) { + + for (j = 0, k = 7; j < 8; j++, k--) { + temp[k] = entry->value[j]; + } + + memcpy (&value, temp, 8); + return value; + + } + + } + + return 0.0f; + +} + +int get_utf8_string (struct constant_pool *p, int index, unsigned long size, char *out) { + + struct constant_utf8 *utf8 = find_utf8 (p, index); + unsigned long copy_size = 0; + + memset (out, 0, size); + + if (utf8 != 0) { + + copy_size = (array_to_integer (utf8->string_size, 2, 1) < size) ? array_to_integer (utf8->string_size, 2, 1) : size; + memcpy (out, utf8->ptr, copy_size); + + } + + return copy_size; + +} diff --git a/constant.h b/constant.h new file mode 100644 index 0000000..d36249b --- /dev/null +++ b/constant.h @@ -0,0 +1,193 @@ +/****************************************************************************** + * @file constant.h + *****************************************************************************/ +#ifndef _CONSTANT_H +#define _CONSTANT_H + +#include "vector.h" +struct constant_pool { + + struct vector vec_strings; + struct vector vec_integers; + struct vector vec_floats; + struct vector vec_longs; + struct vector vec_doubles; + struct vector vec_class_refs; + struct vector vec_string_refs; + struct vector vec_field_refs; + struct vector vec_method_refs; + struct vector vec_interface_refs; + struct vector vec_nats; + struct vector vec_method_handlers; + struct vector vec_invoke_dynamic; + +}; + +void free_constant_pool (struct constant_pool *constant_pool); + +#include +struct constant_pool *parse_constant_pool (FILE *fp, int count); + +struct constant_utf8 { + + unsigned char tag; + + int additional_byte_size; + int index; + + unsigned char string_size[2]; + unsigned char *ptr; + +}; + +struct constant_integer { + + unsigned char tag; + + int additional_byte_size; + int index; + + unsigned char value[4]; + +}; + +struct constant_float { + + unsigned char tag; + + int additional_byte_size; + int index; + + unsigned char value[4]; + +}; + +struct constant_long { + + unsigned char tag; + + int additional_byte_size; + int index; + + unsigned char value[8]; + +}; + +struct constant_double { + + unsigned char tag; + + int additional_byte_size; + int index; + + unsigned char value[8]; + +}; + +struct constant_class_ref { + + unsigned char tag; + + int additional_byte_size; + int index; + + unsigned char string_index[2]; + +}; + +struct constant_string_ref { + + unsigned char tag; + + int additional_byte_size; + int index; + + unsigned char string_index[2]; + +}; + +struct constant_field_ref { + + unsigned char tag; + + int additional_byte_size; + int index; + + unsigned char class_index[2]; + unsigned char nat_index[2]; + +}; + +struct constant_method_ref { + + unsigned char tag; + + int additional_byte_size; + int index; + + unsigned char class_index[2]; + unsigned char nat_index[2]; + +}; + +struct constant_interface_ref { + + unsigned char tag; + + int additional_byte_size; + int index; + + unsigned char class_index[2]; + unsigned char nat_index[2]; + +}; + +struct constant_nat { + + unsigned char tag; + + int additional_byte_size; + int index; + + unsigned char name_index[2]; + unsigned char type_index[2]; + +}; + +struct constant_method_handler { + + unsigned char tag; + + int additional_byte_size; + int index; + + unsigned char reference_kind; + unsigned char reference_index[2]; + +}; + +struct constant_invoke_dynamic { + + unsigned char tag; + + int additional_byte_size; + int index; + + unsigned char bootstrap_method_attr_index[2]; + unsigned char nat_index[2]; + +}; + + +double get_double_from_constant_pool (struct constant_pool *p, int index); +int get_utf8_string (struct constant_pool *p, int index, unsigned long size, char *out); + +struct constant_utf8 *find_utf8 (struct constant_pool *constant_pool, int index); +struct constant_class_ref *find_class_ref (struct constant_pool *constant_pool, int index); +struct constant_string_ref *find_string_ref (struct constant_pool *constant_pool, int index); +struct constant_field_ref *find_field_ref (struct constant_pool *constant_pool, int index); +struct constant_method_ref *find_method_ref (struct constant_pool *constant_pool, int index); +struct constant_nat *find_nat (struct constant_pool *constant_pool, int index); +struct constant_invoke_dynamic *find_invoke_dynamic (struct constant_pool *constant_pool, int index); + +#endif /* _CONSTANT_H */ diff --git a/cstr.c b/cstr.c new file mode 100755 index 0000000..d518f2e --- /dev/null +++ b/cstr.c @@ -0,0 +1,69 @@ +/****************************************************************************** + * @file cstr.c + *****************************************************************************/ +#include +#include + +#include "cstr.h" + +extern void *xrealloc (void *__ptr, unsigned int __size); + +static void cstr_realloc (struct cstring *cstr, int new_size) { + + int size = cstr->size_allocated; + + if (size < 8) { + size = 8; + } + + while (size < new_size) { + size *= 2; + } + + cstr->data = xrealloc (cstr->data, size); + cstr->size_allocated = size; + +} + +void cstr_ccat (struct cstring *cstr, int ch) { + + int size = cstr->size + 1; + + if (size > cstr->size_allocated) { + cstr_realloc (cstr, size); + } + + ((unsigned char *) cstr->data)[size - 1] = ch; + cstr->size = size; + +} + +void cstr_cat (struct cstring *cstr, const char *str, int len) { + + int size; + + if (len <= 0) { + len = strlen (str) + 1 + len; + } + + size = cstr->size + len; + + if (size > cstr->size_allocated) { + cstr_realloc (cstr, size); + } + + memmove (((unsigned char *) cstr->data) + cstr->size, str, len); + cstr->size = size; + +} + +void cstr_new (struct cstring *cstr) { + memset (cstr, 0, sizeof (struct cstring)); +} + +void cstr_free (struct cstring *cstr) { + + free (cstr->data); + cstr_new (cstr); + +} diff --git a/cstr.h b/cstr.h new file mode 100755 index 0000000..2736720 --- /dev/null +++ b/cstr.h @@ -0,0 +1,20 @@ +/****************************************************************************** + * @file cstr.h + *****************************************************************************/ +#ifndef _CSTR_H +#define _CSTR_H + +struct cstring { + + int size, size_allocated; + void *data; + +}; + +void cstr_ccat (struct cstring *cstr, int ch); +void cstr_cat (struct cstring *cstr, const char *str, int len); + +void cstr_new (struct cstring *cstr); +void cstr_free (struct cstring *cstr); + +#endif /* _CSTR_H */ diff --git a/field.c b/field.c new file mode 100644 index 0000000..fdbfa1a --- /dev/null +++ b/field.c @@ -0,0 +1,69 @@ +/****************************************************************************** + * @file java_field_pool_parser.c + *****************************************************************************/ +#include + +#include "lib.h" + +static int parse_attr (FILE *fp, int count) { + + unsigned char temp[4]; + int i; + + for (i = 0; i < count; i++) { + + if (fread (temp, 1, 2, fp) != 2) { + return -1; + } + + if (fread (temp, 1, 4, fp) != 2) { + return -1; + } + + fseek (fp, array_to_integer (temp, 4, 1), SEEK_CUR); + + } + + return 0; + +} + +static int parse (FILE *fp) { + + unsigned char temp[2]; + + if (fread (temp, 1, 2, fp) != 2) { + return -1; + } + + if (fread (temp, 1, 2, fp) != 2) { + return -1; + } + + if (fread (temp, 1, 2, fp) != 2) { + return -1; + } + + if (fread (temp, 1, 2, fp) != 2) { + return -1; + } + + return parse_attr (fp, array_to_integer (temp, 2, 1)); + +} + +int parse_field_pool (FILE *fp, int count) { + + int ret, i; + + for (i = 0; i < count; i++) { + + if ((ret = parse (fp))) { + return ret; + } + + } + + return 0; + +} diff --git a/hashtab.c b/hashtab.c new file mode 100755 index 0000000..328b622 --- /dev/null +++ b/hashtab.c @@ -0,0 +1,217 @@ +/****************************************************************************** + * @file hashtab.c + *****************************************************************************/ +#include +#include +#include + +#include "hashtab.h" + +static struct hashtab_entry *find_entry (struct hashtab_entry *entries, unsigned int capacity, struct hashtab_name *key); + +static int adjust_capacity (struct hashtab *table, unsigned int new_capacity) { + + struct hashtab_entry *new_entries, *old_entries; + unsigned int i, new_count, old_capacity; + + if ((new_entries = malloc (sizeof (*new_entries) * new_capacity)) == NULL) { + return -2; + } + + for (i = 0; i < new_capacity; i++) { + + struct hashtab_entry *entry = &new_entries[i]; + + entry->key = NULL; + entry->value = NULL; + + } + + old_entries = table->entries; + old_capacity = table->capacity; + + new_count = 0; + + for (i = 0; i < old_capacity; i++) { + + struct hashtab_entry *entry = &old_entries[i], *dest; + + if (entry->key == NULL) { + continue; + } + + dest = find_entry (new_entries, new_capacity, entry->key); + + dest->key = entry->key; + dest->value = entry->value; + + new_count++; + + } + + free (old_entries); + + table->capacity = new_capacity; + table->count = new_count; + table->entries = new_entries; + table->used = new_count; + + return 0; + +} + +static struct hashtab_entry *find_entry (struct hashtab_entry *entries, unsigned int capacity, struct hashtab_name *key) { + + struct hashtab_entry *tombstone = NULL; + unsigned int index; + + for (index = key->hash % capacity; ; index = (index + 1) % capacity) { + + struct hashtab_entry *entry = &entries[index]; + + if (entry->key == NULL) { + + if (entry->value == NULL) { + + if (tombstone == NULL) { + return entry; + } + + return tombstone; + + } else if (tombstone == NULL) { + tombstone = entry; + } + + } else if (entry->key->bytes == key->bytes) { + + if (memcmp (entry->key->chars, key->chars, key->bytes) == 0 && entry->key->hash == key->hash) { + return entry; + } + + } + + } + +} + +static unsigned int hash_string (const void *p, unsigned int length) { + + unsigned char *str = (unsigned char *) p; + unsigned int i; + + unsigned int result = 0; + + for (i = 0; i < length; i++) { + result = (((unsigned short) str[i]) << 4) + (result >> 9) + result + (result >> 3) + (((unsigned short) str[i]) << 2) - (result << 12); + } + + return result; + +} + +struct hashtab_name *hashtab_alloc_name (const char *str) { + + unsigned int bytes = strlen (str), hash = hash_string (str, bytes); + struct hashtab_name *name; + + if ((name = malloc (sizeof (*name))) == NULL) { + return NULL; + } + + name->bytes = bytes; + name->chars = str; + name->hash = hash; + + return name; + +} + +struct hashtab_name *hashtab_get_key (struct hashtab *table, const char *name) { + + struct hashtab_name *key; + struct hashtab_entry *entry; + + if (table == NULL || table->count == 0 || !(key = hashtab_alloc_name (name))) { + return 0; + } + + entry = find_entry (table->entries, table->capacity, key); + free (key); + + return entry->key; + +} + +void *hashtab_get (struct hashtab *table, struct hashtab_name *key) { + + struct hashtab_entry *entry; + + if (table == NULL || table->count == 0) { + return NULL; + } + + entry = find_entry (table->entries, table->capacity, key); + + if (entry->key == NULL) { + return NULL; + } + + return entry->value; + +} + +int hashtab_put (struct hashtab *table, struct hashtab_name *key, void *value) { + + const int MIN_CAPACITY = 15; + + struct hashtab_entry *entry; + int ret = 0; + + if (table->used >= table->capacity / 2) { + + int capacity = table->capacity * 2 - 1; + + if (capacity < MIN_CAPACITY) { + capacity = MIN_CAPACITY; + } + + if ((ret = adjust_capacity (table, capacity))) { + return ret; + } + + } + + entry = find_entry (table->entries, table->capacity, key); + + if (entry->key == NULL) { + + table->count++; + + if (entry->value == NULL) { + table->used++; + } + + } + + entry->key = key; + entry->value = value; + + return 0; + +} + +void hashtab_remove (struct hashtab *table, struct hashtab_name *key) { + + struct hashtab_entry *entry; + + if ((entry = find_entry (table->entries, table->capacity, key)) != NULL) { + + entry->key = NULL; + entry->value = NULL; + + --table->count; + + } + +} diff --git a/hashtab.h b/hashtab.h new file mode 100755 index 0000000..ed8e828 --- /dev/null +++ b/hashtab.h @@ -0,0 +1,36 @@ +/****************************************************************************** + * @file hashtab.h + *****************************************************************************/ +#ifndef _HASHTAB_H +#define _HASHTAB_H + +struct hashtab_name { + + const char *chars; + unsigned int bytes, hash; + +}; + +struct hashtab_entry { + + struct hashtab_name *key; + void *value; + +}; + +struct hashtab { + + struct hashtab_entry *entries; + int capacity, count, used; + +}; + +struct hashtab_name *hashtab_alloc_name (const char *str); +struct hashtab_name *hashtab_get_key (struct hashtab *table, const char *name); + +void *hashtab_get (struct hashtab *table, struct hashtab_name *key); + +int hashtab_put (struct hashtab *table, struct hashtab_name *key, void *value); +void hashtab_remove (struct hashtab *table, struct hashtab_name *key); + +#endif /* _HASHTAB_H */ diff --git a/interface.c b/interface.c new file mode 100644 index 0000000..3250e11 --- /dev/null +++ b/interface.c @@ -0,0 +1,32 @@ +/****************************************************************************** + * @file java_interface_pool_parser.c + *****************************************************************************/ +#include + +static int parse (FILE *fp) { + + unsigned char temp[2]; + + if (fread (temp, 1, 2, fp) != 2) { + return -1; + } + + return 0; + +} + +int parse_interface_pool (FILE *fp, int count) { + + int ret, i; + + for (i = 0; i < count; i++) { + + if ((ret = parse (fp))) { + return ret; + } + + } + + return 0; + +} diff --git a/java_lib.c b/java_lib.c new file mode 100644 index 0000000..38f1d05 --- /dev/null +++ b/java_lib.c @@ -0,0 +1,76 @@ +/****************************************************************************** + * @file java_lib.c + *****************************************************************************/ +#include +#include +#include + +#include "constant.h" +#include "stack.h" + +typedef int (*java_lang_lib) (struct constant_pool *p, struct stack_frame *stack); + +struct java_lang_method { + + char *class_name; + char *method_name; + + java_lang_lib runtime; + +}; + +static int java_lang_math_random (struct constant_pool *p, struct stack_frame *stack) { + + double r = 0.0f; + int times = 0, i; + + srand (time (0)); + times = rand () % 100; + + for (i = 0; i < times; i++) { + r = ((double) rand () / (double) RAND_MAX); + } + + push_double (stack, r); + + (void) p; + return 0; + +} + +static struct java_lang_method method_table[] = { + { "java/lang/Math", "random", java_lang_math_random } +}; + +static unsigned long java_lang_method_size = (sizeof (method_table) / sizeof (method_table[0])); + +static struct java_lang_method *find_java_lang_method (char *class_name, char *method_name) { + + struct java_lang_method *entry; + unsigned long i; + + for (i = 0; i < java_lang_method_size; i++) { + + entry = &method_table[i]; + + if (strcmp (entry->class_name, class_name) == 0 && strcmp (entry->method_name, method_name) == 0) { + return entry; + } + + } + + return 0; + +} + +int invoke_java_lang_library (struct constant_pool *p, struct stack_frame *stack, char *class_name, char *method_name) { + + struct java_lang_method *method; + + if ((method = find_java_lang_method (class_name, method_name))) { + return method->runtime (p, stack); + } + + return -1; + +} diff --git a/jvm.c b/jvm.c new file mode 100644 index 0000000..5e33265 --- /dev/null +++ b/jvm.c @@ -0,0 +1,124 @@ +/****************************************************************************** + * @file jvm.c + *****************************************************************************/ +#include +#include +#include + +#include "class.h" +#include "constant.h" +#include "jvm.h" +#include "lib.h" +#include "method.h" +#include "report.h" +#include "stack.h" +#include "vm.h" + +static struct stack_frame *stack_frame = 0; + +/*static FILE *fp = 0; + +static void cleanup (void) { + if (fp) { fclose (fp); } +}*/ + +struct jvm_state *state = 0; +const char *program_name = 0; + +int main (int argc, char **argv) { + + struct java_class *class; + struct method_info *main; + + long i; + + /*atexit (cleanup);*/ + + if (argc && *argv) { + + char *p; + program_name = *argv; + + if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) { + program_name = (p + 1); + } + + } + + state = xmalloc (sizeof (*state)); + parse_args (argc, argv, 1); + + if (state->nb_files == 0) { + + report_at (program_name, 0, REPORT_ERROR, "no input files provided"); + return EXIT_FAILURE; + + } + + for (i = 0; i < state->nb_files; i++) { + + if (!(class = parse_java_class (state->files[i]))) { + continue; + } + + /*{ + + struct constant_pool *p = class->constant_pool; + int i; + + for (i = 0; i < p->vec_method_refs.length; i++) { + + struct constant_method_ref *method = p->vec_method_refs.data[i]; + + struct constant_utf8 *ptr = find_utf8 (class->constant_pool, array_to_integer (method->, 2, 1)); + printf ("method_ref[%d]\n", method->index); + + } + + }*/ + + if (!(main = find_method_in_pool (class->constant_pool, class->method_pool, ACC_PUBLIC | ACC_STATIC, "main", 4))) { + + report_at (program_name, 0, REPORT_ERROR, "main method not found in class %s", state->files[i]); + continue; + + } + + if (!(stack_frame = stack_init (1024))) { + + report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory for a stack"); + continue; + + } + + execute_method (main, stack_frame, class); + + if (stack_frame) { + + if (stack_frame->store) { + free (stack_frame->store); + } + + free (stack_frame); + + } + + stack_frame = 0; + + } + + free_classes (); + + if (stack_frame) { + + if (stack_frame->store) { + free (stack_frame->store); + } + + free (stack_frame); + + } + + return (get_error_count () > 0 ? EXIT_FAILURE : EXIT_SUCCESS); + +} diff --git a/jvm.h b/jvm.h new file mode 100755 index 0000000..bfbea35 --- /dev/null +++ b/jvm.h @@ -0,0 +1,25 @@ +/****************************************************************************** + * @file jvm.h + *****************************************************************************/ +#ifndef _JVM_H +#define _JVM_H + +struct jvm_state { + + const char **files; + long nb_files; + + const char **class_paths; + long nb_class_paths; + +}; + +extern struct jvm_state *state; +extern const char *program_name; + +#include + +int parse_field_pool (FILE *fp, int count); +int parse_interface_pool (FILE *fp, int count); + +#endif /* _JVM_H */ diff --git a/lib.c b/lib.c new file mode 100755 index 0000000..d6897a7 --- /dev/null +++ b/lib.c @@ -0,0 +1,295 @@ +/****************************************************************************** + * @file lib.c + *****************************************************************************/ +#include +#include +#include +#include + +#include "jvm.h" +#include "lib.h" +#include "report.h" + +#define OPTION_CLASS_PATH 0x0001 +#define OPTION_HELP 0x0002 + +struct option { + + const char *name; + int index, flags; + +}; + +#define OPTION_NO_ARG 0x0001 +#define OPTION_HAS_ARG 0x0002 + +static struct option opts[] = { + + { "--class-path", OPTION_CLASS_PATH, OPTION_HAS_ARG }, + { "-cp", OPTION_CLASS_PATH, OPTION_HAS_ARG }, + + { "--help", OPTION_HELP, OPTION_NO_ARG }, + { 0, 0, 0 } + +}; + +static int strstart (const char *val, const char **str) { + + const char *p = val; + const char *q = *str; + + while (*p != '\0') { + + if (*p != *q) { + return 0; + } + + ++p; + ++q; + + } + + *str = q; + return 1; + +} + +static void print_help (void) { + + if (program_name) { + + fprintf (stderr, "Usage: %s [options] file...\n\n", program_name); + fprintf (stderr, "Options:\n\n"); + + fprintf (stderr, " --help Show this help information then exit.\n"); + + } + + exit (EXIT_SUCCESS); + +} + +static void dynarray_add (void *ptab, long *nb_ptr, void *data) { + + int nb, nb_alloc; + void **pp; + + nb = *nb_ptr; + pp = *(void ***) ptab; + + if ((nb & (nb - 1)) == 0) { + + if (!nb) { + nb_alloc = 1; + } else { + nb_alloc = nb * 2; + } + + pp = xrealloc (pp, nb_alloc * sizeof (void *)); + *(void ***) ptab = pp; + + } + + pp[nb++] = data; + *nb_ptr = nb; + +} + +void parse_args (int argc, char **argv, int optind) { + + struct option *popt; + const char *optarg, *r; + +#if defined (_WIN32) + char *p; + unsigned long length, i; +#endif + + if (argc <= optind) { + print_help (); + } + + while (optind < argc) { + + r = argv[optind++]; + + if (r[0] != '-' || r[1] == '\0') { + + dynarray_add (&state->files, &state->nb_files, xstrdup (r)); + continue; + + } + + for (popt = opts; popt; popt++) { + + const char *p1 = popt->name; + const char *r1 = r; + + if (!p1) { + + report_at (program_name, 0, REPORT_ERROR, "invalid option -- '%s'", r); + exit (EXIT_FAILURE); + + } + + if (!strstart (p1, &r1)) { + continue; + } + + optarg = r1; + + if (popt->flags & OPTION_HAS_ARG) { + + if (*optarg == '\0') { + + if (optind >= argc) { + + report_at (program_name, 0, REPORT_ERROR, "argument to '%s' is missing", r); + exit (EXIT_FAILURE); + + } + + optarg = argv[optind++]; + + } + + } else if (*optarg != '\0') { + continue; + } + + break; + + } + + switch (popt->index) { + + case OPTION_CLASS_PATH: { + +#if defined (_WIN32) + length = strlen (p = (char *) optarg); + + for (i = 0; i < length; i++) { + + if (p[i] == '/') { + p[i] = '\\'; + } + + } +#endif + dynarray_add (&state->class_paths, &state->nb_class_paths, xstrdup (optarg)); + break; + + } + + case OPTION_HELP: { + + print_help (); + break; + + } + + default: { + + report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r); + exit (EXIT_FAILURE); + + } + + } + + } + +} + +unsigned long array_to_integer (unsigned char *arr, int size, int bigendian) { + + unsigned long val = 0, mask = 1; + int i; + + if (bigendian) { + + int j; + + for (i = size, j = 0; i > 0; i--, j++) { + + mask *= (UCHAR_MAX + 1); + val |= arr[j] << (CHAR_BIT * (i - 1)); + + } + + } else { + + for (i = 0; i < size; i++) { + + mask *= (UCHAR_MAX + 1); + val |= arr[i] << (CHAR_BIT * i); + + } + + } + + return val & (mask - 1); + +} + +void write_to_byte_array (unsigned char *arr, unsigned long value, int size, int bigendian) { + + int i; + + if (bigendian) { + + int j; + + for (i = size - 1, j = 0; i >= 0; i--, j++) { + arr[i] = (value >> (CHAR_BIT * j)) & UCHAR_MAX; + } + + } else { + + for (i = 0; i < size; i++) { + arr[i] = (value >> (CHAR_BIT * i)) & UCHAR_MAX; + } + + } + +} + +char *xstrdup (const char *str) { + + char *ptr = xmalloc (strlen (str) + 1); + strcpy (ptr, str); + + return ptr; + +} + +void *xmalloc (unsigned long size) { + + void *ptr = malloc (size); + + if (ptr == NULL && size) { + + report_at (program_name, 0, REPORT_ERROR, "memory full (malloc)"); + exit (EXIT_FAILURE); + + } + + memset (ptr, 0, size); + return ptr; + +} + +void *xrealloc (void *ptr, unsigned long size) { + + void *new_ptr = realloc (ptr, size); + + if (new_ptr == NULL && size) { + + report_at (program_name, 0, REPORT_ERROR, "memory full (realloc)"); + exit (EXIT_FAILURE); + + } + + return new_ptr; + +} diff --git a/lib.h b/lib.h new file mode 100755 index 0000000..ea61df1 --- /dev/null +++ b/lib.h @@ -0,0 +1,17 @@ +/****************************************************************************** + * @file lib.h + *****************************************************************************/ +#ifndef _LIB_H +#define _LIB_H + +unsigned long array_to_integer (unsigned char *arr, int size, int bigendian); +void write_to_byte_array (unsigned char *arr, unsigned long value, int size, int bigendian); + +char *xstrdup (const char *str); + +void *xmalloc (unsigned long size); +void *xrealloc (void *ptr, unsigned long size); + +void parse_args (int argc, char **argv, int optind); + +#endif /* _LIB_H */ diff --git a/method.c b/method.c new file mode 100644 index 0000000..b73a7e9 --- /dev/null +++ b/method.c @@ -0,0 +1,208 @@ +/****************************************************************************** + * @file java_method_pool_parser.c + *****************************************************************************/ +#include +#include +#include + +#include "constant.h" +#include "lib.h" +#include "method.h" +#include "vector.h" + +static int parse_attr (FILE *fp, struct method_attribute_info *attributes, int count) { + + struct method_attribute_info *temp; + int i; + + size_t length; + + for (i = 0; i < count; i++) { + + temp = &attributes[i]; + + if (fread (temp->attribute_name_index, 1, 2, fp) != 2) { + return -1; + } + + if (fread (temp->attribute_length, 1, 4, fp) != 4) { + return -1; + } + + length = array_to_integer (temp->attribute_length, 4, 1); + + if (!(temp->info = malloc (sizeof (unsigned char) * length))) { + return -1; + } + + if (fread (temp->info, 1, length, fp) != length) { + + free (temp->info); + return -1; + + } + + } + + return 0; + +} + +static struct method_info *parse (FILE *fp) { + + struct method_info *method_info; + int count; + + if (!(method_info = malloc (sizeof (*method_info)))) { + return 0; + } + + if (fread (method_info->access_flags, 1, 2, fp) != 2) { + + free (method_info); + return 0; + + } + + if (fread (method_info->name_index, 1, 2, fp) != 2) { + + free (method_info); + return 0; + + } + + if (fread (method_info->descriptor_index, 1, 2, fp) != 2) { + + free (method_info); + return 0; + + } + + if (fread (method_info->attributes_count, 1, 2, fp) != 2) { + + free (method_info); + return 0; + + } + + count = array_to_integer (method_info->attributes_count, 2, 1); + + if (!(method_info->attributes = malloc (sizeof (*method_info->attributes) * count))) { + + free (method_info); + return 0; + + } + + memset (method_info->attributes, 0, sizeof (*method_info->attributes) * count); + + if (parse_attr (fp, method_info->attributes, count)) { + + free (method_info->attributes); + free (method_info); + + return 0; + + } + + return method_info; + +} + +struct vector *parse_method_pool (FILE *fp, int count) { + + struct method_info *method_info; + struct vector *method_pool; + + int i, j; + + if (!(method_pool = malloc (sizeof (*method_pool)))) { + return 0; + } + + memset (method_pool, 0, sizeof (*method_pool)); + + for (i = 0; i < count; i++) { + + if (!(method_info = parse (fp))) { + + for (j = 0; j < method_pool->length; j++) { + + method_info = method_pool->data[j]; + + free (method_info->attributes); + free (method_info); + + } + + free (method_pool); + return 0; + + } + + vec_push (method_pool, method_info); + + } + + return method_pool; + +} + +void free_method_pool (struct vector *method_pool) { + + struct method_info *method_info; + int i; + + for (i = 0; i < method_pool->length; i++) { + + method_info = method_pool->data[i]; + + free (method_info->attributes); + free (method_info); + + } + +} + +struct method_info *find_method_in_pool (struct constant_pool *constant_pool, struct vector *method_pool, int flags, const char *method_name, int length) { + + struct constant_utf8 *name; + struct method_info *method; + + int access_flags, size, i; + + for (i = 0; i < method_pool->length; i++) { + + if ((method = method_pool->data[i])) { + + if (flags) { + + access_flags = array_to_integer (method->access_flags, 2, 1); + + if ((access_flags & flags) != flags) { + continue; + } + + } + + if ((name = find_utf8 (constant_pool, array_to_integer (method->name_index, 2, 1)))) { + + size = array_to_integer (name->string_size, 2, 1); + + if (length == size) { + + if (strncmp (method_name, (char *) name->ptr, size) == 0) { + return method; + } + + } + + } + + } + + } + + return 0; + +} diff --git a/method.h b/method.h new file mode 100644 index 0000000..69b9a41 --- /dev/null +++ b/method.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * @file method.h + *****************************************************************************/ +#ifndef _METHOD_H +#define _METHOD_H + +#include "vector.h" + +struct method_attribute_info { + + unsigned char attribute_name_index[2]; + unsigned char attribute_length[4]; + + unsigned char *info; + +}; + +void free_method_pool (struct vector *method_pool); + +#define ACC_PUBLIC (1 << 0) +#define ACC_PRIVATE (1 << 1) +#define ACC_PROTECTED (1 << 2) + +#define ACC_STATIC (1 << 3) + +struct method_info { + + unsigned char access_flags[2]; + unsigned char name_index[2]; + unsigned char descriptor_index[2]; + unsigned char attributes_count[2]; + + struct method_attribute_info *attributes; + +}; + +#include +struct vector *parse_method_pool (FILE *fp, int count); + +#include "constant.h" +struct method_info *find_method_in_pool (struct constant_pool *constant_pool, struct vector *method_pool, int flags, const char *method_name, int length); + +#endif /* _METHOD_H */ diff --git a/report.c b/report.c new file mode 100644 index 0000000..8e128ef --- /dev/null +++ b/report.c @@ -0,0 +1,150 @@ +/****************************************************************************** + * @file report.c + *****************************************************************************/ +#include +#include +#include + +#include "report.h" + +unsigned long errors = 0; + +#ifndef __PDOS__ +#if defined (_WIN32) +# include +static int OriginalConsoleColor = -1; +#endif + +static void reset_console_color (void) { + +#if defined (_WIN32) + + HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE); + + if (OriginalConsoleColor == -1) { return; } + + SetConsoleTextAttribute (hStdError, OriginalConsoleColor); + OriginalConsoleColor = -1; + +#else + + fprintf (stderr, "\033[0m"); + +#endif + +} + +static void set_console_color (int color) { + +#if defined (_WIN32) + + HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE); + WORD wColor; + + if (OriginalConsoleColor == -1) { + + CONSOLE_SCREEN_BUFFER_INFO csbi; + + if (!GetConsoleScreenBufferInfo (hStdError, &csbi)) { + return; + } + + OriginalConsoleColor = csbi.wAttributes; + + } + + wColor = (OriginalConsoleColor & 0xF0) + (color & 0xF); + SetConsoleTextAttribute (hStdError, wColor); + +#else + + fprintf (stderr, "\033[%dm", color); + +#endif + +} +#endif + +static void output_message (const char *filename, unsigned long lineno, unsigned long idx, enum report_type type, const char *fmt, va_list ap) { + + if (filename) { + + if (lineno == 0 && idx == 0) { + fprintf (stderr, "%s: ", filename); + } else { + fprintf (stderr, "%s:", filename); + } + + } + + if (lineno > 0) { + + if (idx == 0) { + fprintf (stderr, "%lu: ", lineno); + } else { + fprintf (stderr, "%lu:", lineno); + } + + } + + if (idx > 0) { + fprintf (stderr, "%lu: ", idx); + } + + if (type == REPORT_ERROR || type == REPORT_FATAL_ERROR) { + +#ifndef __PDOS__ + set_console_color (COLOR_ERROR); +#endif + + if (type == REPORT_ERROR) { + fprintf (stderr, "error:"); + } else { + fprintf (stderr, "fatal error:"); + } + + } else if (type == REPORT_INTERNAL_ERROR) { + +#ifndef __PDOS__ + set_console_color (COLOR_INTERNAL_ERROR); +#endif + + fprintf (stderr, "internal error:"); + + } else if (type == REPORT_WARNING) { + +#ifndef __PDOS__ + set_console_color (COLOR_WARNING); +#endif + + fprintf (stderr, "warning:"); + + } + +#ifndef __PDOS__ + reset_console_color (); +#endif + + fprintf (stderr, " "); + vfprintf (stderr, fmt, ap); + fprintf (stderr, "\n"); + + if (type != REPORT_WARNING) { + ++errors; + } + +} + +unsigned long get_error_count (void) { + return errors; +} + +void report_at (const char *filename, unsigned long lineno, enum report_type type, const char *fmt, ...) { + + va_list ap; + + va_start (ap, fmt); + output_message (filename, lineno, 0, type, fmt, ap); + va_end (ap); + +} diff --git a/report.h b/report.h new file mode 100644 index 0000000..8fc8758 --- /dev/null +++ b/report.h @@ -0,0 +1,29 @@ +/****************************************************************************** + * @file report.h + *****************************************************************************/ +#ifndef _REPORT_H +#define _REPORT_H + +enum report_type { + + REPORT_ERROR = 0, + REPORT_FATAL_ERROR, + REPORT_INTERNAL_ERROR, + REPORT_WARNING + +}; + +#if defined (_WIN32) +# define COLOR_ERROR 12 +# define COLOR_WARNING 13 +# define COLOR_INTERNAL_ERROR 19 +#else +# define COLOR_ERROR 91 +# define COLOR_INTERNAL_ERROR 94 +# define COLOR_WARNING 95 +#endif + +unsigned long get_error_count (void); +void report_at (const char *filename, unsigned long line_number, enum report_type type, const char *fmt, ...); + +#endif /* _REPORT_H */ diff --git a/stack.c b/stack.c new file mode 100644 index 0000000..d3312cb --- /dev/null +++ b/stack.c @@ -0,0 +1,100 @@ +/****************************************************************************** + * @file stack.c + *****************************************************************************/ +#include +#include +#include + +#include "lib.h" +#include "stack.h" + +struct stack_frame *stack_init (int entry_size) { + + struct stack_frame *stack_frame; + int i; + + if (!(stack_frame = malloc (sizeof (*stack_frame)))) { + return 0; + } + + memset (stack_frame, 0, sizeof (*stack_frame)); + + if (!(stack_frame->store = malloc (sizeof (*stack_frame->store) * entry_size))) { + + free (stack_frame); + return 0; + + } + + for (i = 0; i < entry_size; i++) { + memset (&stack_frame->store[i], 0, sizeof (stack_frame->store[i])); + } + + stack_frame->size = entry_size; + stack_frame->max_size = entry_size; + + return stack_frame; + +} + + +void push_double (struct stack_frame *stack, double value) { + + struct stack_entry *entry = &stack->store[--stack->size]; + memset (entry, 0, sizeof (*entry)); + + memcpy (entry->entry, &value, sizeof (double)); + entry->type = STACK_ENTRY_DOUBLE; + +} + +void push_int (struct stack_frame *stack, int value) { + + struct stack_entry *entry = &stack->store[--stack->size]; + memset (entry, 0, sizeof (*entry)); + + write_to_byte_array (entry->entry, value, sizeof (int), 1); + entry->type = STACK_ENTRY_INT; + +} + +void push_ref (struct stack_frame *stack, int value) { + + struct stack_entry *entry = &stack->store[--stack->size]; + memset (entry, 0, sizeof (*entry)); + + write_to_byte_array (entry->entry, value, sizeof (int), 1); + entry->type = STACK_ENTRY_REF; + +} + +double pop_double (struct stack_frame *stack) { + + struct stack_entry *entry = &stack->store[stack->size++]; + + double value = 0; + memcpy (&value, entry->entry, sizeof (double)); + + return value; + +} + +int pop_int (struct stack_frame *stack) { + + struct stack_entry *entry = &stack->store[stack->size++]; + return array_to_integer (entry->entry, 4, 1); + +} + + +struct stack_entry *pop_entry (struct stack_frame *stack) { + return &stack->store[stack->size++]; +} + +int entry_to_int (struct stack_entry *entry) { + return array_to_integer (entry->entry, 4, 1); +} + +int is_ref_entry (struct stack_frame *stack) { + return (stack->store[stack->size].type == STACK_ENTRY_REF); +} diff --git a/stack.h b/stack.h new file mode 100644 index 0000000..4df831d --- /dev/null +++ b/stack.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * @file stack.h + *****************************************************************************/ +#ifndef _STACK_H +#define _STACK_H + +struct stack_entry { + + unsigned char entry[8]; + int type; + +}; + +#define STACK_ENTRY_NONE 0 +#define STACK_ENTRY_INT 1 +#define STACK_ENTRY_REF 2 +#define STACK_ENTRY_LONG 3 +#define STACK_ENTRY_DOUBLE 4 +#define STACK_ENTRY_FLOAT 5 + +struct stack_frame { + + int max_size, size; + struct stack_entry *store; + +}; + +struct stack_frame *stack_init (int entry_size); + +void push_double (struct stack_frame *stack, double value); +void push_int (struct stack_frame *stack, int value); +void push_ref (struct stack_frame *stack, int value); + +double pop_double (struct stack_frame *stack); +int pop_int (struct stack_frame *stack); + +struct stack_entry *pop_entry (struct stack_frame *stack); + +int entry_to_int (struct stack_entry *entry); +int is_ref_entry (struct stack_frame *stack); + +#endif /* _STACK_H */ diff --git a/vector.c b/vector.c new file mode 100755 index 0000000..723993a --- /dev/null +++ b/vector.c @@ -0,0 +1,54 @@ +/****************************************************************************** + * @file vector.c + *****************************************************************************/ +#include +#include + +#include "vector.h" + +extern void *xrealloc (void *__ptr, unsigned int __size); + +int vec_adjust (struct vector *vec, int length) { + + if (vec->capacity <= length) { + + if (vec->capacity == 0) { + vec->capacity = 16; + } else { + vec->capacity <<= 1; + } + + vec->data = xrealloc (vec->data, sizeof (*(vec->data)) * vec->capacity); + + } + + return 0; + +} + +void *vec_pop (struct vector *vec) { + + if (!vec || vec == NULL) { + return NULL; + } + + if (vec->length == 0) { + return NULL; + } + + return vec->data[--vec->length]; + +} + +int vec_push (struct vector *vec, void *elem) { + + int ret; + + if ((ret = vec_adjust (vec, vec->length)) != 0) { + return ret; + } + + vec->data[vec->length++] = elem; + return 0; + +} diff --git a/vector.h b/vector.h new file mode 100755 index 0000000..3da05e1 --- /dev/null +++ b/vector.h @@ -0,0 +1,19 @@ +/****************************************************************************** + * @file vector.h + *****************************************************************************/ +#ifndef _VECTOR_H +#define _VECTOR_H + +struct vector { + + void **data; + int capacity, length; + +}; + +int vec_adjust (struct vector *vec, int length); +int vec_push (struct vector *vec, void *elem); + +void *vec_pop (struct vector *vec); + +#endif /* _VECTOR_H */ diff --git a/vm.c b/vm.c new file mode 100644 index 0000000..95356ae --- /dev/null +++ b/vm.c @@ -0,0 +1,864 @@ +/****************************************************************************** + * @file vm.c + *****************************************************************************/ +#include +#include +#include + +#include "class.h" +#include "cstr.h" +#include "lib.h" +#include "method.h" +#include "stack.h" +#include "vector.h" +#include "vm.h" + +int invoke_java_lang_library (struct constant_pool *p, struct stack_frame *stack, char *class_name, char *method_name); + +static char *java_lib[] = { + + "java/io/PrintStream", + "java/lang/StringBuilder" + +}; + +struct cstring string_builder_buffer = { 0 };; + +struct local_variables { int integer[10]; } local_variables = { 0 }; +struct vector vec_classes = { 0 }; + +static double get_double_parameter (struct stack_frame *stack, struct constant_pool *p) { + + if (is_ref_entry (stack)) { + return get_double_from_constant_pool (p, pop_int (stack)); + } + + return pop_double (stack); + +} + +typedef int (*opcode_func) (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class); + +typedef struct { + + const char *name; + + unsigned char opcode; + int offset; + + opcode_func func; + +} byte_code; + +static int op_aload_0 (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + push_int (stack, 0); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_bipush (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + push_int (stack, opcode[0][1]); + (void) java_class; + + *opcode = *opcode + 2; + return 0; + +} + +static int op_d2i (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + push_int (stack, (int) pop_double (stack)); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_dadd (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + double value1 = get_double_parameter (stack, java_class->constant_pool); + double value2 = get_double_parameter (stack, java_class->constant_pool); + + push_double (stack, value1 + value2); + *opcode = *opcode + 1; + + return 0; + +} + +static int op_dconst_1 (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + push_double (stack, 1.0f); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_dmul (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + double value1 = get_double_parameter (stack, java_class->constant_pool); + double value2 = get_double_parameter (stack, java_class->constant_pool); + + push_double (stack, value1 * value2); + *opcode = *opcode + 1; + + return 0; + +} + +static int op_dup (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + struct stack_entry *entry = pop_entry (stack); + int value = entry_to_int (entry); + + if (entry->type == STACK_ENTRY_INT) { + + push_int (stack, value); + push_int (stack, value); + + } else { + + push_ref (stack, value); + push_ref (stack, value); + + } + + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_getstatic (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + unsigned short field_index = (opcode[0][1] << 8) | opcode[0][2]; + + /*struct constant_pool *p = java_class->constant_pool; + struct vector *field_refs = &p->vec_field_refs; + + struct constant_field_ref *ref; + struct constant_nat *nat; + + int i; + + for (i = 0; i < field_refs->length; i++) { + + ref = field_refs->data[i]; + + if ((nat = find_nat (p, array_to_integer (ref->nat_index, 2, 1)))) { + printf ("FUCK YEAHHH\n"); + } + + }*/ + + push_ref (stack, field_index); + (void) java_class; + + *opcode = *opcode + 3; + return 0; + +} + +static int op_iadd (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + int result = pop_int (stack) + pop_int (stack); + + push_int (stack, result); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_iconst_0 (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + push_int (stack, 0); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_iconst_1 (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + push_int (stack, 1); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_iconst_2 (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + push_int (stack, 2); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_iconst_3 (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + push_int (stack, 3); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_iconst_4 (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + push_int (stack, 4); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_iconst_5 (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + push_int (stack, 5); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_idiv (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + int value2 = pop_int (stack), value1 = pop_int (stack); + + push_int (stack, value1 / value2); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_iload (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + push_int (stack, local_variables.integer[opcode[0][1]]); + (void) java_class; + + *opcode = *opcode + 2; + return 0; + +} + +static int op_iload_1 (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + push_int (stack, local_variables.integer[1]); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_iload_2 (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + push_int (stack, local_variables.integer[2]); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_iload_3 (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + push_int (stack, local_variables.integer[3]); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_imul (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + int result = pop_int (stack) * pop_int (stack); + + push_int (stack, result); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +/*static int op_invokedynamic (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + struct constant_pool *p = java_class->constant_pool; + unsigned short method_index = (opcode[0][1] << 8) | opcode[0][2]; + + struct vector *methods = &p->vec_invoke_dynamic; + struct constant_invoke_dynamic *method; + + *opcode = *opcode + 5; + + if ((method = find_invoke_dynamic (p, method_index))) { + + struct constant_nat *nat; + char name[255]; + + if ((nat = find_nat (p, array_to_integer (method->nat_index, 2, 1)))) { + + get_utf8_string (p, array_to_integer (nat->name_index, 2, 1), 255, name); + printf ("YAYAY: %s\n", name); + + } + + } + + (void) stack; + + if (method_index < methods->length) { + + if ((method = methods->data[method_index])) { + return execute_method (method, stack, java_class); + } + + } + + if (vec_classes.length > 0) { + + java_class = vec_classes.data[vec_classes.length - 1]; + + p = java_class->constant_pool; + method_refs = java_class->method_pool; + + if (method_index < method_refs->length) { + + if ((method = method_refs->data[method_index])) { + return execute_method (method, stack, java_class); + } + + } + + } + + return 0; + +}*/ + +static int op_invokespecial (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + unsigned short method_index = (opcode[0][1] << 8) | opcode[0][2]; + + struct vector *methods = java_class->method_pool; + struct method_info *method; + + *opcode = *opcode + 3; + + if (method_index < methods->length) { + + if ((method = methods->data[method_index])) { + return execute_method (method, stack, java_class); + } + + } + + /*if (vec_classes.length > 0) { + + java_class = vec_classes.data[vec_classes.length - 1]; + + p = java_class->constant_pool; + method_refs = java_class->method_pool; + + if (method_index < method_refs->length) { + + if ((method = method_refs->data[method_index])) { + return execute_method (method, stack, java_class); + } + + } + + }*/ + + return 0; + +} + +static int op_invokestatic (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + unsigned short method_index = (opcode[0][1] << 8) | opcode[0][2]; + + struct constant_pool *p = java_class->constant_pool; + struct vector *methods = java_class->method_pool; + + char class_name[255]; + char method_name[255]; + char method_type[255]; + + struct method_info *method; + int ret = -1; + + *opcode = *opcode + 3; + + if (method_index < methods->length) { + + method = methods->data[method_index]; + memset(method_name, 0, 255); + + get_utf8_string (p, array_to_integer (method->name_index, 2, 1), 255, method_name); + printf ("method_name: %s\n", method_name); + + } else { + + struct constant_method_ref *method_ref; + struct constant_class_ref *class_ref; + struct constant_nat *nat; + + if ((method_ref = find_method_ref (p, method_index))) { + + if ((class_ref = find_class_ref (p, array_to_integer (method_ref->class_index, 2, 1))) && (nat = find_nat (p, array_to_integer (method_ref->nat_index, 2, 1)))) { + + get_utf8_string (p, array_to_integer (class_ref->string_index, 2, 1), 255, class_name); + get_utf8_string (p, array_to_integer (nat->name_index, 2, 1), 255, method_name); + get_utf8_string (p, array_to_integer (nat->type_index, 2, 1), 255, method_type); + + if ((ret = invoke_java_lang_library (p, stack, class_name, method_name))) { + + struct java_class *class = parse_java_class (class_name); + + if ((method = find_method_in_pool (class->constant_pool, class->method_pool, ACC_STATIC, method_name, strlen (method_name)))) { + ret = execute_method (method, stack, class); + } + + } + + } + + } + + } + + return ret; + +} + +static int op_invokevirtual (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + struct constant_pool *p = java_class->constant_pool; + unsigned short object_ref = (opcode[0][1] << 8) | opcode[0][2]; + + char class_name[255]; + char utf8[255]; + char method_name[255]; + + struct constant_method_ref *method_ref; + *opcode = *opcode + 3; + + if ((method_ref = find_method_ref (p, object_ref))) { + + struct constant_class_ref *class_ref = find_class_ref (p, array_to_integer (method_ref->class_index, 2, 1)); + struct constant_nat *nat = find_nat (p, array_to_integer (method_ref->nat_index, 2, 1)); + + if (class_ref && nat) { + + get_utf8_string (p, array_to_integer (class_ref->string_index, 2, 1), 255, class_name); + + if (strcmp (class_name, "java/io/PrintStream") == 0) { + + struct stack_entry *entry = pop_entry (stack); + struct constant_string_ref *string_ref; + + if (entry->type == STACK_ENTRY_REF) { + + if ((string_ref = find_string_ref (p, entry_to_int (entry)))) { + + get_utf8_string (p, array_to_integer (string_ref->string_index, 2, 1), 255, utf8); + cstr_cat (&string_builder_buffer, utf8, strlen (utf8)); + + } + + } else if (entry->type == STACK_ENTRY_INT) { + + sprintf (utf8, "%d", entry_to_int (entry)); + cstr_cat (&string_builder_buffer, utf8, strlen (utf8)); + + } + + cstr_ccat (&string_builder_buffer, '\0'); + printf ("%s\n", (char *) string_builder_buffer.data); + + cstr_free (&string_builder_buffer); + memset (&string_builder_buffer, 0, sizeof (string_builder_buffer)); + + return 0; + + } else if (strcmp (class_name, "java/lang/StringBuilder") == 0) { + + struct stack_entry *entry = pop_entry (stack); + struct constant_string_ref *string_ref; + + if (entry->type == STACK_ENTRY_REF) { + + if ((string_ref = find_string_ref (p, entry_to_int (entry)))) { + + get_utf8_string (p, array_to_integer (string_ref->string_index, 2, 1), 255, utf8); + cstr_cat (&string_builder_buffer, utf8, strlen (utf8)); + + } + + } else if (entry->type == STACK_ENTRY_INT) { + + sprintf (utf8, "%d", entry_to_int (entry)); + cstr_cat (&string_builder_buffer, utf8, strlen (utf8)); + + } + + cstr_ccat (&string_builder_buffer, '\0'); + string_builder_buffer.size--; + + return 0; + + } else { + + struct stack_entry *entry = pop_entry (stack); + struct method_info *method; + + get_utf8_string (p, array_to_integer (nat->name_index, 2, 1), 255, method_name); + + if (entry->type == STACK_ENTRY_REF) { + + struct java_class *class = parse_java_class (class_name); + + /*struct constant_field_ref *ref; + char temp[255]; + + printf ("index: %d\n", index);*/ + + /*if ((ref = find_field_ref (p, index))) {*/ + + /*printf ("FUCKKKK! %ld\n", array_to_integer (ref->class_index, 2, 1)); + exit (1);*/ + + /*get_utf8_string (p, array_to_integer (ref->string_index, 2, 1), 255, temp);*/ + /*printf ("%s\n", temp);*/ + + if ((method = find_method_in_pool (class->constant_pool, class->method_pool, 0, method_name, strlen (method_name)))) { + return execute_method (method, stack, class); + } + + /*}*/ + + } else if (entry->type == STACK_ENTRY_INT) { + + if (vec_classes.length > 0) { + + struct java_class *class = vec_pop (&vec_classes); + + if (strcmp (class->class_name, class_name) == 0) { + + if ((method = find_method_in_pool (class->constant_pool, class->method_pool, 0, method_name, strlen (method_name)))) { + return execute_method (method, stack, class); + } + + } + + } + + } + + } + + } + + } + + return -1; + +} + +static int op_irem (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + int value1 = pop_int (stack), value2 = pop_int (stack); + + push_int (stack, value2 % value1); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_istore (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + local_variables.integer[opcode[0][1]] = pop_int (stack); + (void) java_class; + + *opcode = *opcode + 2; + return 0; + +} + +static int op_istore_1 (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + local_variables.integer[1] = pop_int (stack); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_istore_2 (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + local_variables.integer[2] = pop_int (stack); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_istore_3 (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + local_variables.integer[3] = pop_int (stack); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_isub (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + int value2 = pop_int (stack), value1 = pop_int (stack); + + push_int (stack, value1 - value2); + (void) java_class; + + *opcode = *opcode + 1; + return 0; + +} + +static int op_ldc (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + push_ref (stack, opcode[0][1]); + *opcode = *opcode + 2; + + (void) java_class; + return 0; + +} + +static int op_ldc2_w (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + int index = (opcode[0][1] << 8) | opcode[0][2]; + + push_ref (stack, index); + *opcode = *opcode + 3; + + (void) java_class; + return 0; + +} + +static int op_new (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + unsigned short object_ref = (opcode[0][1] << 8) | opcode[0][2]; + + struct constant_class_ref *ref; + struct java_class *class; + + char class_name[255]; + int ret = -1; + + unsigned long count = (sizeof (java_lib) / sizeof (java_lib[0])); + unsigned long i; + + *opcode = *opcode + 3; + + if ((ref = find_class_ref (java_class->constant_pool, object_ref))) { + + get_utf8_string (java_class->constant_pool, array_to_integer (ref->string_index, 2, 1), 255, class_name); + + for (i = 0; i < count; i++) { + + if (strcmp (java_lib[i], class_name) == 0) { + return 0; + } + + } + + if ((class = parse_java_class (class_name))) { + ret = vec_push (&vec_classes, class); + } + + } + + (void) stack; + return ret; + +} + +static int op_return (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + *opcode = *opcode + 1; + + (void) stack; + (void) java_class; + + return -1; + +} + +static int op_sipush (unsigned char **opcode, struct stack_frame *stack, struct java_class *java_class) { + + push_int (stack, (opcode[0][1] << 8) | opcode[0][2]); + (void) java_class; + + *opcode = *opcode + 3; + return 0; + +} + +static byte_code opcodes[] = { + + { "aload_0", 0x2A, 1, op_aload_0 }, + { "bipush", 0x10, 2, op_bipush }, + { "dup", 0x59, 1, op_dup }, + { "getstatic", 0xB2, 3, op_getstatic }, + { "iadd", 0x60, 1, op_iadd }, + { "iconst_0", 0x03, 1, op_iconst_0 }, + { "iconst_1", 0x04, 1, op_iconst_1 }, + { "iconst_2", 0x05, 1, op_iconst_2 }, + { "iconst_3", 0x06, 1, op_iconst_3 }, + { "iconst_4", 0x07, 1, op_iconst_4 }, + { "iconst_5", 0x08, 1, op_iconst_5 }, + { "dconst_1", 0x0F, 1, op_dconst_1 }, + { "idiv", 0x6C, 1, op_idiv }, + { "imul", 0x68, 1, op_imul }, + { "dadd", 0x63, 1, op_dadd }, + { "dmul", 0x6B, 1, op_dmul }, + { "d2i", 0x8e, 1, op_d2i }, + { "invokespecial", 0xB7, 3, op_invokespecial }, + { "invokevirtual", 0xB6, 3, op_invokevirtual }, + { "invokestatic", 0xB8, 3, op_invokestatic }, + /*{ "invokedynamic", 0xBA, 3, op_invokedynamic },*/ + { "iload", 0x15, 2, op_iload }, + { "iload_1", 0x1B, 1, op_iload_1 }, + { "iload_2", 0x1C, 1, op_iload_2 }, + { "iload_3", 0x1D, 1, op_iload_3 }, + { "istore", 0x36, 2, op_istore }, + { "istore_1", 0x3C, 1, op_istore_1 }, + { "istore_2", 0x3D, 1, op_istore_2 }, + { "istore_3", 0x3E, 1, op_istore_3 }, + { "isub", 0x64, 1, op_isub }, + { "ldc", 0x12, 2, op_ldc }, + { "ldc2_w", 0x14, 3, op_ldc2_w }, + { "new", 0xBB, 3, op_new }, + { "irem", 0x70, 1, op_irem }, + { "sipush", 0x11, 3, op_sipush }, + { "return", 0xB1, 1, op_return } + +}; + +static unsigned long opcodes_size = (sizeof (opcodes) / sizeof (opcodes[0])); + +static opcode_func find_opcode_func (unsigned char op) { + + unsigned long i; + + for (i = 0; i < opcodes_size; i++) { + + if (opcodes[i].opcode == op) { + return opcodes[i].func; + } + + } + + return 0; + +} + +static int convert_to_code_attribute (struct code_attribute *ca, struct method_attribute_info *attr) { + + unsigned long length; + int info_p = 0; + + memcpy (ca->max_stack, attr->info + info_p, 2); + info_p += 2; + + memcpy (ca->max_locals, attr->info + info_p, 2); + info_p += 2; + + memcpy (ca->code_length, attr->info + info_p, 4); + info_p += 4; + + length = array_to_integer (ca->code_length, 4, 1); + + if (!(ca->code = malloc (sizeof (unsigned char) * length))) { + return -1; + } + + memcpy (ca->code, attr->info + info_p, length); + return 0; + +} + +int execute_method (struct method_info *method, struct stack_frame *stack, struct java_class *java_class) { + + unsigned long count = array_to_integer (method->attributes_count, 2, 1); + unsigned char *pc; + + char name[255]; + unsigned long i; + + struct code_attribute ca; + opcode_func func; + + memset (&ca, 0, sizeof (ca)); + + for (i = 0; i < count; i++) { + + if (convert_to_code_attribute (&ca, &method->attributes[i])) { + break; + } + + get_utf8_string (java_class->constant_pool, array_to_integer (ca.attribute_name_index, 2, 1), 255, name); + + if (memcmp (name, "code", 4) == 0) { + continue; + } + + pc = ca.code; + + for (;;) { + + if (!(func = find_opcode_func (pc[0]))) { + break; + } + + if (func (&pc, stack, java_class)) { + break; + } + + } + + } + + return 0; + +} diff --git a/vm.h b/vm.h new file mode 100644 index 0000000..807c15a --- /dev/null +++ b/vm.h @@ -0,0 +1,26 @@ +/****************************************************************************** + * @file vm.h + *****************************************************************************/ +#ifndef _VM_H +#define _VM_H + +struct code_attribute { + + unsigned char attribute_name_index[2]; + unsigned char attribute_length[4]; + + unsigned char max_stack[2]; + unsigned char max_locals[2]; + + unsigned char code_length[4]; + unsigned char *code; + +}; + +#include "class.h" +#include "method.h" +#include "stack.h" + +int execute_method (struct method_info *method, struct stack_frame *stack, struct java_class *java_class); + +#endif /* _VM_H */ -- 2.34.1