GMP is a free library for arbitrary precision arithmetic, extremely useful for cryptography applications and research, Internet security applications, etc. In order to use this highly efficient library, one must code in C/C++. This is a Java port for fellow Java developers who dislike thinking about pointers.
I am providing some base code that any developer can build upon to link desired functions from libgmp and call them within Java classes. The code uses JNA technology, a much more elegant alternative to JNI (the latter requires Java developers to write C code…).
My Java code borrows heavily and shamelessly from the jna-gmp project on GitHub. I cleaned it up a bit to make it more obvious how to enable additional functionality, and added more useful functions for integer arithmetic that are commonly used in cryptography.
Using my code on a Linux system is easy, as compiling libgmp is pretty straightforward. However, I ran into issues when trying to compile the library on Windows using Cygwin. Here I describe the process of compiling and using libgmp within Java on a Windows platform.
Requirements
You will need the following:
- Cygwin installation on Windows (https://cygwin.com/)
- libgmp source code (https://gmplib.org/)
- msvcrt.dll (your system may already have this)
- Java Native Access Library (java-native-access)
Compiling libgmp on Windows
For the compilation, I used the Cygwin environment (32-bit version) on Windows 10. Simply install all the dev tools available in Cygwin. It may work just as well with the 64-bit version, but I haven’t tested it.
The main issue with compiling libgmp using Cygwin’s GCC is that the dll library generated is dependent on cygwin1.dll. However, cygwin1.dll cannot be dynamically loaded as it expects to have access to the 4K bytes at the bottom of the stack when initializing. This causes an EXCEPTION_ACCESS_VIOLATION
at runtime.
Older versions of GCC used to allow the “-mno-cygwin” flag to circumvent this issue, but this is now obsolete (it dates back to GCC 3.x). After a lot of searching on the web, the consensus seems to be that it is impossible to load cygwin1.dll using JNA (Java Native Access), so that prevents us from loading any dll file that was compiled with Cygwin’s GCC.
There is a workaround: use the mingw compiler that comes with Cygwin instead. This will ensure the generated dll is dependent on Microsoft’s msvcrt.dll library, rather than the cygwin one. Here are the correct compilation steps:
followed by
This will generate libgmp-10.dll in the .libs subfolder. The configure script generates a static library by default, so you need the --disable-static --enable-shared
config options ensure that you get a .dll file as output, and the --host=x86_64-w64-mingw32
ensures that you use the mingw compiler, avoiding the troublesome cygwin1.dll dependency.
This workaround solves any general dependency issues for anything compiled under Cygwin, not just libgmp. Your resulting .dll files can now be loaded, and they will depend on msvcrt.dll instead.
Adding extra functions
The first step for adding new functionality is to create the appropriate method in LibGMP.java. For example, to use the integer multiplication
void mpz_mul (mpz_t rop, const mpz_t op1, const mpz_t op2)
, you would add the following line of code to LibGMP.java:
Next up is editing GMP.java, the actual class we will be using to safely call libgmp functions in our Java code. First we add the import of the function we just created above:
Then we create two methods:
The first static method is visible to us, and simply calls the second one through an instance of the GMP class that is automatically created at runtime (the static code at the top of GMP.java does this for you, no need to worry about it).
The private Impl method is where each function will differ. Because there needs to be a conversion between Java’s BigInteger type and the C mpz_t type, you need to make sure you allocate enough bits for the result when translating back into BigInteger. When in doubt, add more bits (unless you want a memory efficient app, then you need to be more careful). The Impl method also contains the actual call to the GMP library.
For integer multiplication, if you want to multiply two integers of and bits long, the result will be at most
$$
(2^a - 1)(2^b - 1) = 2^{a+b} - 2^a - 2^b + 1 \leq 2^{a+b} - 1
$$
so we need at most bits to store it. This is reflected in the code for multiplyImpl()
:
With all this in place, you are ready to call the function: simply do GMP.multiply(a,b)
where a,b are of BigInteger type. The method returns a BigInteger.
Correct .dll and .jar location
You need to specify where libgmp-10.dll is located at runtime. To do this, set up your project run configuration to include the -D JVM parameter
-Djna.library.path="C:\dev\lib\dll"
(on my dev environment, I keep all my .dll files in C:\dev\lib\dll but you may modify that as you wish).
You will also need to have jna.jar and win32-x86-64.jar (these come with JNA) in your build path (and at runtime). If you’re using Eclipse, all of this can be changed under Project Properties (add jars to build path, and modify the runtime configuration for loading the dll).
Testing
Test.java
runs some very basic arithmetic operations on two BigIntegers. Run it to see if you get the expected results (and no errors). Also this is a good example of how you should call the GMP class. I get the following output:
TestPerformance.java
compares computation time between Java’s BigInteger functions and GMP. Notice that simple arithmetic operations are more efficient in Java, probably due to the overhead necessary to call GMP. However, number theoretic functions and group operations are executed significantly faster via the native GMP call. Here’s an output sample on my system: