From 571cc6a85e465438dbda40b5220dbf773d17ed80 Mon Sep 17 00:00:00 2001 From: Carsten Elton Sorensen Date: Tue, 13 Nov 2012 09:11:49 +0100 Subject: [PATCH] Added new embed utility for Windows --- makefile | 47 ++++++++++++++- src/embed.cpp | 125 ++++++++++++++++++++++++++++++++++++++++ src/embed.h | 17 ++++++ src/embedded-loader.cpp | 105 +++++++++++++++++++++++++++++++++ 4 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 src/embed.cpp create mode 100644 src/embed.h create mode 100644 src/embedded-loader.cpp diff --git a/makefile b/makefile index 8330ff31e0..8e494b5595 100755 --- a/makefile +++ b/makefile @@ -496,6 +496,10 @@ ifeq ($(platform),windows) else shared += -Wl,--add-stdcall-alias endif + + embed = $(build-embed)/embed.exe + embed-loader = $(build-embed-loader)/embed-loader.exe + embed-loader-o = $(build-embed)/embed-loader.o endif ifeq ($(mode),debug) @@ -640,6 +644,15 @@ vm-asm-sources = $(src)/$(asm).S target-asm = $(asm) +build-embed = $(build)/embed +build-embed-loader = $(build)/embed-loader + +embed-loader-sources = $(src)/embedded-loader.cpp +embed-loader-objects = $(call cpp-objects,$(embed-loader-sources),$(src),$(build-embed-loader)) + +embed-sources = $(src)/embed.cpp +embed-objects = $(call cpp-objects,$(embed-sources),$(src),$(build-embed)) + ifeq ($(process),compile) vm-sources += \ $(src)/compiler.cpp \ @@ -890,7 +903,7 @@ test-args = $(test-flags) $(input) .PHONY: build build: $(static-library) $(executable) $(dynamic-library) $(lzma-loader) \ $(lzma-encoder) $(executable-dynamic) $(classpath-dep) $(test-dep) \ - $(test-extra-dep) + $(test-extra-dep) $(embed) $(test-dep): $(classpath-dep) @@ -1005,6 +1018,38 @@ else $(ld) $(^) $(shared) $(lflags) -o $(@) endif +ifdef embed +$(embed): $(embed-objects) $(embed-loader-o) + @echo "building $(embed)" + $(build-cxx) $(^) -mwindows -mconsole -static -o $(@) + +$(build-embed)/%.o: $(src)/%.cpp + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(build-cxx) -D_UNICODE -DUNICODE -c $(<) -o $(@) + +$(embed-loader-o): $(embed-loader) $(converter) + @mkdir -p $(dir $(@)) + $(converter) $(<) $(@) _binary_loader_start \ + _binary_loader_end $(target-format) $(arch) + +$(embed-loader): $(embed-loader-objects) $(static-library) + @mkdir -p $(dir $(@)) + cd $(dir $(@)) && $(ar) x ../../../$(static-library) + $(dlltool) -z $(addsuffix .def,$(basename $(@))) $(dir $(@))/*.o + $(dlltool) -d $(addsuffix .def,$(basename $(@))) -e $(addsuffix .exp,$(basename $(@))) + $(cxx) $(addsuffix .exp,$(basename $(@))) $(dir $(@))/*.o -L../win32/lib -lmingwthrd -lm -lz -lws2_32 -liphlpapi \ + -mwindows -mconsole -static -o $(@) + strip --strip-all $(@) + +$(build-embed-loader)/%.o: $(src)/%.cpp + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(cxx) -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/win32 \ + -D_JNI_IMPLEMENTATION_ -c $(<) -o $(@) + +endif + $(build)/%.o: $(lzma)/C/%.c @echo "compiling $(@)" @mkdir -p $(dir $(@)) diff --git a/src/embed.cpp b/src/embed.cpp new file mode 100644 index 0000000000..ed02731c1a --- /dev/null +++ b/src/embed.cpp @@ -0,0 +1,125 @@ +/* Copyright (c) 2008-2012, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include +#include +#include +#include +#include +#include + +#include "embed.h" + +extern "C" const uint8_t binary_loader_start[]; +extern "C" const uint8_t binary_loader_end[]; + +__declspec(noreturn) +void printUsage(const wchar_t* executableName) +{ + wprintf(L"Usage: %s destination.exe classes.jar package.Main\n", executableName); + exit(0); +} + +void writeDestinationFile(const wchar_t* filename) +{ + if(FILE* file = _wfopen(filename, L"wb")) + { + size_t count = binary_loader_end - binary_loader_start; + if(count == fwrite(binary_loader_start, sizeof(binary_loader_start[0]), count, file)) + { + fclose(file); + return; + } + } + + fprintf(stderr, "Unable to write to destination file\n"); + exit(EXIT_FAILURE); +} + +void readFile(std::vector* jarFile, const wchar_t* fileName) +{ + if(FILE* file = _wfopen(fileName, L"rb")) + { + fseek(file, 0, SEEK_END); + jarFile->resize(ftell(file)); + fseek(file, 0, SEEK_SET); + fread(&jarFile->at(0), 1, jarFile->size(), file); + fclose(file); + } +} + +bool mkStringSection(std::vector* stringSection, const std::vector& strings, int first, int last) +{ + stringSection->clear(); + for(int i = first; i <= last; ++i) + { + const std::wstring& s = strings.at(i); + stringSection->push_back(s.size()); + stringSection->insert(stringSection->end(), s.begin(), s.end()); + } + + // pad to 16 entries + for(int i = last - first; i < 15; ++i) + stringSection->push_back(0); + + return stringSection->size() > 16; +} + +void writeStringResources(HANDLE hDest, const std::vector& strings) +{ + for(int i = 0; i < strings.size(); i += 16) + { + std::vector stringSection; + + if(mkStringSection(&stringSection, strings, i, std::min(i + 15, strings.size() - 1))) + UpdateResourceW(hDest, RT_STRING, MAKEINTRESOURCE((i >> 4) + 1), LANG_NEUTRAL, &stringSection.at(0), sizeof(wchar_t) * stringSection.size()); + } +} + +int wmain(int argc, wchar_t* argv[]) +{ + if(argc != 4) + printUsage(argv[0]); + + const wchar_t* destinationName = argv[1]; + const wchar_t* classesName = argv[2]; + const wchar_t* mainClassName = argv[3]; + + writeDestinationFile(destinationName); + + if(HANDLE hDest = BeginUpdateResourceW(destinationName, TRUE)) + { + std::vector strings; + strings.resize(RESID_MAIN_CLASS + 1); + strings.at(RESID_MAIN_CLASS) = mainClassName; + + writeStringResources(hDest, strings); + + std::vector jarFile; + readFile(&jarFile, classesName); + UpdateResourceW(hDest, RT_RCDATA, _T(RESID_BOOT_JAR), LANG_NEUTRAL, &jarFile.at(0), jarFile.size()); + + EndUpdateResource(hDest, FALSE); + } + + + return 0; +} + +extern "C" int _CRT_glob; +extern "C" void __wgetmainargs(int*, wchar_t***, wchar_t***, int, int*); + +int main() +{ + wchar_t **enpv, **argv; + int argc, si = 0; + __wgetmainargs(&argc, &argv, &enpv, _CRT_glob, &si); + return wmain(argc, argv); +} diff --git a/src/embed.h b/src/embed.h new file mode 100644 index 0000000000..a38108e64b --- /dev/null +++ b/src/embed.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2008-2012, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#ifndef EMBED_H +#define EMBED_H + +#define RESID_MAIN_CLASS 100 +#define RESID_BOOT_JAR "BOOT.JAR" + +#endif diff --git a/src/embedded-loader.cpp b/src/embedded-loader.cpp new file mode 100644 index 0000000000..d34bb0bce0 --- /dev/null +++ b/src/embedded-loader.cpp @@ -0,0 +1,105 @@ +/* Copyright (c) 2008-2012, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include +#include +#include + +#include "embed.h" +#include "jni.h" + + +#if (defined __MINGW32__) || (defined _MSC_VER) +# define EXPORT __declspec(dllexport) +#else +# define EXPORT __attribute__ ((visibility("default"))) \ + __attribute__ ((used)) +#endif + +extern "C" { + + EXPORT const uint8_t* + bootJar(unsigned* size) + { + if(HRSRC hResInfo = FindResource(NULL, _T(RESID_BOOT_JAR), RT_RCDATA)) + { + if(HGLOBAL hRes = LoadResource(NULL, hResInfo)) + { + *size = SizeofResource(NULL, hResInfo); + return (const uint8_t*)LockResource(hRes); + } + } + + fprintf(stderr, "boot.jar resource not found\n"); + + *size = 0; + return NULL; + } +} // extern "C" + +static bool getMainClass(char* pName, int maxLen) +{ + if(0 == LoadString(NULL, RESID_MAIN_CLASS, pName, maxLen)) + { + fprintf(stderr, "Main class not specified\n"); + strcpy(pName, "Main"); + } +} + +int +main(int ac, const char** av) +{ + JavaVMInitArgs vmArgs; + vmArgs.version = JNI_VERSION_1_2; + vmArgs.nOptions = 1; + vmArgs.ignoreUnrecognized = JNI_TRUE; + + JavaVMOption options[vmArgs.nOptions]; + vmArgs.options = options; + + options[0].optionString = const_cast("-Xbootclasspath:[bootJar]"); + + JavaVM* vm; + void* env; + JNI_CreateJavaVM(&vm, &env, &vmArgs); + JNIEnv* e = static_cast(env); + + char mainClass[256]; + getMainClass(mainClass, sizeof(mainClass)); + + jclass c = e->FindClass(mainClass); + if (not e->ExceptionCheck()) { + jmethodID m = e->GetStaticMethodID(c, "main", "([Ljava/lang/String;)V"); + if (not e->ExceptionCheck()) { + jclass stringClass = e->FindClass("java/lang/String"); + if (not e->ExceptionCheck()) { + jobjectArray a = e->NewObjectArray(ac-1, stringClass, 0); + if (not e->ExceptionCheck()) { + for (int i = 1; i < ac; ++i) { + e->SetObjectArrayElement(a, i-1, e->NewStringUTF(av[i])); + } + + e->CallStaticVoidMethod(c, m, a); + } else fprintf(stderr, "Couldn't create array\n"); + } else fprintf(stderr, "java.lang.String not found\n"); + } else fprintf(stderr, "main method not found\n"); + } else fprintf(stderr, "Main class not found\n"); + + int exitCode = 0; + if(e->ExceptionCheck()) { + exitCode = -1; + e->ExceptionDescribe(); + e->ExceptionClear(); + } + + vm->DestroyJavaVM(); + + return exitCode; +} \ No newline at end of file