JVM 类文件格式

最后更新: 2024-11-18 19:52:43 +0800 +0800
原文以英文撰写。您正在阅读AI翻译版本。

类文件是由 JVM 使用的大端序二进制结构。文件以 0xCAFEBABE 开头,后跟 u2 类型的次版本号和主版本号。JVM 仅接受其实现所定义的版本;类文件解析器必须拒绝不受支持的版本,而不能假定较新的文件向后兼容。

顶层结构 #

在魔数和版本之后,文件包含:

  1. constant_pool_count 和带索引的常量池条目;
  2. 类的 access_flags、this_class 和 super_class;
  3. 接口索引;
  4. field_info 结构;
  5. method_info 结构;以及
  6. 类级 attribute_info 结构。

计数均为无符号 u2 值,只有属性的 attribute_length 为 u4。访问标志用于描述上下文:ACC_PUBLIC、ACC_FINAL、ACC_SUPER、ACC_INTERFACE、ACC_ABSTRACT、ACC_SYNTHETIC、ACC_ANNOTATION、ACC_ENUM 和 ACC_MODULE 并非对每种类文件类型都有意义或都合法。

常量池与名称 #

常量池索引从一开始;除非特定结构将零保留作特殊用途,否则索引零无效。CONSTANT_Long 和 CONSTANT_Double 各占用两个连续索引,其中第二个索引不可使用。条目包括基本类型常量和字符串常量;Class;String;字段、方法和接口方法引用;名称与类型对;方法句柄和方法类型;动态及调用动态调用点;模块;以及包。引用采用符号形式:例如,CONSTANT_Methodref 通过类条目及名称与类型条目进行指向,而不是指向内存中的方法。

类文件名称使用斜杠分隔的内部名称,例如 java/lang/String;面向源代码的 API 中使用点号分隔的二进制名称。字段和方法描述符用于编码类型,例如 Ljava/lang/String;、[I 和 (I[Ljava/lang/String;)V。CONSTANT_Utf8 使用类文件的 modified UTF-8 编码;该编码将空字符编码为 C0 80,并分别表示 UTF-16 代理代码单元。它不能与通用 UTF-8 解码器互换使用。

成员与属性 #

每个 field_info 和 method_info 都包含访问标志、名称与描述符索引,以及属性表。方法的 Code 属性存储 max_stack、max_locals、字节码、异常处理器和嵌套属性,例如行号或栈映射帧。字段可携带 ConstantValue;类和成员通过属性携带元数据,包括注解、签名、嵌套信息、记录、模块和调试数据。只能根据未知属性声明的长度跳过该属性;不能根据其名称猜测语义。

安全解析 #

即使类文件来自构建过程,也应将其视为不可信输入。每次读取前,都应验证剩余字节数;验证每个计数、索引、标签、标志组合、描述符和属性长度;对偏移量与长度的加法使用经过检查的算术运算;并拒绝尾部的结构不一致。在解析符号引用前,应强制执行格式的版本规则和上下文规则。验证器会增加执行安全检查,但不能代替有边界约束的二进制解析器。

javap -v 等工具可以显示解码后的视图,但执行转换时应使用受维护的库;该库必须支持目标类文件版本,并在需要时保留未知属性。

参考资料 #

评论

在 GitHub 上阅读或评论(发表评论需要登录)