Jimmy Chen

A Programmer

从openjdk源码分析class文件格式

  在拜读邓凡平老师的《深入理解Android Java虚拟机ART》后,试着结合openjdk 11的代码对class文件的格式进行分析。因为详细的介绍在《深入理解》中已经有说明,所以结合代码分析的时候可能个别部分不会进行很详细的说明

  准备工作,此处我们进行分析的代码可以从github上下载:https://github.com/openjdk/jdk,对应tag为jdk-11+28。准备工作完毕,下面直接开始进行分析

1. 总体流程

首先将class文件的格式贴出来,如下图所示,看class文件的总体格式相对比较简单,只要按照这个ClassFile格式将内容写入即可

《从openjdk源码分析class文件格式》

下面我们查看write_class_file_format方法的代码,可以看到代码原有的注释中已经有比较详细的说明了,其中部分方法我们会在后面继续进行说明

写入class文件的总体流程如上,对比着java虚拟机规范第四章“class文件格式”看还是比较简单的。上面给的代码只是流程,细节的地方我们接着看,首先我们看写入cp_info的流程。

2. 写入常量池流程

在开始copy_cpool_bytes分析前需要首先说明的是writeable_address这个方法,这个方法主要是进行内存判断的,因为在写入class文件的前是会先分配一部分内存,这里判断之前分配的内存是否足够使用,如果不足以存在常量池的内容的话就需要对之前分配的内存进行扩展,扩展为之前的两倍以上,并且会进行边界对齐。OK,我们进入主题,分析copy_cpool_bytes

这个方法的后面部分都是按照tag结构的不同分别写入常量池,因为代码太长,所以省略了部分代码。这里我们可以看个简单例子,首先我们看JVM_CONSTANT_Fieldref、JVM_CONSTANT_Methodref、JVM_CONSTANT_InterfaceMethodref的类型定义如下,首先是写入tag,这个代码部分在switch前就已经写入了,其次是两个字节的class_index以及两个字节的name_and_type_index,上述代码中则是通过两次调用Bytes::put_Java_u2将index写入到 bytes+1 以及 bytes+3

《从openjdk源码分析class文件格式》

写入常量池的过程就到这里,后面我们接着看成员变量的写入

3. 写入类包含的成员变量的数量和信息

写入成员变量会调用 write_field_infos 方法进行, filed_info在java虚拟机规范中的定义如下所示,因此我们照着虚拟机中的规范进行查看

《从openjdk源码分析class文件格式》

这里最后需要处理类成员变量相关的attribute信息,这里我们用write_signature_attribute来进行展示说明,java虚拟机规范中 Signature_attribute 定义如下所示:

《从openjdk源码分析class文件格式》

对照这java虚拟机规范中的定义,再查看代码的实现方式就比较容易理解了。其余的attribute也可以按照这种方式进行分析,其余attribute这里就不多做赘述。

4. 写入类的成员函数的数量和信息

写入成员函数是在 write_method_infos 方法中进行的,我们首先来分析这个方法

这里最后会轮循进行method的写入,调用的对应方法是 write_method_info,这个方法就是实际的按照java虚拟机规范中method_info的结构进行写入的地方了,我们首先将 method_info 的结构贴出来,然后就可以对着该结构进行代码分析:

《从openjdk源码分析class文件格式》

对于 method_info 中,这里我们比较关注的属性就是code,里面有个字段就是存储的就是该方法编译后的字节码。除了code属性外,下面还有很多其他的属性需要记录和写入,例如exception、anotation和signature,这些属性都比较简单,各位同学可以按照需要跟踪学习。code属性在 write_code_attribute 中进行展现。老规矩了,我们先将 Code_attribute 结构贴出来,如下所示:

《从openjdk源码分析class文件格式》

首先我们将上面Code_attribute中会用到的一些属性的定义贴出来:

《从openjdk源码分析class文件格式》

《从openjdk源码分析class文件格式》

《从openjdk源码分析class文件格式》

《从openjdk源码分析class文件格式》

这里只是简单展示其对应的流程,具体的代码细节,各位同学可自行查看分析。最后我们接下来分析

5. 写入类包含的属性信息

剩下最后一部分了,write_class_attributes 用来写入类包含的对应的属性,前两部分也有提到一些属性,之前提到的属性都只是对应字段或者方法中涉及到的属性。这里的属性则是类相关的属性

上面方法的代码阅读起来不是特别的困难,这里就偷懒不做额外的解释了,有兴趣的同学可以自行查看。另外,这里的代码分析没有设计到Java指令码的介绍,因为Java指令码会在编译的时候生成,这里的流程只是简单的将编译好的Java指令码写入文件。下一篇,我们希望写一个class解析软件,对class文件进行解析。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注