# The Ibex Makefile
#
+# auto directory creation with %/%: %?
+
target_Darwin := powerpc-apple-darwin
target_Win32 := i686-pc-mingw32
target_Solaris := sparc-sun-solaris2.7
build/$(platform)/%.java.o: build/class/%.class
@echo -e "\n\033[1mcompiling .java -> .o: $<\033[0m"
mkdir -p `dirname $@`
- $(gcj) -fCLASSPATH=build/class -c $< -o $@
+ $(gcj) -fCLASSPATH=build/pruned -c $< -o $@
# FIXME detect subclasses of X11
build/$(platform)/org/ibex/plat/Linux.cc.o: .install_WindowMaker-0.80.2_$(target)
# note: binaries appear in a different order in the dependency line vs the link line
build/$(platform)/$(target_bin): upstream/jpeg-6b/build-$(target)/libjpeg.a build/$(platform)/builtin.o build/$(platform)/org/ibex/plat/$(platform).cc.o
@echo -e "\n\033[1mlinking .o -> $(target_bin)\033[0m"
+ find upstream/gcc-3.3/build-i686-pc-linux-gnu/i686-pc-linux-gnu/libjava/ \
+ -name nat\*.o -or \( -name \*.o -not -name '*[A-Z]*' \) -exec nm {} \; |\
+ grep _ZN | c++filt --format java | grep " U " | sed 's_\.class\$$_.class_' | sed 's_ * U __' | sed 's_(.*__' \
+ > .natcalls
make $(java_sources)
rm -rf build/pruned; mkdir -p build/pruned
javac -classpath lib/bcel-5.1.jar:build/class -d build/class src/org/ibex/util/BytecodePruner.java
org/ibex/plat/GCJ*.class \
org/ibex/plat/POSIX*.class
java -cp lib/bcel-5.1.jar:build/class org.ibex.util.BytecodePruner \
- build/$(platform)/ibex.jar:upstream/install/share/java/libgcj-3.3.jar -o build/pruned
-# rm -rf out/java/awt
-# rm -f out/java/lang/*.*
-# rm -f out/java/lang/reflect/*
-# mkdir -p out/java/lang/ref
-# mkdir -p out/java/lang/reflect
-# cp upstream/gcc-3.3/src/libjava/java/lang/*.java out/java/lang/
-# cp upstream/gcc-3.3/src/libjava/java/lang/ref/*.java out/java/lang/ref/
-# cp upstream/gcc-3.3/src/libjava/java/lang/reflect/*.java out/java/lang/reflect/
+ build/$(platform)/ibex.jar:upstream/install/share/java/libgcj-3.3.jar \
+ -o build/pruned \
+ `cat .natcalls`
+
rm -rf build/pruned/org/ibex/js/*; cp build/java/org/ibex/js/* build/pruned/org/ibex/js/
+
rm build/pruned/java/lang/System.class
cp upstream/gcc-3.3/src/libjava/java/lang/System.java build/pruned/java/lang/
- cd build/pruned; \
- for A in `find . -name \*.class -or -name \*.java`; do \
- echo compiling $$A....; \
- ../../upstream/install/bin/$(target)-gcj -w -c -fCLASSPATH=../../build/class $$A && \
+
+ rm build/pruned/gnu/gcj/runtime/FirstThread*.class
+ cp upstream/gcc-3.3/src/libjava/gnu/gcj/runtime/FirstThread.java build/pruned/gnu/gcj/runtime/
+
+ rm build/pruned/org/ibex/plat/GCJ*.class
+ cp build/java/org/ibex/plat/GCJ.java build/pruned/org/ibex/plat/
+
+ cp upstream/gcc-3.3/build-$(target)/$(target)/libjava/java/lang/Object.class build/pruned/java/lang/
+
+ find build/pruned/gnu/java/locale/ -name 'LocaleInformation_*' -not -name 'LocaleInformation_en.class' -not -name 'LocaleInformation_en_US.class' -exec rm {} \;
+
+# cd build/pruned; fastjar cvf ../../tmp.jar .
+# java -jar lib/jarg.jar -verbose -verboseufm -normlv -normsf -normsy -normin \
+# -normex -nornc -nornf -nornm -nobco tmp.jar
+# cd build/pruned; fastjar xvf ../../tmp.jar
+
+# try oneshot compile/link
+# --redefine-sym to merge errorthrowers? or do this in C++?
+# --rename-section to try to merge Utf8 sections?
+
+ cd build/pruned; \
+ for A in `find . -name \*.class -or -name \*.java`; do \
+ echo compiling $$A....; \
+ ../../upstream/install/bin/$(target)-gcj \
+ -fdata-sections -ffunction-sections -w -c -Os \
+ -fCLASSPATH=../../build/$(platform)/ibex.jar $$A && \
(mkdir -p ../../build/$(platform)/`dirname $$A`; mv *.o ../../build/$(platform)/`dirname $$A`); \
done
rm -f build/$(platform)/ibex.a
cd upstream/gcc-3.3/build-$(target)/$(target)/libjava; \
$(shell pwd)/upstream/install/$(target)/bin/ar cq \
$(shell pwd)/build/$(platform)/ibex.a \
- `find . -name nat\*.o` \
+ boehm.o defineclass.o exception.o \
+ posix-threads.o posix.o prims.o resolve.o \
+ `find . -name nat\*.o | grep -v JIS` \
`find . -name \*.o -not -name '*[A-Z]*'` \
- $(shell pwd)/build/$(platform)/org/ibex/plat/$(platform).cc.o \
`find $(shell pwd)/build/$(platform) -name \*.o`
PATH=upstream/install/bin:$$PATH upstream/install/bin/$(target)-gcj \
--main=org.ibex.plat.$(platform) \
-Lupstream/install/$(target)/lib \
-Lupstream/install/lib \
+ -ffunction-sections -fdata-sections -Os -w -fCLASSPATH=build/$(platform)/ibex.jar \
build/$(platform)/ibex.a \
upstream/jpeg-6b/build-$(target)/libjpeg.a \
$(link_flags) \
upstream/gcc-3.3/build-$(target)/$(target)/boehm-gc/.libs/libgcjgc.a \
-lz -ldl -lffi \
+ -Wl,--noinhibit-exec,--gc-sections \
-o $@
+# linker
+# --no-whole-archive,--relax,-O,2
+#
+# -fomit-frame-pointer \
+# -fno-force-mem \
+# -fno-force-addr \
+# -fno-inline-functions \
+# -fnew-ra \
+# -fbranch-probabilities \
+# -finline-limit=1 \
+# -fno-schedule-insns \
+# -fno-optimize-sibling-calls \
+# -fno-if-conversion \
+# -fno-thread-jumps \
+# -fno-hosted \
### Builtin Resources ##############################################################################
--- /dev/null
+package org.ibex.util;
+import java.util.*;
+import java.io.*;
+import java.util.zip.*;
+import org.apache.bcel.*;
+import org.apache.bcel.generic.*;
+import org.apache.bcel.classfile.*;
+import org.apache.bcel.util.*;
+
+// Reachability rules:
+
+// - a constructor is reachable iff it is called
+// - a static method is reachable iff it is called
+// - a nonstatic method is reachable
+// - a static field is reachable iff it is referenced
+// - a nonstatic field is reachable iff it is referenced
+// - <clinit> is reachable iff any methods, static methods, fields, or constructors are reachable
+
+// - if a method is reachable, all the methods it overrides are reachable
+
+// FIXME: nonstatic method invocation or field access implies that object will be constructed (ie hint)
+
+public class BytecodePruner {
+
+ // FIXME
+ public static SyntheticRepository repo = null;
+
+ public static HashSet dest = new HashSet();
+
+ public static String outdir = ".";
+
+ public void loadAllMethods(String classname) throws Exception {
+ visitJavaClass(repo.loadClass(classname));
+ Method[] meths = repo.loadClass(classname).getMethods();
+ for(int i=0; i<meths.length; i++) visitJavaMethod(repo.loadClass(classname), meths[i]);
+ }
+ public void loadMethod(String classAndMethodName) throws Exception {
+ String classname = classAndMethodName.substring(0, classAndMethodName.lastIndexOf('.'));
+ String methodname = classAndMethodName.substring(classAndMethodName.lastIndexOf('.') + 1);
+ visitJavaClass(repo.loadClass(classname));
+ Method[] meths = repo.loadClass(classname).getMethods();
+ for(int i=0; i<meths.length; i++)
+ if (meths[i].getName().equals(methodname))
+ visitJavaMethod(repo.loadClass(classname), meths[i]);
+ }
+ public static void main(String[] s) throws Exception {
+ int start = 1;
+ if (s.length >= 3 && s[1].equals("-o")) { outdir = s[2]; start += 2; }
+ repo = SyntheticRepository.getInstance(new ClassPath(s[0]));
+ BytecodePruner bcp = new BytecodePruner();
+
+ for(int i=start; i<s.length; i++) {
+ try {
+ if (s[i].endsWith(".class")) {
+ bcp.visitJavaClass(repo.loadClass(s[i].substring(0, s[i].length() - 6)));
+ } else {
+ JavaClass cl = repo.loadClass(s[i].substring(0, s[i].lastIndexOf('.')));;
+ bcp.visitJavaClass(cl);
+ Method[] meths = cl.getMethods();
+ for(int j=0; j<meths.length; j++) {
+ if (meths[j].getName().equals(s[i].substring(s[i].lastIndexOf('.') + 1)))
+ bcp.visitJavaMethod(cl, meths[j]);
+ }
+ }
+ } catch (Exception e) {
+ System.out.println("WARNING: couldn't load class for " + s[i]);
+ }
+ }
+ System.out.println("\n\n======================================================================\n");
+
+ // we call start(), but the VM calls run()...
+ bcp.loadMethod("java.lang.Thread.run");
+
+ bcp.loadAllMethods("java.lang.Throwable");
+ bcp.loadAllMethods("java.io.PrintStream");
+ bcp.loadAllMethods("gnu.gcj.runtime.StringBuffer");
+ bcp.loadAllMethods("java.security.cert.Certificate");
+
+ bcp.loadMethod("java.util.SimpleTimeZone.useDaylightTime");
+ bcp.loadMethod("java.util.TimeZone.getAvailableIDs");
+ bcp.loadMethod("java.util.TimeZone.getDefaultTimeZoneId");
+ bcp.loadMethod("java.util.Collections$SynchronizedIterator.hasNext");
+ bcp.loadMethod("java.util.Hashtable$HashIterator.hasNext");
+
+ bcp.visitJavaClass(repo.loadClass("java.util.Stack"));
+ bcp.visitJavaClass(repo.loadClass("gnu.classpath.Configuration"));
+ bcp.visitJavaClass(repo.loadClass("gnu.gcj.runtime.JNIWeakRef"));
+
+ bcp.visitJavaClass(repo.loadClass("gnu.gcj.protocol.http.Handler"));
+ bcp.visitJavaClass(repo.loadClass("gnu.gcj.protocol.file.Handler"));
+ bcp.visitJavaClass(repo.loadClass("gnu.gcj.protocol.jar.Handler"));
+ bcp.visitJavaClass(repo.loadClass("gnu.gcj.protocol.core.Handler"));
+ bcp.visitJavaClass(repo.loadClass("gnu.gcj.runtime.FinalizerThread"));
+ bcp.visitJavaClass(repo.loadClass("gnu.gcj.runtime.FirstThread"));
+
+ // SecurityManager hacks to avoid java.security?
+ // URL and all descendents? Probably impossible.
+ // ObjectInput/ObjectOutput? Serialization?
+
+ // often called from native subclasses....
+ bcp.loadAllMethods("org.ibex.Surface");
+ bcp.loadAllMethods("org.ibex.Picture");
+ bcp.loadAllMethods("org.ibex.PixelBuffer");
+ bcp.loadAllMethods("org.ibex.Platform");
+ bcp.loadAllMethods("org.ibex.Scheduler");
+ bcp.loadAllMethods("org.ibex.plat.X11");
+ bcp.loadAllMethods("org.ibex.plat.X11$X11Picture");
+ bcp.loadAllMethods("org.ibex.plat.X11$X11PixelBuffer");
+ bcp.loadAllMethods("org.ibex.plat.X11$X11Surface");
+ bcp.loadAllMethods("org.ibex.XMLRPC");
+
+ bcp.loadAllMethods("java.util.Date");
+ bcp.loadAllMethods("java.text.DateFormat");
+ bcp.loadAllMethods("java.text.NumberFormat");
+
+
+ Method[] meths = repo.loadClass("org.ibex.plat.Linux").getMethods();
+ for(int i=0; i<meths.length; i++) {
+ if (meths[i].getName().equals("main"))
+ bcp.visitJavaMethod(repo.loadClass("org.ibex.plat.Linux"), meths[i]);
+ }
+ System.out.println();
+
+ System.out.println("Dumping...");
+
+ StringTokenizer st = new StringTokenizer(s[0], ":");
+ while(st.hasMoreTokens()) {
+ ZipFile zf = new ZipFile(st.nextToken());
+ Enumeration e = zf.entries();
+ while(e.hasMoreElements()) {
+ String ss = ((ZipEntry)e.nextElement()).getName();
+ if (!ss.endsWith(".class")) continue;
+ ss = ss.substring(0, ss.length() - 6);
+ ss = ss.replace('/', '.');
+ dump(repo.loadClass(ss));
+ }
+ }
+ }
+
+ public BytecodePruner() { }
+
+ public static void dump(JavaClass clazz) throws Exception {
+ if (clazz.getClassName().startsWith("java.sql.")) return;
+
+ ConstantPoolGen newcpg = new ConstantPoolGen(clazz.getConstantPool());
+ ClassGen cg = new ClassGen(clazz);
+ InstructionFactory factory = new InstructionFactory(cg, newcpg);
+ cg.setMajor(46);
+ cg.setMinor(0);
+ cg.setConstantPool(newcpg);
+ Field[] fields = clazz.getFields();
+ int numFields = 0;
+ for(int i=0; i<fields.length; i++)
+ if (!dest.contains(fields[i]) && false) {
+ System.out.println(" pruning " + clazz.getClassName() + "." + fields[i].getName());
+ fields[i] = null;
+ } else numFields++;
+
+ // superprune: URLClassLoader, convert.In/Output other than needed, unneeded locales
+ // reflective metadata is killing us...
+
+ Method[] methods = clazz.getMethods();
+ int numMethods = 0;
+ boolean good = false;
+ for(int i=0; i<methods.length; i++)
+ if (clazz.getClassName().startsWith("gnu.")
+ || clazz.getClassName().startsWith("java.lang.")
+ || clazz.getClassName().startsWith("java.io.")
+ || clazz.getClassName().startsWith("org.ibex.")) {
+ good = true;
+ } else if (dest.contains(methods[i])) {
+ if (!methods[i].getName().equals("<clinit>")) good = true;
+ } else {
+ if (methods[i].getCode() != null) {
+ System.out.println(" pruning " + clazz.getClassName() + "." + methods[i].getName());
+ MethodGen mg = new MethodGen(methods[i], clazz.getClassName(), newcpg);
+ mg.removeExceptions();
+ InstructionList il = new InstructionList();
+ mg.setInstructionList(il);
+
+ InstructionHandle ih_0 = il.append(factory.createNew("java.lang.UnsatisfiedLinkError"));
+ il.append(InstructionConstants.DUP);
+ il.append(factory.createInvoke("java.lang.UnsatisfiedLinkError",
+ "<init>", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL));
+ il.append(InstructionConstants.ATHROW);
+
+ mg.setMaxStack();
+ mg.setMaxLocals();
+ mg.removeExceptions();
+ mg.removeLocalVariables();
+ mg.removeExceptionHandlers();
+ mg.removeLineNumbers();
+
+ cg.replaceMethod(methods[i], mg.getMethod());
+ il.dispose();
+ }
+ }
+ if ((clazz.getClassName().startsWith("gnu.java.locale.LocaleInformation") &&
+ !clazz.getClassName().endsWith("LocaleInformation_en") &&
+ !clazz.getClassName().endsWith("LocaleInformation") &&
+ !clazz.getClassName().endsWith("LocaleInformation_en_US"))
+ ||
+ (!good &&
+ !clazz.isInterface() &&
+ !clazz.isAbstract() &&
+ !clazz.getClassName().endsWith("Error") &&
+ !clazz.getClassName().endsWith("Exception") &&
+ !clazz.getClassName().endsWith("Permission"))) {
+
+ System.out.println("DROPPING " + clazz.getClassName());
+ return;
+ }
+ new File(outdir + "/" + new File(clazz.getClassName().replace('.', '/')).getParent()).mkdirs();
+ System.out.println("dumping " + clazz.getClassName());
+ cg.getJavaClass().dump(outdir + "/" + clazz.getClassName().replace('.', '/') + ".class");
+ }
+
+ public JavaClass sig2class(String sig) throws Exception {
+ if (sig == null) return null;
+ while (sig.length() > 0 && (sig.charAt(0) == 'L' || sig.charAt(0) == '[')) {
+ if (sig.charAt(0) == 'L') sig = sig.substring(1, sig.length() - 1);
+ else if (sig.charAt(0) == '[') sig = sig.substring(1, sig.length());
+ }
+ if (sig.length() <= 1) return null;
+ if (sig.equals("<null object>")) return null;
+ if (sig.startsWith("<return address")) return null;
+ return repo.loadClass(sig);
+ }
+ public void load(String sig) throws Exception {
+ if (sig == null) return;
+ while (sig.length() > 0 && (sig.charAt(0) == 'L' || sig.charAt(0) == '[')) {
+ if (sig.charAt(0) == 'L') sig = sig.substring(1, sig.length() - 1);
+ else if (sig.charAt(0) == '[') sig = sig.substring(1, sig.length());
+ }
+ if (sig.length() <= 1) return;
+ if (sig.equals("<null object>")) return;
+ if (sig.startsWith("<return address")) return;
+ visitJavaClass(repo.loadClass(sig));
+ }
+ public void load(Type t) throws Exception {
+ if (t == null) return;
+ //String sig = t.getSignature();
+ if (t instanceof ArrayType) load(((ArrayType)t).getElementType());
+ if (!(t instanceof ObjectType)) return;
+ load(((ObjectType)t).getClassName());
+ }
+
+ // hashtable of hashsets
+ public static Hashtable subclasses = new Hashtable();
+
+ public String getMethodSignature(Method m) throws Exception {
+ ConstantPoolGen cpg = new ConstantPoolGen(m.getConstantPool());
+ return m.getName() + m.getSignature();
+ }
+
+ public String getMethodSignature(InvokeInstruction ii, ConstantPoolGen cpg) throws Exception {
+ String sig = "";
+ Type[] argtypes = ii.getArgumentTypes(cpg);
+ for(int j=0; j<argtypes.length; j++) sig += argtypes[j].getSignature();
+ return ii.getMethodName(cpg) + "(" + sig + ")" + ii.getReturnType(cpg).getSignature();
+ }
+ public static int level = 0;
+ public void visitJavaMethod(JavaClass jc, Method method) throws Exception {
+ visitJavaClass(jc);
+ if (dest.contains(method)) return;
+ dest.add(method);
+ level += 2;
+ for(int i=0; i<level; i++) System.out.print(" ");
+ System.out.println(jc.getClassName() + "." + getMethodSignature(method));
+ markMethodInSubclasses(jc, method);
+ ConstantPoolGen cpg = new ConstantPoolGen(method.getConstantPool());
+ if (method.getCode() == null) { level -= 2; return; }
+ byte[] code = method.getCode().getCode();
+ InstructionList il = new InstructionList(code);
+ Instruction[] instructions = il.getInstructions();
+ for(int i=0; i<instructions.length; i++){
+ Instruction instr = instructions[i];
+ if (instr instanceof LoadClass) load(((LoadClass)instr).getLoadClassType(cpg));
+ if (instr instanceof CPInstruction) load(((CPInstruction)instr).getType(cpg));
+ if (instr instanceof InvokeInstruction) {
+ InvokeInstruction ii = (InvokeInstruction)instr;
+ String ii_sig = getMethodSignature(ii, cpg);
+ JavaClass c = sig2class(ii.getLoadClassType(cpg).getSignature());
+ load(ii.getReturnType(cpg));
+ load(ii.getType(cpg));
+ Method[] meths = c.getMethods();
+ boolean good = false;
+ for(int i2=0; i2<meths.length; i2++) {
+ if (getMethodSignature(meths[i2]).equals(ii_sig)) {
+ visitJavaMethod(c, meths[i2]);
+ good = true;
+ break;
+ }
+ }
+ if (!good)
+ throw new Exception("couldn't find method " + getMethodSignature(ii, cpg) + " in " + c.getClassName());
+ }
+ }
+ level -= 2;
+ load(method.getReturnType());
+ Type[] argtypes = method.getArgumentTypes();
+ for(int i=0; i<argtypes.length; i++) load(argtypes[i]);
+ if (method.getExceptionTable() != null) {
+ String[] exntypes = method.getExceptionTable().getExceptionNames();
+ for(int i=0; i<exntypes.length; i++) load(exntypes[i]);
+ }
+ }
+
+ public void visitJavaField(Field field) throws Exception {
+ if (dest.contains(field)) return;
+ dest.add(field);
+ load(field.getType());
+ }
+
+ public void visitJavaClass(JavaClass clazz) throws Exception {
+
+ if (dest.contains(clazz)) return;
+ dest.add(clazz);
+
+ String name = clazz.getClassName();
+
+ JavaClass superclass = clazz.getSuperClass();
+
+ JavaClass[] interfaces = clazz.getAllInterfaces();
+ Field[] fields = clazz.getFields();
+ Method[] methods = clazz.getMethods();
+ System.out.println(clazz.getClassName() + ".class");
+ for(int i=0; i<methods.length; i++)
+ if (methods[i].getName().equals("<clinit>")) visitJavaMethod(clazz, methods[i]);
+ for(int i=0; i<methods.length; i++) {
+ if (methods[i].getName().equals("equals")) visitJavaMethod(clazz, methods[i]);
+ if (methods[i].getName().equals("hashCode")) visitJavaMethod(clazz, methods[i]);
+ if (methods[i].getName().equals("finalize")) visitJavaMethod(clazz, methods[i]);
+ if (methods[i].getName().equals("clone")) visitJavaMethod(clazz, methods[i]);
+ if (methods[i].getName().equals("toString")) visitJavaMethod(clazz, methods[i]);
+ }
+ for(int i=0; i<fields.length; i++) visitJavaField(fields[i]);
+ if (superclass != null) {
+ for(JavaClass sup = superclass; sup != null; sup = sup.getSuperClass()) {
+ if (subclasses.get(sup) == null) subclasses.put(sup, new HashSet());
+ ((HashSet)subclasses.get(sup)).add(clazz);
+ visitJavaClass(sup);
+ remarkMethods(sup, clazz);
+ }
+ }
+ for(int i=0; i<interfaces.length; i++) {
+ if (subclasses.get(interfaces[i]) == null) subclasses.put(interfaces[i], new HashSet());
+ ((HashSet)subclasses.get(interfaces[i])).add(clazz);
+ visitJavaClass(interfaces[i]);
+ remarkMethods(interfaces[i], clazz);
+ }
+ }
+
+ public void markMethodInSubclasses(JavaClass c, Method m, JavaClass subclass) throws Exception {
+ if (m.isStatic()) return;
+ String sig = getMethodSignature(m);
+ Method[] submethods = subclass.getMethods();
+ for(int j=0; j<submethods.length; j++)
+ if (getMethodSignature(submethods[j]).equals(sig))
+ visitJavaMethod(subclass, submethods[j]);
+ }
+ public void markMethodInSubclasses(JavaClass c, Method m) throws Exception {
+ if (m.isStatic()) return;
+ HashSet s = (HashSet)subclasses.get(c);
+ if (s == null) return;
+ Object[] subclasses = s.toArray();
+ for(int i=0; i<subclasses.length; i++) {
+ JavaClass subclass = (JavaClass)subclasses[i];
+ if (subclass == c) return;
+ markMethodInSubclasses(c, m, subclass);
+ }
+ }
+
+ public void remarkMethods(JavaClass c) throws Exception {
+ Method[] meths = c.getMethods();
+ for(int j=0; j<meths.length; j++) if (dest.contains(meths[j])) markMethodInSubclasses(c, meths[j]);
+ }
+
+ public void remarkMethods(JavaClass c, JavaClass target) throws Exception {
+ Method[] meths = c.getMethods();
+ for(int j=0; j<meths.length; j++) if (dest.contains(meths[j])) markMethodInSubclasses(c, meths[j], target);
+ }
+
+}