Java Manifest Crash Course

I’m recently learning about AWS features and one of them are “Lambdas”. Those are functions that can be deployed and triggered on various events (like HTTP request, or file upload), but you don’t have to maintain the servers that run them. By default AWS provides several runtimes like NodeJS, Python and Java. Since I’m recently a bit tired with Python, I’ve decided to refresh my Java knowledge. The first step would be to understand the Java manifest files.

Jar (not necessarily java jar)

What is Java Manifest?

While you can find some description of manifests in Oracle’s official docs, they don’t really seem to be something that gives you an idea, what it is:

When you create a JAR file, it automatically receives a default manifest file. There can be only one manifest file in an archive, and it always has the pathname.

Honestly? I didn’t have any clue what to do with that info. So what are manifests?

Well, think about it that way: when you compile java source code, you get binary class files. Those files are understood by the Java Virtual Machine, which is something that actually runs it. Just as you type python main.py to run Python script, you can execute those binaries by running javac Main.java and then java Main.class to compile and run Java source code (the link to the example repo is at the end of this article). The problem with this approach is that in big projects, you’re going to have tons of those class files and you’d like to pack them into a single file that you can run.

Indeed, someone has thought about solving this problem and has designed the jar files. Jars contain compressed code. You can create your jar by running jar cf Main.jar Main.class. It creates Main.jar out of the Main.class file. Cool. Let’s run it.

$ java -jar Main.jar
no main manifest attribute, in Main.jar

Why Doesn’t My jar Work?

As you can see there’s some problem with the manifest file. Remember when I said, that jar is a compressed file? Well, it’s a zip. So we can actually browse its contents with tools like vim by running vim Main.jar.

" zip.vim version v28
" Browsing zipfile /home/gonczor/Projects/blog/Jars/Main.jar
" Select a file with cursor and press ENTER

META-INF/
META-INF/MANIFEST.MF
Main.class

OK, some manifest has been created for us. Let’s look at its contents.

Manifest-Version: 1.0
Created-By: 11.0.10 (Ubuntu)

The problem is that it does not tell the JVM where it should start executing the jar. There’s no entrypoint. In Assembly, we expect an address of the first instruction in the data section, here we expect the name of the place to start the execution (you may want to look at some reverse engineering stuff I did or look at some assemblies).

Luckily, we can add such info to the Java Manifest file. Let’s create a text file called manifest.txt:

Main-Class: Main

And merge it into current jar. To do this we need one extra parameter to our command.

jar -cfm Main.jar manifest.txt Main.class

The m letter tells to add contents of the manifest.txt to the Main.jar, which will also include the Main.class. Does it work now?

$ java -jar Main.jar
Hello, world!

Summary

Jars are way more powerful than just creating executables. They can help to sign the code and do other magic that is currently beyond my comprehension. I hope, however, that you got your “AHA” moment, just like I got mine a few minutes ago.

That’s all folks, happy hacking.

Additional resources

Latest posts