[kaffe] trampoline/class loader/misc fixes

Timothy Stack stack at flux.utah.edu
Mon Aug 12 15:43:30 PDT 2002


hi,

I've attached a bunch of patches for the aforementioned trampoline and
class loader problems as well as some miscellaneous fixes.  Also, I
changed the current behaviour for missing static field references so
that the method will throw an exception at the reference and not when
the method is jitted, this is similar to what jdk1.3.1 does.  For
example, the method:

void referenceMissingClass()
{
    try
    {
	MissingClass.foo = 4;
    }
    catch(NoClassDefError e)
    {
    }
}

Would fail to compile previously, now it will compile, but the
reference to "foo" will cause a NoSuchClassDefError to be thrown.

Heres the rest of the ChangeLog:

  config/i386/trampolines.c:  Create a real stack frame for
  i386_do_fixup_trampoline so it doesn't mask the caller's frame.

  kaffe/kaffeh/support.c: startFields/startMethods now return
  success/failure and take errorInfo's.

  kaffe/kaffevm/baseClasses.[ch]: Load in ClassNotFoundException and
  NoClassDefFoundError.

  kaffe/kaffevm/classMethod.[ch]: startFields/startMethods now return
  success/failure and take errorInfo's.  Use postExceptionMessage in
  addMethod/addField before returning failure.  Post a
  NoClassDefFoundError if ClassLoader.loadClass() throws a
  ClassNotFoundException.

  kaffe/kaffevm/code-analyse.c: Check for NoClassDefFoundErrors in
  PUTSTATIC/GETSTATIC handlers.

  kaffe/kaffevm/kaffe.def: Handle unknown classes in field references
  using softcall_nosuchclass() instead of failing/throwing an
  exception.

  kaffe/kaffevm/lookup.c: For getField(), set ret->field/ret->class to
  NULL and set ret->cname/name/signature from the information
  available in the current class so that a failure will not leave
  these fields undefined.

  kaffe/kaffevm/readClass.c: check return values of
  startFields()/startMethods()

  kaffe/kaffevm/[intrp/jit/jit3]/machine.h: Add
  get_static_field_info_noerror() define.

  test/regression/LostTrampolineFrame.java: Test for x86 lost
  trampoline frame bug.

  test/regression/NoClassDefTest.java: Tests for NoClassDefFoundErrors
  in combination with ClassLoaders.

  test/regression/Makefile.[am|in]: Add LostTrampolineFrame.java and
  NoClassDefTest.java tests.

I ran it through FullTest.sh and it resulted only in known failures
(stringUninternString race and the linker shuffling JNI functions
around).

thanks,

tim stack
-------------- next part --------------

import java.io.File;
import java.io.FileInputStream;

/**
 * Test for NoClassDefFoundErrors when ClassLoaders are involved.
 */
public class NoClassDefTest
    extends ClassLoader
{
    /**
     * A class that is visible during the compile, but will be missing at run
     * time.
     */
    public static class MissingClass
    {
	/**
	 * A static field we can reference to force a NoClassDefFoundError.
	 */
	public static int foo = 1;

	/**
	 * A method we can reference to force a NoClassDefFoundError.
	 */
	public MissingClass()
	{
	}
    }

    /**
     * This is an intermediate class we use to reference the missing class'
     * method.
     */
    public static class MethodReferenceClass
	implements Runnable
    {
	public MethodReferenceClass()
	{
	}

	public void run()
	{
	    try
	    {
		new MissingClass();
	    }
	    catch(NoClassDefFoundError e)
	    {
		System.out.println("Caught NoClassDefError for method "
				   + "MissingClass.<init> in class: "
				   + e.getMessage());
	    }
	}
    }

    /**
     * This is an intermediate class we use to reference the missing class'
     * field.
     */
    public static class FieldReferenceClass
	implements Runnable
    {
	public FieldReferenceClass()
	{
	}

	public void run()
	{
	    try
	    {
		MissingClass.foo = 1;
	    }
	    catch(NoClassDefFoundError e)
	    {
		System.out.println("Caught NoClassDefError for field "
				   + "MissingClass.foo in class: "
				   + e.getMessage());
	    }
	}
    }

    /**
     * This is an intermediate class we use to test a cast of missing class.
     */
    public static class CastReferenceClass
	implements Runnable
    {
	public CastReferenceClass()
	{
	}

	public void run()
	{
	    try
	    {
		Object obj = null;

		obj = (MissingClass)obj;
	    }
	    catch(NoClassDefFoundError e)
	    {
		System.out.println("Caught NoClassDefError for cast of class: "
				   + e.getMessage());
	    }
	}
    }

    /**
     * Read in the byte code for the given class.
     *
     * @param name The name of the class to read in.
     * @return The byte code for the class.
     */
    static byte [] readin(String name) throws Exception
    {
        File cf = new File(name);
        FileInputStream cfi = new FileInputStream(cf);

        int len = (int)cf.length();
        byte [] cb = new byte[len];
        if (cfi.read(cb) != len)
            throw new Exception("short read for " + name);
        return cb;
    }
    
    public synchronized Class loadClass(String name, boolean resolve)
	throws ClassNotFoundException
    {
	Class retval = null;

	if( !name.startsWith("NoClassDefTest") )
	{
	    /* System class... */
	    retval = super.findSystemClass(name);
	}
	else if( !name.equals("NoClassDefTest$MissingClass") )
	{
	    /* One of our class' and not the MissingClass. */
	    try
	    {
		byte []b = readin(name + ".class");
		retval = defineClass(name, b, 0, b.length);
	    }
	    catch(Exception e)
	    {
		throw new ClassNotFoundException(name);
	    }
	}
	else
	{
	    throw new ClassNotFoundException(name);
	}
	if( resolve )
	    super.resolveClass(retval);
	return retval;
    }

    public static void main(String args[])
	throws Throwable
    {
	NoClassDefTest test;
	Runnable run;
	Class cl;

	/* Create our ClassLoader */
	test = new NoClassDefTest();

	/* Load the intermediate classes and force NoClassDefFoundErrors. */
	
	cl = test.loadClass("NoClassDefTest$MethodReferenceClass");
	run = (Runnable)cl.newInstance();
	run.run();
	cl = test.loadClass("NoClassDefTest$FieldReferenceClass");
	run = (Runnable)cl.newInstance();
	run.run();
	cl = test.loadClass("NoClassDefTest$CastReferenceClass");
	run = (Runnable)cl.newInstance();
	run.run();
    }
}

/* Expected Output:
Caught NoClassDefError for method MissingClass.<init> in class: NoClassDefTest$MissingClass
Caught NoClassDefError for field MissingClass.foo in class: NoClassDefTest$MissingClass
Caught NoClassDefError for cast of class: NoClassDefTest$MissingClass
*/
-------------- next part --------------

import java.util.Vector;

import java.io.File;
import java.io.FileInputStream;

/**
 * This class will check for a bug in the x86 trampoline (and possibly others)
 * that caused kaffe to ignore the stack frame of a routine that jumped through
 * a trampoline.  By "ignore", we mean that an exception handler in the calling
 * routine wasn't found and executed.
 */
class LostTrampolineFrame
    extends ClassLoader
{
    /**
     * We use a bad class to get the jitter to throw an exception through the
     * trampoline.  The following byte code was generated by disassembling the
     * DamagedClass class below, tweaking some values, and then reassembling
     * it.
     */
    public static byte damagedByteCode[] = {
        (byte)0xca, (byte)0xfe, (byte)0xba, (byte)0xbe,
        (byte)0x0, (byte)0x3, (byte)0x0, (byte)0x2d,
        (byte)0x0, (byte)0xd, (byte)0xa, (byte)0x0,
        (byte)0x4, (byte)0x0, (byte)0xa, (byte)0x1,
        (byte)0x0, (byte)0x4, (byte)0x43, (byte)0x6f,
        (byte)0x64, (byte)0x65, (byte)0x7, (byte)0x0,
        (byte)0x8, (byte)0x7, (byte)0x0, (byte)0x9,
        (byte)0x1, (byte)0x0, (byte)0x6, (byte)0x3c,
        (byte)0x69, (byte)0x6e, (byte)0x69, (byte)0x74,
        (byte)0x3e, (byte)0x1, (byte)0x0, (byte)0xa,
        (byte)0x53, (byte)0x6f, (byte)0x75, (byte)0x72,
        (byte)0x63, (byte)0x65, (byte)0x46, (byte)0x69,
        (byte)0x6c, (byte)0x65, (byte)0x1, (byte)0x0,
        (byte)0xf, (byte)0x4c, (byte)0x69, (byte)0x6e,
        (byte)0x65, (byte)0x4e, (byte)0x75, (byte)0x6d,
        (byte)0x62, (byte)0x65, (byte)0x72, (byte)0x54,
        (byte)0x61, (byte)0x62, (byte)0x6c, (byte)0x65,
        (byte)0x1, (byte)0x0, (byte)0x20, (byte)0x4c,
        (byte)0x6f, (byte)0x73, (byte)0x74, (byte)0x54,
        (byte)0x72, (byte)0x61, (byte)0x6d, (byte)0x70,
        (byte)0x6f, (byte)0x6c, (byte)0x69, (byte)0x6e,
        (byte)0x65, (byte)0x46, (byte)0x72, (byte)0x61,
        (byte)0x6d, (byte)0x65, (byte)0x24, (byte)0x44,
        (byte)0x61, (byte)0x6d, (byte)0x61, (byte)0x67,
        (byte)0x65, (byte)0x64, (byte)0x43, (byte)0x6c,
        (byte)0x61, (byte)0x73, (byte)0x73, (byte)0x1,
        (byte)0x0, (byte)0x10, (byte)0x6a, (byte)0x61,
        (byte)0x76, (byte)0x61, (byte)0x2f, (byte)0x6c,
        (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2f,
        (byte)0x4f, (byte)0x62, (byte)0x6a, (byte)0x65,
        (byte)0x63, (byte)0x74, (byte)0xc, (byte)0x0,
        (byte)0x5, (byte)0x0, (byte)0xc, (byte)0x1,
        (byte)0x0, (byte)0x18, (byte)0x4c, (byte)0x6f,
        (byte)0x73, (byte)0x74, (byte)0x54, (byte)0x72,
        (byte)0x61, (byte)0x6d, (byte)0x70, (byte)0x6f,
        (byte)0x6c, (byte)0x69, (byte)0x6e, (byte)0x65,
        (byte)0x46, (byte)0x72, (byte)0x61, (byte)0x6d,
        (byte)0x65, (byte)0x2e, (byte)0x6a, (byte)0x61,
        (byte)0x76, (byte)0x61, (byte)0x1, (byte)0x0,
        (byte)0x3, (byte)0x28, (byte)0x29, (byte)0x56,
        (byte)0x0, (byte)0x21, (byte)0x0, (byte)0x3,
        (byte)0x0, (byte)0x4, (byte)0x0, (byte)0x0,
        (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x1,
        (byte)0x0, (byte)0x1, (byte)0x0, (byte)0x5,
        (byte)0x0, (byte)0xc, (byte)0x0, (byte)0x1,
        (byte)0x0, (byte)0x2, (byte)0x0, (byte)0x0,
        (byte)0x0, (byte)0x21, (byte)0x0, (byte)0x0,
        (byte)0x0, (byte)0x1, (byte)0x0, (byte)0x0,
        (byte)0x0, (byte)0x5, (byte)0x2a, (byte)0xb7,
        (byte)0x0, (byte)0x1, (byte)0xb1, (byte)0x0,
        (byte)0x0, (byte)0x0, (byte)0x1, (byte)0x0,
        (byte)0x7, (byte)0x0, (byte)0x0, (byte)0x0,
        (byte)0xa, (byte)0x0, (byte)0x2, (byte)0x0,
        (byte)0x0, (byte)0x0, (byte)0x53, (byte)0x0,
        (byte)0x4, (byte)0x0, (byte)0x54, (byte)0x0,
        (byte)0x1, (byte)0x0, (byte)0x6, (byte)0x0,
        (byte)0x0, (byte)0x0, (byte)0x2, (byte)0x0,
        (byte)0xb
    };

    /**
     * Read in the byte code for the given class.
     *
     * @param name The name of the class to read in.
     * @return The byte code for the class.
     */
    static byte [] readin(String name) throws Exception
    {
        File cf = new File(name);
        FileInputStream cfi = new FileInputStream(cf);

        int len = (int)cf.length();
        byte [] cb = new byte[len];
        if (cfi.read(cb) != len)
            throw new Exception("short read for " + name);
        return cb;
    }
    
    public synchronized Class loadClass(String name, boolean resolve)
	throws ClassNotFoundException
    {
	Class retval = null;

	if( !name.startsWith("LostTrampolineFrame") )
	{
	    /* System class... */
	    retval = super.findSystemClass(name);
	}
	else if( name.equals("LostTrampolineFrame$DamagedClass") )
	{
	    /* Load our damaged class file. */
	    retval = defineClass(name,
				 damagedByteCode,
				 0,
				 damagedByteCode.length);
	}
	else
	{
	    /* One of our class' and not the DamagedClass. */
	    try
	    {
		byte []b = readin(name + ".class");
		retval = defineClass(name, b, 0, b.length);
	    }
	    catch(Exception e)
	    {
		throw new ClassNotFoundException(name);
	    }
	}
	if( resolve )
	    super.resolveClass(retval);
	return retval;
    }

    /**
     * The original class that was used to generate damagedByteCode.  NOTE:
     * This is not actually used.
     */
    public static class DamagedClass
    {
	public DamagedClass()
	{
	}
    }

    /**
     * An intermediate class that will try to construct a DamagedClass object.
     */
    public static class IntermediateClass
	implements Runnable
    {
	public IntermediateClass()
	{
	}

	public void run()
	{
	    try
	    {
		/* The constructor shouldn't verify ... */
		new DamagedClass();
	    }
	    catch(VerifyError e)
	    {
		/*
		 * If the VerifyError isn't caught here, that means the
		 * trampoline is hiding the stack frame.
		 */
		System.out.println("Success");
	    }
	}
    }

    public static void main(String args[])
	throws Throwable
    {
	LostTrampolineFrame ltf;
	Runnable run;
	Class cl;
	
	ltf = new LostTrampolineFrame();
	cl = ltf.loadClass("LostTrampolineFrame$IntermediateClass");
	try
	{
	    run = (Runnable)cl.newInstance();
	    run.run();
	}
	catch(VerifyError e)
	{
	    /*
	     * If the VerifyError is caught here, that means the trampoline is
	     * hiding the stack frame.
	     */
	    System.out.println("Bad trampoline");
	}
    }
}

/* Expected Output:
Success
*/
-------------- next part --------------
A non-text attachment was scrubbed...
Name: trcl.diff
Type: application/octet-stream
Size: 20240 bytes
Desc: ASCII C++ program text, with very long lines
Url : http://kaffe.org/pipermail/kaffe/attachments/20020812/a061ff91/attachment-0003.obj 


More information about the kaffe mailing list