JDK-6647068 : libjvm.so is not built PIC
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 6
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: linux
  • CPU: x86
  • Submitted: 2008-01-04
  • Updated: 2012-05-09
  • Resolved: 2011-03-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7 Other
6u14Fixed 7Fixed hs14Fixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_01"
Java(TM) SE Runtime Environment (build 1.6.0_01-b06)
Java HotSpot(TM) Server VM (build 1.6.0_01-b06, mixed mode)


FULL OS VERSION :
Linux xx.xx.xx.xx 2.4.21-40.ELsmp #1 SMP Thu Feb 2 22:22:39 EST 2006 i686 i686 i386 GNU/Linux


A DESCRIPTION OF THE PROBLEM :
We launch java from within a large and complex native application. By the time we launch java, the native app has already loaded a very large number of libraries.

One of these libraries (or some prior memory allocation) has reserved memory through virtual address 0x6000000. This is a problem because the libjvm.so for IA32 is not built PIC-style. Instead it uses the address 0x6000000 as its base. The loader will load the libjvm.so and - since 0x6000000 is already in use - will relocate it. This inevitably then causes a crash.

THE PROBLEM WAS REPRODUCIBLE WITH -Xint FLAG: Yes

THE PROBLEM WAS REPRODUCIBLE WITH -server FLAG: Yes

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Take the source code below and compile and run as shown in the comments. For java 6 this crashes, but for java 5 it works. Note that the mmap() is used here to simulate the effect of some other component reserving address 0x6000000 before the JVM is started. This would normally happen as libraries are loaded.



EXPECTED VERSUS ACTUAL BEHAVIOR :
Should run like this:

bash-2.05b$  ./invoke
Before calling JNI_CreateJavaVM : 0.6507886317570846
After calling JNI_CreateJavaVM : 0.6507886317570846
After calling DestroyJavaVM : 0.6507886317570846


Actually does this:

bash-2.05b$  ./invoke
Before calling JNI_CreateJavaVM : 0.6507886317570846
./invoke: relocation error: ./invoke: symbol JNI_CreateJavaVM, version SUNWprivate_1.1 not defined in file libjvm.so with link time reference
REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
/*
 * To build : cc -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -L$JAVA_HOME/jre/lib/i386 -L$JAVA_HOME/jre/lib/i386/server  -o invoke invoke.c -ljava -lpthread -ljvm -lverify -lm
 *
 * To run : export LD_LIBRARY_PATH=$JAVA_HOME/jre/lib/i386:$JAVA_HOME/jre/lib/i386/server
 *          ./server
 *
*/

#include <jni.h>
#include <math.h>
#include <sys/mman.h>

#define PATH_SEPARATOR ':'
#define USER_CLASSPATH "."

double testFn() {
        double X,Z;

        return tan(1000000.0/M_PI);
}

main(int argc, char *argv[]) {
        JNIEnv *env;
        JavaVM *jvm;
        jint res;
        JavaVMInitArgs vm_args;
        JavaVMOption options[10];
        int nOptions;
        int i;

        mmap(0x6000000, 0x1000000, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_SHARED, 0, 0);
        printf("Before calling JNI_CreateJavaVM : %.16g\n", testFn());
        nOptions = 0;
        options[nOptions++].optionString = "-Djava.class.path=" USER_CLASSPATH;
        vm_args.version = 0x00010002;
        vm_args.options = options;
        vm_args.nOptions = nOptions;
        vm_args.ignoreUnrecognized = JNI_TRUE;

        res = JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);

        if (res < 0) {
                fprintf(stderr, "Can't create Java VM\n");
                exit(1);
        }
        printf("After calling JNI_CreateJavaVM : %.16g\n", testFn());

        (*jvm)->DestroyJavaVM(jvm);
        printf("After calling DestroyJavaVM : %.16g\n", testFn());
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Use java 5.

Release Regression From : 6
The above release value was the last known release where this 
bug was not reproducible. Since then there has been a regression.

Comments
EVALUATION One way to fix this is to remove the base address (0x06000000) built into the current libjvm.so. Based on the measurements, there is about 4.01% regression on real footprint and 3.89% regression on startups. This is mostly due to the extra relocations done by the runtime linker & loader (ldd.so) due to the lack of the base address. No other regressions are seen so far. To reduce the impact of the regression on footrpint, I also tried the partial PIC approach just as what we did for Solaris. This can bring down the real footprint regression to 1.3%. But the startup regression is still there. In conclusion, I believe the less risky thing we could do is to use the same approach as we do for Solaris, using partial PIC and don't build a base address inside the libjvm.so.
31-01-2008

EVALUATION This change was done by a previous runtime team member in order to improve GC pause time performance around JDK 6 timeframe. The relevant change is at <hotspot workspace>/build/linux/makefiles/gcc.make file. 0x60000000 was taken as the perferred base address for libjvm.so and that is how the VM creation failed in the attached example. Anyways, the ultimate solution for this might be to build the fat launcher or we could take the approach I took for Solaris which is the partial PIC.
04-01-2008