问题 关于摩托罗拉FX9500的JamVM问题 - 我该怎么办?


我正在使用 摩托罗拉FX9500 RFID阅读器,用Linux运行Linux jamvm 它上面的版本1.5.0(我只能部署应用程序 - 我无法更改Java VM或其他任何内容,因此我的选项有限) - 这是我在查看版本时看到的内容:

[cliuser@FX9500D96335 ~]$ /usr/bin/jamvm -version
java version "1.5.0"
JamVM version 1.5.4
Copyright (C) 2003-2010 Robert Lougher <rob@jamvm.org.uk>

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2,
or (at your option) any later version.

This program 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 General Public License for more details.

Build information:

Execution Engine: inline-threaded interpreter with stack-caching
Compiled with: gcc 4.2.2

Boot Library Path: /usr/lib/classpath
Boot Class Path: /usr/local/jamvm/share/jamvm/classes.zip:/usr/share/classpath/glibj.zip

我需要编写一个应用程序,所以我抓住了Oracle Java SDK 1.5.0并将其安装到我的Windows 7 PC上,所以它有这个版本:

C:\>javac -version
javac 1.5.0

考虑到我使用该编译器编译的应用程序可以在上述JamVM上正常工作,我是否过于理想化?无论如何,在无知中按下我写这个小应用程序:

public final class TestApp {
    public static void main(final String[] args) {
        long p = Long.MIN_VALUE;
        int o = (int)(-(p + 10) % 10);
        System.out.println(o);
    }
}

用前面提到的javac编译器编译它并在PC上运行它,如下所示:

C:\>javac TestApp.java

C:\>java TestApp
8

一切都很好。生活是美好的,所以我接受了 .class 文件并将其放在 FX9500 并运行它:

[cliuser@FX9500D96335 ~]$ /usr/bin/jamvm TestApp
-2

Eek,正如你所看到的那样 - 它会返回不同的结果。

那么,为什么以及谁错了,或者像规范这样的东西不清楚如何处理这个计算(肯定不是)?难道我需要用不同的编译器编译它吗?


我为什么关心这个?

我遇到这种情况的原因是内部发生的计算完全一样 java.lang.Long.toString 我在我的实际应用程序中有一个错误,我注销了很长时间并得到了一个 java.lang.ArrayIndexOutOfBoundsException。因为我想要记录的值很可能在a的末尾 Long

我想我可以通过检查Long.MIN_VALUE和Long.MAX_VALUE来解决它并记录“Err,我不能告诉你这个号码,但它真的是Long.XXX,相信我,我会欺骗你吗?”。但是当我发现这个时,我觉得我的应用程序现在建立在一个沙质基础上,它需要非常强大。我正在认真地考虑说JamVM不能胜任工作并用Python编写应用程序(因为读者也有Python运行时)。

我有点希望有人告诉我,我是一个笨蛋,我应该在我的Windows PC上编译它......然后它会工作,所以请告诉我(如果它是真的,当然) !


更新

Noofiz让我思考(谢谢),我敲了这个额外的测试应用程序:

public final class TestApp2 {
    public static void main(final String[] args) {

        long p = Long.MIN_VALUE + 10;

        if (p != -9223372036854775798L) {
            System.out.println("O....M.....G");
            return;
        }

        p = -p;

        if (p != 9223372036854775798L) {
            System.out.println("W....T.....F");
            return;            
        }

        int o = (int)(p % 10);

        if (o != 8) {
            System.out.println("EEEEEK");
            return;
        }

        System.out.println("Phew, that was a close one");
    }
}

我再次在Windows机器上编译并运行它。

它打印 Phew, that was a close one 

我复制了 .class 提交有问题的装置并运行它。

它打印...

......等待......

W....T.....F

噢亲爱的。我觉得有点恍惚,我想我需要一杯茶......

更新2

我试过的另一件事,没有任何区别,就是将FX9500上的classes.zip和glibj.zip文件复制到PC上,然后像这样进行交叉编译(这必须意味着编译后的文件应该没问题)对?):

javac -source 1.4 -target 1.4 -bootclasspath classes.zip;glibj.zip -extdirs "" TestApp2.java

但是,在读取器上运行时生成的.class文件会打印相同的消息。


7905
2018-04-05 08:07


起源

我认为,纱线旋转的纱线为+1(即问题井并且写得很有趣)。是不是有一个用于Windows的jamvm编译器你可以尝试?不确定常规的SDK编译器是做什么的...... - Simon Hellinger
谢谢。我找不到jamvm编译器 - 我认为它只是一个虚拟机。 - kmp
您是否尝试使用自定义模数运算。我的意思是 - (a / b)* b而不是%b。可能在实施方面存在一些差异。添加和否操作将直接导致问题。 - Mikhail


答案:


我写了JamVM。正如您可能猜到的那样,此类错误现在已经被注意到,而JamVM甚至无法通过最简单的测试套件(GNU Classpath有自己的Mauve,而OpenJDK有jtreg)。我经常运行ARM(FX9500使用PXA270 ARM)和x86-64,但各种平台都作为IcedTea的一部分进行测试。

所以我对这里发生的事情并不了解。我猜它只会影响Java longs,因为这些很少使用,因此大多数程序都可以工作。 JamVM将Java longs映射到C long longs,所以我的猜测是用于构建JamVM的编译器会产生错误的代码,以便在32位ARM上进行长时间处理。

不幸的是,如果你不能替换JVM,你可以做的事情不多(除了避免长时间)。你唯一能做的就是尝试关闭JIT(一个简单的代码复制JIT,也就是内联线程)。为此,在命令行中使用-Xnoinlining,例如:

jamvm -Xnoinlining ...


5
2018-04-06 01:14



您的一条(kmp)评论暗示JamVM是摩托罗拉随FX9500提供的软件的一部分。这是真的?如果是,则无法保证JamVM未被修改和/或黑客入侵。您可以尝试向摩托罗拉寻求源代码,因为我在GPL下发布了JamVM,因此必须提供该代码。 - user2251099
非常感谢 - 我对此感到非常惊讶 - 我认为在jamvm中这么简单的错误是非常不可能的 - 关于它是如何构建的想法似乎很可能 - 我希望我可以自己替换它但它完全被锁定所以我向摩托罗拉求助 - kmp
只是为了更新你 - 传递-Xnoinlining没有任何区别。 - kmp


问题在于不同的模数实现:

public static long mod(long a, long b){
    long result = a % b;
    if (result < 0)
    {
        result += b;
    }
    return result;
}

此代码返回 -2,而这个:

public static long mod2(long a, long b){
    long result = a % b;
    if (result > 0 && a < 0)
    {
        result -= b;
    }
    return result;
}

回报 8。 JamVM这样做的原因是我的理解背后的原因。

来自JLS:

15.17.3。剩余运营商%

二进制后整数操作数的余数运算   数字促销(第5.5.2节)产生一个结果值   (a / b)* b +(a%b)等于a。

根据这个JamVM中断语言规范。很坏。


4
2018-04-05 09:13



好吧,jamvm源代码可用,总是可以看看。但是我不记得我有任何与jamvm有关的问题,但我自己编译并且仅用于x86目标(不知道摩托罗拉的装置是什么运行)。 - Archie
我不认为它是模数运算符 - 请参阅我对问题的更新 - kmp
这是一个真正的WTF!如果JVM对基本类型的规范有这样的有线解释,我不明白你将如何将它用于中等规模的项目。 - Mikhail
根据JamVM网页,它符合JVM规范版本2(蓝皮书)。也许联系JamVM开发人员来讨论这个问题? - mthmulders
是的,我现在真的,真的,担心这个项目 - 谁知道我会遇到什么样的疯狂。我真的认为我能做的唯一合理的事情就是完全删除Java,并希望它上面的Python运行时也没有像这样的基本缺陷。我想摩托罗拉不太可能通过更好的JVM发布它的更新(作为设备的用户,我没有自己安装不同JVM的权限,所以即使它已经在新的JamVM中修复了 - 它不会帮助我)。 - kmp


我会评论,但由于某种原因,这需要声誉。

长时间否定不适用于此设备。我不明白它的确切性质,但如果你做了两个一元的弊端,你会回到你开始的地方,例如: X = 10; -x == 4294967286; -x == 10。 4294967286非常接近Integer.MAX_VALUE * 2(2147483647 * 2 = 4294967294)。它甚至更接近Integer.MAX_VALUE * 2-10!

它似乎与这一个操作是隔离的,并且不会以更进一步的方式影响多头。在你自己的代码中避免操作是很简单的,并且一些灵巧的滥用bootclasspath可以避免在GNU Classpath代码中调用,用* -1s替换它们。如果需要从设备GUI启动应用程序,可以将-Xbootclasspath = ...切换到args参数中,以便将其传递给JamVM)。

该bug实际上已在后者(比最新版本)JamVM代码中修复: * https://github.com/ansoncat/jamvm/commit/736c2cb76baf1fedddc1eda5825908f5a0511373 * https://github.com/ansoncat/jamvm/commit/ac83bdc886ac4f6e60d684de1b4d0a5e90f1c489

虽然没有帮助我们在设备上的固定版本。 Rob Lougher已经提到这个问题是发布新版JamVM的一个原因,虽然我不知道这是什么时候,或者摩托罗拉是否足以说服他们更新他们的固件。

FX9500实际上是一个重新打包的Sirit IN610,这意味着两个设备都有这个错误。 Sirit对摩托罗拉更友好,并且正在提供固件升级,将在不久的将来推出。我希望摩托罗拉也能提供修复,但我不知道双方安排的细节。

无论哪种方式,我们都在FX9500上运行了一个非常大的应用程序,并且长期否定操作并未被证明是不可逾越的障碍。

祝你好运,丹。


2
2017-09-04 14:33



谢谢Dan - 尤其是关于-Xbootclasspath的提示。实际上,我们遇到问题的唯一地方是我们在做String.format(“%d”,x)而x是任何负数(-1会这样做) - 然后就会崩溃。我刚刚编写了自己的String.format方法,该方法没有在内部将整数转换为long并使用它(我们只在编写日志消息时使用它 - 幸运的是应用程序非常简单)。如果我再次使用相同的设备做任何非常重要的事情,我想我会使用python解释器代替。 - kmp