Java – Cross-compiling Java app to run directly on ARM

armjavajvmoperating systemsreal time

Please note: although I am using ARM SAM3X8E in this example, I'm just using that as a concrete example, and the answer to this question could easily be given using any other MCU such as AVR, etc.

I have a crazy question. In reality, its more about confirming my knowledge of low-level computer architecture than anything else, and is not necessarily something that I'm going to endeavour to actually do.

I am interested in understanding the process and tooling involved with writing a Java app on, say, a Linux or Windows machine, and then cross-compiling that app into an image that can be directly flashed to an MCU, such as the ARM SAM3X8E. I know, crazy.

Why would I ever want to do this, would be a sane person's first response. Again:

  • This question is more about confirming/clarifying my understanding of low-level programming/computer architecture than anything else; but
  • One possible reason to do this would be in a scenario where I have to have an MCU/ARM chip running an application (not a Linux box or anything else), but the application is so complicated that to write it in C and then maintain it would be a monumental undertaking. But, in Java, with its powerful syntax, data structure and massive ecosystem of 3rd party libraries/OSS components that can be leveraged, writing the app is many order of magnitude easier.

Even if you're still not convinced of my use case above (which, I'd be interested as to why), I am still interested in seeing how such a solution could be brute forced to work.

If I had to whip together a solution without any help/guidance, here's what I would do:

  • Implement my own JVM in C (lol)
  • Ultimately a Java app is fed into a JVM as input data (bytecode); and so the source code of this custom JVM would need to be hardcoded to look for a specific input file and read bytecode out of, we'll call this input file bytecode_input.c.
  • I would need some kind of a cross compiler that took Java bytecode (produced by javac) and converted it into bytecode_input.c. This way, when compiling my custom JVM, the bytecode of my app gets integrated into the resultant JVM executable.
  • Writing my custom JVM would also require implementation of certain system calls in C code, so that when an application uses FileWriter to write to a file, the JVM knows what system calls to make
  • Now I would just need to make sure that whatever is the output of the JVM's compilation is something that can be flashed to the MCU

Unless I'm missing anything (again, understanding that the steps above are by no means trivial and each of them are probably several man-years in the making), I believe that's all one would need. Basically, I write my app in Java, compile it to bytecode, run it through the bytecode_file_generator, turning it into a C file named bytecode_input.c. That C file then gets compiled with the rest of my JVM (which contains everything a standard JVM has, GC, memory management, bytecode verifier, classloader, etc.), which produces some executable/image that can be flashed to the MCU.

If bytecode_input.c seems mysterious and vague, here's some C pseudo-code for what I mean:

// This C file simply defines a variable called 'bytecode'
// which contains a static/hardcoded table of all the app's bytecode:
int[] BYTECODE = new int[SIZE_OF_CLASS_FILE] {
    103, 1929, 1939, 549, 9939,
    3949, 3949, 20304, 28, 238
};

The JVM then reads BYTECODE as hardcoded input instead of at runtime like a normal JVM.

Typically JVMs run on top of an OS. I don't have to worry about OSes because MCUs run programs bare metal, and if I have the need for RTOS-like functionality, I could just build that into the custom JVM.

I could (I suppose) just craft a very clever cross-compiler that converted my app's Java source into C, and skip involving a custom JVM altogether. But then I'd be losing out on all the good things the JVM provides: GC, memory management, etc.

So my question: assuming you can push the magical "Ok, I'll accept this as a valid use case for now…" button, is my approach here fundamentally wrong? Am I overlooking any major steps/processes?

Best Answer

You can compile Java to binary for a target using LLVM. A quick look at the docs shows support for many ARM variants.

https://llvm.org/svn/llvm-project/java/trunk/docs/java-frontend.txt

Also another stack-overflow style answer in this vein at

https://stackoverflow.com/questions/10804280/is-there-a-llvm-java-front-end-that-converts-java-source-to-llvms-intermediate