Skip to content

Loading…

"embed" utility for Windows #21

Merged
merged 3 commits into from

3 participants

@csoren

This patch adds an "embed" utility for Windows. Its usage is "embed.exe destination.exe boot.jar package.MainClass", which will produce a ready to deploy destination.exe with the boot.jar embedded which will start the package.MainClass main method when run.

It works by writing out a skeleton loader and embedding the boot.jar and main class string as PE resources with no need for an installed C++ build environment.

I suppose something similar can be made for Linux/Mac, but an embed shell script that simply invokes gcc may be a better option for these platforms.

@mkeesey
ReadyTalk member

The primary maintainer is out of the office for a few weeks so it may be awhile before we get a chance to look at this - looks cool.

@dicej dicej merged commit 859f0ec into ReadyTalk:master
@dicej
ReadyTalk member

Thanks, Carsten. This is really cool. I just had to make a few tweaks to ensure cross compiles (e.g. building on Linux for Windows targets) and MSVC-based builds work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 13, 2012
  1. @csoren
Commits on Nov 30, 2012
  1. @csoren

    Fix for C++ compile error

    csoren committed
Showing with 300 additions and 8 deletions.
  1. +46 −1 makefile
  2. +125 −0 src/embed.cpp
  3. +17 −0 src/embed.h
  4. +105 −0 src/embedded-loader.cpp
  5. +7 −7 src/interpret.cpp
View
47 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 $(@))
View
125 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 <Windows.h>
+#include <tchar.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <vector>
+#include <string>
+
+#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<char>* 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<wchar_t>* stringSection, const std::vector<std::wstring>& 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<std::wstring>& strings)
+{
+ for(int i = 0; i < strings.size(); i += 16)
+ {
+ std::vector<wchar_t> stringSection;
+
+ if(mkStringSection(&stringSection, strings, i, std::min<int>(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<std::wstring> strings;
+ strings.resize(RESID_MAIN_CLASS + 1);
+ strings.at(RESID_MAIN_CLASS) = mainClassName;
+
+ writeStringResources(hDest, strings);
+
+ std::vector<char> 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);
+}
View
17 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
View
105 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 <Windows.h>
+#include <tchar.h>
+#include <stdint.h>
+
+#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<char*>("-Xbootclasspath:[bootJar]");
+
+ JavaVM* vm;
+ void* env;
+ JNI_CreateJavaVM(&vm, &env, &vmArgs);
+ JNIEnv* e = static_cast<JNIEnv*>(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;
+}
View
14 src/interpret.cpp
@@ -92,7 +92,7 @@ inline void
pushLong(Thread* t, uint64_t v)
{
if (DebugStack) {
- fprintf(stderr, "push long %"LLD" at %d\n", v, t->sp);
+ fprintf(stderr, "push long %" LLD " at %d\n", v, t->sp);
}
pushInt(t, v >> 32);
@@ -123,7 +123,7 @@ inline uint32_t
popInt(Thread* t)
{
if (DebugStack) {
- fprintf(stderr, "pop int %"ULD" at %d\n",
+ fprintf(stderr, "pop int %" ULD " at %d\n",
t->stack[((t->sp - 1) * 2) + 1],
t->sp - 1);
}
@@ -142,7 +142,7 @@ inline uint64_t
popLong(Thread* t)
{
if (DebugStack) {
- fprintf(stderr, "pop long %"LLD" at %d\n",
+ fprintf(stderr, "pop long %" LLD " at %d\n",
(static_cast<uint64_t>(t->stack[((t->sp - 2) * 2) + 1]) << 32)
| static_cast<uint64_t>(t->stack[((t->sp - 1) * 2) + 1]),
t->sp - 2);
@@ -178,7 +178,7 @@ inline uint32_t
peekInt(Thread* t, unsigned index)
{
if (DebugStack) {
- fprintf(stderr, "peek int %"ULD" at %d\n",
+ fprintf(stderr, "peek int %" ULD " at %d\n",
t->stack[(index * 2) + 1],
index);
}
@@ -192,7 +192,7 @@ inline uint64_t
peekLong(Thread* t, unsigned index)
{
if (DebugStack) {
- fprintf(stderr, "peek long %"LLD" at %d\n",
+ fprintf(stderr, "peek long %" LLD " at %d\n",
(static_cast<uint64_t>(t->stack[(index * 2) + 1]) << 32)
| static_cast<uint64_t>(t->stack[((index + 1) * 2) + 1]),
index);
@@ -228,7 +228,7 @@ inline void
pokeLong(Thread* t, unsigned index, uint64_t value)
{
if (DebugStack) {
- fprintf(stderr, "poke long %"LLD" at %d\n", value, index);
+ fprintf(stderr, "poke long %" LLD " at %d\n", value, index);
}
pokeInt(t, index, value >> 32);
@@ -461,7 +461,7 @@ pushResult(Thread* t, unsigned returnCode, uint64_t result, bool indirect)
case DoubleField:
case LongField:
if (DebugRun) {
- fprintf(stderr, "result: %"LLD"\n", result);
+ fprintf(stderr, "result: %" LLD "\n", result);
}
pushLong(t, result);
break;
Something went wrong with that request. Please try again.