4

我一直在从各种文件中读取 Java 字节码,以帮助我理解一个项目的 .class 文件,在该项目中我需要与没有源代码和可用文档不足的第 3 方库集成。

为了我自己的乐趣,我通过我的 maven 存储库运行了 Apache BCEL 库,以查看在哪里使用了较少见的类和方法属性(例如类型注释)以及原因。

我偶然发现了一个特定 jar 的问题,该 jar 无法解码常量字段之一 - 特别是 CONSTANT_Utf8_info。库是icu4j-2.6.1.jar (com.ibm.icu:icu4j),特别是LocaleElements_zh__PINYIN.class文件。Apache BCEL 失败(以及我自己在符合 JVMS 版本 8 和 9 的快速字节码阅读器上的尝试)偶然发现了同样的问题,他们误读了这个常量,然后读取了下一个字节,该字节被评估为不正确的常量标签 (0x3C/60) .

快速检查我是否可以在 IDE 中使用该类失败(无法解析符号)。使用十六进制编辑器研究实际字节码,显示该偏移量 ( 0x1AC) 处的常量是长度为 的 Utf8 常量 (tag= 0x01) 0x480E。向前移动文件中的该数量确实0x3C在该位置有一个字节。直观地查看文件,我可以看到有问题的常量在 location 结束,0x149BD这使得字符串的实际长度0x1480E(本质上是 location 的前三个字节0x1AC)。根据 JVM 类文件规范,这当然不可能,因为0xFFFFUtf8 常量的最大长度为 65535 或 65535。类文件相当旧 - 版本 46 或 Java 1.2。

我仔细研究了规范并尝试了不同的可能实现(越来越严格)来尝试解析这个常量,但它要么无法解析它,要么破坏了其他有效 Utf8 常量的读取。

那么我的问题是,我是否遗漏了什么,或者是编译器错误,在这种情况下,我的第二个问题是这首先是如何发生的 - 编译器往往经过相对彻底的检查。最后,Java 编译器通常如何管理长度超过 65535字节的字符串文字?

4

1 回答 1

4

既然您说“类文件很旧 - 版本 46 或 Java 1.2”,那么确实有可能由于当时的编译器在超出限制时没有拒绝代码,所以类文件被简单地破坏了。

请参阅JDK-4309152:# 编译器静默生成超出 VM 限制的字节码

编译器没有正确地对各种类文件组件的数量或大小强制实施某些限制。这导致代码看起来编译成功,但在验证期间运行时失败。

这些最初是作为单独的错误报告的,现在已作为此错误的副本关闭。原始错误编号包含在下面的每个项目中。

…</p>

  1. UTF-8 编码的字符串有 64k 的限制。(4071592)

据报道,此错误已修复1.3.1_10,因此它适合时间范围。

请注意,引用的错误 #4071592 是指UTFDataFormatException尝试在1.2.0和更早的版本中写入过大的字符串时抛出 a,但#4303354报告在1.3.0. 因此,如果有问题的类文件是由 生成的javac,那么它一定是在 version1.3.01.3.1_10with之间-target 1.2

自修复以来,如果某个构造超出类文件/JVM 限制,编译器的标准行为是生成编译器错误。

于 2018-02-09T13:44:14.857 回答