[libbluray-devel] [Git][videolan/libbluray][master] 4 commits: BD-J: Add (debug) option to disable JIT

Petri Hintukainen gitlab at videolan.org
Fri Sep 18 17:07:38 CEST 2020



Petri Hintukainen pushed to branch master at VideoLAN / libbluray


Commits:
20e961c9 by hpi1 at 2020-09-18T18:01:37+03:00
BD-J: Add (debug) option to disable JIT

- - - - -
25bc1781 by hpi1 at 2020-09-18T18:01:37+03:00
BDJClassLoader: support generic class patching

- - - - -
824d87f4 by hpi1 at 2020-09-18T18:01:37+03:00
BDJLoader: Implement overriding method calls in Xlets

- - - - -
1ce479c1 by hpi1 at 2020-09-18T18:01:37+03:00
Fix long delay in "Evangelion, You are (not) alone" menu

Override java.lang.System.getCurrentTimeMillis() with synchronized version in Xlet.

Xlet uses unsynchronized flag to signal loading complete.
Flag is read in busy loop, checking for timeout.
Fix by triggering memory synchronization while Xlet is checking for timeout.

- - - - -


7 changed files:

- src/libbluray/bdj/bdj.c
- + src/libbluray/bdj/java/org/videolan/BDJClassFilePatcher.java
- src/libbluray/bdj/java/org/videolan/BDJClassLoader.java
- src/libbluray/bdj/java/org/videolan/BDJSecurityManager.java
- src/libbluray/bdj/java/org/videolan/BDJXletContext.java
- + src/libbluray/bdj/java/org/videolan/backdoor/System.java
- + src/libbluray/bdj/java/org/videolan/patchers/ReplaceMethodPatcher.java


Changes:

=====================================
src/libbluray/bdj/bdj.c
=====================================
@@ -838,6 +838,7 @@ static const char * const java_base_exports[] = {
         "com.aacsla.bluray.online",
         "com.aacsla.bluray.mc",
         "com.aacsla.bluray.mt",
+        "org.videolan.backdoor", /* entry for injected Xlet / runtime fixes */
 };
 static const size_t num_java_base_exports = sizeof(java_base_exports) / sizeof(java_base_exports[0]);
 
@@ -914,7 +915,12 @@ static int _create_jvm(void *jvm_lib, const char *java_home, const char *jar_fil
 
     /* JVM debug options */
 
+    if (getenv("BDJ_JVM_DISABLE_JIT")) {
+        BD_DEBUG(DBG_CRIT | DBG_BDJ, "Disabling BD-J JIT\n");
+        option[n++].optionString = str_dup("-Xint");
+    }
     if (getenv("BDJ_JVM_DEBUG")) {
+        BD_DEBUG(DBG_CRIT | DBG_BDJ, "Enabling BD-J debug mode\n");
         option[n++].optionString = str_dup("-ea");
         //option[n++].optionString = str_dup("-verbose");
         //option[n++].optionString = str_dup("-verbose:class,gc,jni");


=====================================
src/libbluray/bdj/java/org/videolan/BDJClassFilePatcher.java
=====================================
@@ -0,0 +1,28 @@
+/*
+ * This file is part of libbluray
+ * Copyright (C) 2020  Petri Hintukainen <phintuka at users.sourceforge.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+package org.videolan;
+
+/**
+ * Patch Xlet classes at runtime (modify bytecode)
+ */
+
+public interface BDJClassFilePatcher {
+    public abstract byte[] patch(byte[] b) throws ClassFormatError;
+}


=====================================
src/libbluray/bdj/java/org/videolan/BDJClassLoader.java
=====================================
@@ -38,8 +38,9 @@ import javax.tv.xlet.Xlet;
 
 import org.videolan.bdjo.AppCache;
 
-public class BDJClassLoader extends URLClassLoader {
-    public static BDJClassLoader newInstance(AppCache[] appCaches, String basePath, String classPathExt, final String xletClass) {
+class BDJClassLoader extends URLClassLoader {
+    public static BDJClassLoader newInstance(AppCache[] appCaches, String basePath, String classPathExt, final String xletClass,
+                                             final BDJClassFilePatcher patcher) {
         ArrayList classPath = new ArrayList();
         URL url = translateClassPath(appCaches, basePath, null);
         if (url != null)
@@ -55,7 +56,7 @@ public class BDJClassLoader extends URLClassLoader {
         return (BDJClassLoader)AccessController.doPrivileged(
                 new PrivilegedAction() {
                     public Object run() {
-                        return new BDJClassLoader(urls, xletClass);
+                        return new BDJClassLoader(urls, xletClass, patcher);
                     }
                 });
     }
@@ -115,7 +116,7 @@ public class BDJClassLoader extends URLClassLoader {
         }
     }
 
-    private BDJClassLoader(URL[] urls, String xletClass) {
+    private BDJClassLoader(URL[] urls, String xletClass, BDJClassFilePatcher patcher) {
         super(urls);
         this.xletClass = xletClass;
 
@@ -125,6 +126,7 @@ public class BDJClassLoader extends URLClassLoader {
             bootClasses = a.getBootClasses();
             xletClasses = a.getXletClasses();
         }
+        this.patcher = patcher;
     }
 
     protected Xlet loadXlet() throws ClassNotFoundException,
@@ -250,6 +252,19 @@ public class BDJClassLoader extends URLClassLoader {
     }
 
     protected Class findClass(String name) throws ClassNotFoundException {
+
+        if (patcher != null) {
+            try {
+                byte[] b = loadClassCode(name);
+                b = patcher.patch(b);
+                return defineClass(b, 0, b.length);
+            } catch (ThreadDeath td) {
+                throw td;
+            } catch (Throwable t) {
+                logger.error("Class patching failed: " + t);
+            }
+        }
+
         try {
             return super.findClass(name);
 
@@ -319,8 +334,9 @@ public class BDJClassLoader extends URLClassLoader {
 
     private String xletClass;
 
+    private final BDJClassFilePatcher patcher;
     private Map hideClasses;  /* classes that should be hidden from Xlet */
-    private Map bootClasses;  /* additional bootstrap clases */
+    private Map bootClasses;  /* additional bootstrap classes */
     private Map xletClasses;  /* fallback for possibly missing classes */
 
     private static final Logger logger = Logger.getLogger(BDJClassLoader.class.getName());


=====================================
src/libbluray/bdj/java/org/videolan/BDJSecurityManager.java
=====================================
@@ -241,7 +241,7 @@ final class BDJSecurityManager extends SecurityManager {
      * Allow package access (Java 11)
      */
 
-    private static String pkgPrefixes[] = {
+    private static final String pkgPrefixes[] = {
         "javax.media" ,
         "javax.tv",
         "javax.microedition",
@@ -252,12 +252,18 @@ final class BDJSecurityManager extends SecurityManager {
         "org.blurayx",
         "com.aacsla.bluray",
     };
+    private static final String pkgs[] = {
+        "org.videolan.backdoor",
+    };
 
     public void checkPackageAccess(String pkg) {
 
         for (int i = 0; i < pkgPrefixes.length; i++)
             if (pkg.startsWith(pkgPrefixes[i]))
                 return;
+        for (int i = 0; i < pkgs.length; i++)
+            if (pkg.equals(pkgs[i]))
+                return;
 
         super.checkPackageAccess(pkg);
     }


=====================================
src/libbluray/bdj/java/org/videolan/BDJXletContext.java
=====================================
@@ -40,13 +40,27 @@ import org.videolan.bdjo.AppEntry;
 
 public class BDJXletContext implements javax.tv.xlet.XletContext, javax.microedition.xlet.XletContext {
     public BDJXletContext(AppEntry entry, AppCache[] caches, Container container) {
+        BDJClassFilePatcher patcher = null;
+
+        /* Disc-specific fixes */
+        if (org.bluray.ti.DiscManager.getDiscManager().getCurrentDisc().getId().equals("00000000000000000000000000000000")) {
+            if (entry.getIdentifier().getOID() == 0xffff27dd && entry.getIdentifier().getAID() == 0x1212 && entry.getInitialClass().equals("PlayMenuMain")) {
+                // "Evangelion, You are (not) alone"
+                // Disc uses unsynchronized flag to signal loading complete. Flag is read in busy loop, checking for timeout.
+                // Fix by triggering memory synchronization while Xlet is checking for timeout.
+                logger.error("Detected broken Xlet, applying patch");
+                patcher = new org.videolan.patchers.ReplaceMethodPatcher("java/lang/System", "currentTimeMillis", "org/videolan/backdoor/System", "currentTimeMillisSynced", "()J");
+            }
+        }
+
         this.appid = entry.getIdentifier();
         this.args = entry.getParams();
         this.loader = BDJClassLoader.newInstance(
                 caches,
                 entry.getBasePath(),
                 entry.getClassPathExt(),
-                entry.getInitialClass());
+                entry.getInitialClass(),
+                patcher);
         this.container = container;
 
         this.threadGroup = BDJThreadGroup.create(Integer.toHexString(appid.getOID()) + "." +


=====================================
src/libbluray/bdj/java/org/videolan/backdoor/System.java
=====================================
@@ -0,0 +1,35 @@
+/*
+ * This file is part of libbluray
+ * Copyright (C) 2020  Petri Hintukainen <phintuka at users.sourceforge.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Public package, exported to Xlets.
+ */
+
+package org.videolan.backdoor;
+
+public class System {
+
+    private static final Object syncBarrier = new Object();
+
+    public static long currentTimeMillisSynced() {
+        synchronized (syncBarrier) {
+            return java.lang.System.currentTimeMillis();
+        }
+    }
+}


=====================================
src/libbluray/bdj/java/org/videolan/patchers/ReplaceMethodPatcher.java
=====================================
@@ -0,0 +1,115 @@
+/*
+ * This file is part of libbluray
+ * Copyright (C) 2020  Petri Hintukainen <phintuka at users.sourceforge.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+package org.videolan.patchers;
+
+/**
+ * Replace method calls in Xlet
+ */
+
+import java.util.Map;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Attribute;
+
+import org.videolan.BDJClassFilePatcher;
+import org.videolan.Logger;
+
+public class ReplaceMethodPatcher implements BDJClassFilePatcher
+{
+    /* replace static method calls */
+
+    final String origMethod, origClass, newMethod, newClass, signature;
+    final int callOpcode;
+
+    public ReplaceMethodPatcher(String origClass, String origMethod, String newClass, String newMethod, String signature) {
+
+        if (origMethod == null || origClass == null || newMethod == null || newClass == null || signature == null)
+            throw new NullPointerException();
+
+        this.origMethod = origMethod;
+        this.origClass = origClass;
+        this.newMethod = newMethod;
+        this.newClass = newClass;
+        this.signature = signature;
+        this.callOpcode = Opcodes.INVOKESTATIC;
+    }
+
+    /*
+     * Replace method call
+     */
+
+    public byte[] patch(byte[] b) throws ClassFormatError {
+        try {
+            ClassReader cr = new ClassReader(b);
+            ClassWriter cw = new ClassWriter(cr, 0/*ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS*/);
+            ClassVisitor cv = new PatchClassVisitor(cw);
+            cr.accept(cv, ClassReader.SKIP_DEBUG | ClassReader.EXPAND_FRAMES);
+            return cw.toByteArray();
+        } catch (Exception e) {
+            logger.error("Failed patching class: " + e);
+        }
+
+        return b;
+    }
+
+    private final class PatchClassVisitor extends ClassVisitor {
+
+        public PatchClassVisitor(ClassVisitor cv) {
+            super(Opcodes.ASM4, cv);
+        }
+
+        public MethodVisitor visitMethod(int access, String name, String desc,
+                                         String signature, String[] exceptions) {
+            MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
+            return new PatchMethodVisitor(mv);
+        }
+    }
+
+    private final class PatchMethodVisitor extends MethodVisitor {
+        public PatchMethodVisitor(MethodVisitor mv) {
+            super(Opcodes.ASM4, mv);
+        }
+
+        public void visitAttribute(Attribute attr) {
+            super.visitAttribute(attr);
+        }
+
+        public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
+                if (opcode == callOpcode &&
+                    owner.equals(origClass) &&
+                    name.equals(origMethod) &&
+                    desc.equals(signature)) {
+
+                    // replace funtion call
+                    super.visitMethodInsn(callOpcode, newClass, newMethod, signature, false);
+                }
+                else {
+                    // no change
+                    super.visitMethodInsn(opcode, owner, name, desc, itf);
+                }
+        }
+    }
+
+    private static final Logger logger = Logger.getLogger(ReplaceMethodPatcher.class.getName());
+}



View it on GitLab: https://code.videolan.org/videolan/libbluray/-/compare/509b554a8e719de85ba7f277cb407cdceb90c4c4...1ce479c1cfa1dfdb33a2f150e91edaf28af364e4

-- 
View it on GitLab: https://code.videolan.org/videolan/libbluray/-/compare/509b554a8e719de85ba7f277cb407cdceb90c4c4...1ce479c1cfa1dfdb33a2f150e91edaf28af364e4
You're receiving this email because of your account on code.videolan.org.




More information about the libbluray-devel mailing list