JVM系列之class文件剖析
英属哥伦比亚大学(加拿大)校训:“这是你的思想之地。”
帝都几天的阴雨连绵,心中的热情似乎也被浇灭了,总是感觉有气无力,真的挺渴望阳光的味道。再无力,该做的事还得干,世间的事不可能每件都喜欢,把每件不讨厌的市干习惯了就是会产生依赖感,进而喜欢,这就跟国人夫妻结婚生子过日子一个道理。
本篇是JVM系列开篇,本想搞个深度的剖析,但类文件解析本是繁琐的事情,这几天加班加上心情烦躁,未能全面地梳理,但会尽本人之全力呈现一篇值得阅读的文章。
本篇从以下类文件结构/常量池结构/字段表结构/方法表结构等展示讲解,最后以一个demo进行说明,涉及的东西比较多,会拎出重点的进行讲解。
一 class文件结构
我们知道咱们正常搬砖的地方是.java文件,执行javac命令就会编译成.class字节码文件,这样jvm才能正常解析执行,今天要聊的正是字节码文件。一个class文件由虚拟机指令/符号表/其他辅助信息组成,其数据结构就是无符号数和表。
无符号数:指的是全部二进制位均表示数值,无符号位,第一个"0"或"1"来表示正负;无符号数在class文件中是基本数据类型,均是非负数,用来描述数字、索引引用、数量值、UTF-8编码构成字符串等,包含u1 u2 u4 u8四种,分别代表1个字节、2个字节、4个字节和8个字节。
表:指的是由多个无符号数或其他表作为数据项构成的复合数据类型,以_info结尾,主要用来表示常量池、字段表、方法表等。
附上class文件格式:
译名 | 数据类型 | 名称 | 数量 |
---|---|---|---|
魔数 | u4 | magic | 1 |
次版本号 | u2 | minor_version | 1 |
主版本号 | u2 | major_version | 1 |
常量池计数器 | u2 | constant_pool_count | 1 |
常量池 | cp_info | constant_pool | constant_pool_count-1 |
访问标志 | u2 | access_flags | 1 |
类索引 | u2 | this_class | 1 |
父类索引 | u2 | super_class | 1 |
接口计数器 | u2 | interfaces_class | 1 |
接口索引集合 | u2 | interfaces | interfaces_count |
字段计数器 | u2 | fields_count | 1 |
字段表集合 | field_info | fields | fields_count |
方法计数器 | u2 | methods_count | 1 |
方法表集合 | method_info | methods | methods_count |
属性计数器 | u2 | attributes_count | 1 |
属性表集合 | attribute_info | attributes | attributes_count |
二 常量池
常量池代表class文件中的仓库资源,主要存放两大类常量:字面量和符号引用。
字面量:表示固定值,可以当作常量,通俗理解为等号右边的内容;
符号引用:包含三类常量,分别是类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。
常量池有11种不同的表结构,每一项的第一位都是是一个u1类型的标志位(tag),代表常量类型,结构表单(因简书markdown不支持table标签,无法合并单元格,在此只能用图片替代)如下:
类型 | 标志 | 描述 |
---|---|---|
CONSTANT_Utf8_info | 1 | utf8编码字符串 |
CONSTANT_Integer_info | 3 | 整型字面量 |
CONSTANT_Float_info | 4 | 浮点型字面量 |
CONSTANT_Long_info | 5 | 长整型字面量 |
CONSTANT_Double_info | 6 | 双精度浮点型字面量 |
CONSTANT_Class_info | 7 | 类或接口的符号引用 |
CONSTANT_String_info | 8 | 字符串型字面量 |
CONSTANT_Fieldref_info | 9 | 字段的符号引用 |
CONSTANT_Methodref_info | 10 | 类中方法的符号引用 |
CONSTANT_InterfaceMethodref_info | 11 | 接口中方法的符号引用 |
CONSTANT_NameAndType_info | 12 | 字段或方法的部分符号引用 |
CONSTANT_MethodHandle_info | 15 | 标识方法句柄 |
CONSTANT_MethodType_info | 16 | 标识方法类型 |
CONSTANT_InvokeDynamic_info | 18 | 表示一个动态方法调用点 |
三 访问标记
紧接着常量池之后的两个字节(u2)代表访问标志(access_flags),用于识别一些类或接口层次的访问信息,表单如下:
名称 | 值 | 说明 |
---|---|---|
ACC_PUBLIC | 0X0001 | public类型 |
ACC_FINAL | 0X0010 | 声明final,只有类可设置 |
ACC_SUPER | 0X0020 | 使用invokespecial指令新语义,编译后类的这个标识为真 |
ACC_INTERFACE | 0X0200 | 接口 |
ACC_ABSTRACT | 0X0400 | 接口或抽象类此值为真,其他为假 |
ACC_SYNTHETIC | 0X1000 | 标识此类并非为用户代码生成 |
ACC_ANNOTATION | 0X2000 | 注解 |
ACC_ENUM | 0X4000 | 枚举 |
四 索引顺序
在访问标记之后,有三项来确定该类的继承关系,按顺序依次为:
类索引(this):描述类的全限定名
父类索引(super):描述父类的全限定名
接口索引集合(interfaces):描述类实现的接口集合。
五 字段表集合
在接口索引集合之后是字段计数器,用来标识有多少个字段,紧接着就是字段表集合,字段表用来描述接口或者类中声明的变量。它包括类级变量和实例级变量,但不包括局部变量以及从父类和接口中继承而来的字段。
字段表的格式如下:
名称 | 类型 | 数量 | 描述 |
---|---|---|---|
access_flags | u2 | 1 | 字段修饰符 |
name_index | u2 | 1 | 字段和方法名称在常量池中的引用 |
descriptor_index | u2 | 1 | 字段和方法描述符在常量池中的引用 |
attribute_count | u2 | 1 | 字段额外信息属性的个数 |
attributes | attribute_info | attribute_count | 字段的额外信息属性 |
字段修饰符如下:
名称 | 数值 | 含义 |
---|---|---|
ACC_PUBLIC | 0X0001 | 是否public |
ACC_PRIVATE | 0X0002 | 是否private |
ACC_PROTECTED | 0X0004 | 是否protected |
ACC_STATIC | 0X0008 | 是否static |
ACC_FINAL | 0X0010 | 是否final |
ACC_VOLATILE | 0X0040 | 是否volatile |
ACC_TRANSIENT | 0X0080 | 是否transient |
ACC_SYNTHETIC | 0X0100 | 是否由编译器自动生成 |
ACC_ENUM | 0X0400 | 是否枚举 |
全限定名
类全路径中的"."替换为"/",如:org/xwz/demo/jvm/classfile/Test;
简单名称
指的是没有类型和修饰符的字段或者方法名称;
描述符
描述字段的数据类型、方法的参数列表和返回值,其中基本类型字段的描述符用一个大写字母来标识,对象类型用"L"加上类全限定名来标识。
字段描述符如下:
描述符 | 含义 |
---|---|
B | 基本类型byte |
C | 基本类型char |
D | 基本类型double |
F | 基本类型float |
I | 基本类型int |
J | 基本类型long |
S | 基本类型short |
Z | 基本类型boolean |
V | 特殊类型void |
L | 对象类型,如Ljava/lang/Object |
[ | 数组类型,如[java/lang/String |
六 方法表集合
类文件对方法描述与对字段描述几乎采用完全一致的方式,其结构也完全一致,不同之处在于访问标志有所区别,如方法具有字段不具备的特有修饰,像native/synchronized等,同理字段也具备特别的修饰像volatile等。
方法表访问标志如下:
名称 | 数值 | 含义 |
---|---|---|
ACC_PUBLIC | 0X0001 | 是否public |
ACC_PRIVATE | 0X0002 | 是否private |
ACC_PROTECTED | 0X0004 | 是否protected |
ACC_STATIC | 0X0008 | 是否static |
ACC_FINAL | 0X0010 | 是否final |
ACC_SYNCHRONIZED | 0X0020 | 是否synchronized |
ACC_BRIDGE | 0X0040 | 是否由编译器产生的桥接方法 |
ACC_VARARGS | 0X080 | 是否接受不定参数 |
ACC_NATIVE | 0X0100 | 是否native |
ACC_ABSTRACT | 0X0400 | 是否abstract |
ACC_STRICTFP | 0X0800 | 是否strictfp |
ACC_SYNTHETIC | 0X1000 | 是否由编译器自动产生 |
七 属性表集合
Class文件、字段表、方法表、属性表都可以携带自己的属性表集合,用 于描述某些场景专有的信息。
属性表结构如下:
名称 | 类型 | 数量 |
---|---|---|
attribute_name_index | u2 | 1 |
attribute_length | u4 | 1 |
info | u1 | attribute_length |
属性列表如下:
名称 | 位置 | 含义 |
---|---|---|
Code | 方法表 | 编译后的字节码指令 |
ConstantValue | 字段表 | final定义的常量值 |
Deprecated | 类、字段表、方法表 | 声明deprecated的方法和字段 |
Exceptions | 方法表 | 方法抛出的异常 |
EnclosingMethod | 类文件 | 标识这个类所在的外围方法,仅当一个类为局部类或者匿名类时才拥有此属性 |
InnerClasses | 类文件 | 内部类列表 |
LineNumberTable | Code属性 | Java源码的行号与字节码指令的对应关系 |
LocalVariableTable | Code属性 | 方法的局部变量描述 |
StackMapTable | Code属性 | JDK1.6中新增的属性,供新的类型检查验证器(Type Checker)检查和处理目标方法的局部变量和操作数栈所需要的类型是否匹配 |
SourceFile | 类文件 | 记录源文件名称 |
Signature | 类、方法表、字段表 | JDK1.5中新增的属性,用于支持泛型下的方法签名,在Java语言中,任何类、 接口、初始化方法或成员的泛型签名如果包含了类型变量(Type Variables)或参数化类型(Parameterized Types),则Signature属性会为它记录泛型签名信息。由于Java的泛型采用擦除法实现,在为了避免类型信息被擦除后导致签名混乱,需要这个属性记录泛型中的相关信息。 |
SourceDebugExtension | 类文件 | JDK1.6中新增的属性,用于存储额外的调试信息 |
Synthetic | 类、方法表、字段表 | 标识方法或字段为编译器自动生成的 |
LocalVariableTypeTable | 类 | JDK1.5中新增的属性,使用特征签名代替描述符,是为了引入泛型语法之后能描述泛型参数化类型而添加 |
RuntimeVisibleAnnotations | 类、方法表、字段表 | JDK1.5新增的属性,为动态注解提供支持,用于注明哪些注解是运行时(实际上运行时就是进行反射调用)可见的 |
RuntimeInvisibleAnnotations | 类、方法表、字段表 | JDK1.5新增的属性,用于指明哪些注解是运行时不可见的 |
RuntimeVisibleParameterAnnotations | 方法表 | JDK1.5新增的属性,作用与RuntimeVisibleAnnotations属性类似,只不过作用对象为方法参数 |
RuntimeInvisibleParameterAnnotations | 方法表 | JDK1.5新增的属性,作用与RuntimeInvisibleAnnotations属性类似,只不过作用对象为方法参数 |
AnnotationDefault | 方法表 | JDK1.5新增的属性,用于记录注解类元素的默认值 |
BootstrapMethods | 类文件 | JDK1.7中新增的属性,用于保存invokedynamic指令引用的引导方法限定符 |
下面说下属性表中的重点属性:
Code属性
方法体中的代码经过Javac编译处理后,最终变为字节码指令,存储在方法表的属性集合Code属性中,但并非所有方法表都有Code属性,如抽象类或接口。
Code属性表结构如下:
名称 | 类型 | 数量 | 含义 |
---|---|---|---|
attribute_name_index | u2 | 1 | 指向CONSTANT_Utf8_info类型常量的值固定为"Code" |
attribute_length | u4 | 1 | 标识属性值的总长度 |
max_stack | u2 | 1 | 代表了操作数栈(Operand Stacks)深度的最大值 |
max_locals | u2 | 1 | 代表了局部变量所表示的存储空间 |
code_length | u4 | 1 | 字节码指令长度 |
code | u1 | code_length | 存储编译后产生的字节码指令 |
exception_table_length | u2 | 1 | |
exception_table | exception_info | exception_table_length | |
attribute_count | u2 | ||
attributes | attribute_info | attribute_count |
Exceptions属性
在方法表中与Code属性平级的一项属性,作用是列举出方法中可能抛出的受检查异常(Checked Exceptions)。
Exceptions属性表结构如下:
名称 | 类型 | 数量 |
---|---|---|
attribute_name_index | u2 | 1 |
attribute_length | u4 | 1 |
number_of_exceptions | u2 | 1 |
exception_index_table | u2 | number_of_exceptions |
LineNumberTable属性
描述Java源码行号与字节码行号(字节码的偏移量)之间的对应关系,可以在编译的时候分别使用-g:none和-g:lines选项来取消或者要求生成这项信息。
LineNumberTable属性表结构如下:
名称 | 类型 | 数量 |
---|---|---|
attribute_name_index | u2 | 1 |
attribute_length | u4 | 1 |
line_number_table_length | u2 | 1 |
line_number_table | line_number_info | line_number_table_length |
** LocalVariableTable属性**
描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系。
LocalVariableTable属性表结构如下:
名称 | 类型 | 数量 |
---|---|---|
attribute_name_index | u2 | 1 |
attribute_length | u4 | 1 |
local_varible_table_length | u2 | 1 |
local_variable_table | local_variable_info | local_varible_table_length |
局部变量表结构如下:
名称 | 类型 | 数量 | 含义 |
---|---|---|---|
start_pc | u2 | 1 | 代表局部变量的生命周期开始的字节码偏移量 |
length | u2 | 1 | 代表局部变量的生命周期作用范围覆盖的长度 |
name_index | u2 | 1 | 指向常量池中CONSTANT_Utf8_info型常量的索引,代表局部变量的名称 |
descriptor_index | u2 | 1 | 指向常量池中CONSTANT_Utf8_info型常 量的索引,代表局部变量的描述符 |
index | u2 | 1 | 局部变量在栈帧局部变量表中Slot的位置,当这个变量数据类型是64位类型时(double和long),它占用的Slot为index和index+1两个 |
SourceFile属性
记录生成这个Class文件的源码文件名称,sourcefile_index数据项是指向常量池中CONSTANT_Utf8_info型常量的索引,常量值是源码文件的文件名。
SourceFile属性表结构如下:
名称 | 类型 | 数量 |
---|---|---|
attribute_name_index | u2 | 1 |
attribute_length | u4 | 1 |
sourcefile_index | u2 | 1 |
ConstantValue属性
作用是通知虚拟机自动为静态变量赋值,只有被static关键字修饰的常量(类变量)才可以使用这项属性。目前Sun Javac编译器的选择是:如果同时使用final和static 来修饰一个变量,并且这个变量的数据类型是基本类型或者String的话,就生成ConstantValue属性来进行初 始化,如果这个变量没有被final修饰,或者并非基本类型及字符串,则将会选择在<clinit>方法中进行初始化。
ConstantValue属性表结构如下:
名称 | 类型 | 数量 |
---|---|---|
attribute_name_index | u2 | 1 |
attribute_length | u4 | 1 |
constantvalue_index | u2 | 1 |
InnerClasses属性
用于记录内部类与宿主类之间的关联关系,"number_of_classes"代表需要记录多少个内部类信息。
InnerClasses属性表结构如下:
名称 | 类型 | 数量 |
---|---|---|
attribute_name_index | u2 | 1 |
attribute_length | u4 | 1 |
number_of_classes | u2 | 1 |
inner_classes | inner_classes_info | number_of_classes |
inner_classes_info表结构如下:
名称 | 类型 | 数量 | 含义 |
---|---|---|---|
inner_class_info_index | u2 | 1 | 指向常量池中CONSTANT_Class_info型常量的索引,代表内部类的符号引用 |
outer_class_info_index | u2 | 1 | 指向常量池中CONSTANT_Class_info型常量的索引,代表宿主类的符号引用 |
inner_name_index | u2 | 1 | 代表内部类的名称 |
inner_class_access_flags | u2 | 1 | 内部类的访问标志 |
内部类访问标志如下:
名称 | 数值 | 含义 |
---|---|---|
ACC_PUBLIC | 0X0001 | 是否public |
ACC_PRIVATE | 0X0002 | 是否private |
ACC_PROTECTED | 0X0004 | 是否protected |
ACC_STATIC | 0X0008 | 是否static |
ACC_FINAL | 0X0010 | 是否final |
ACC_INTERFACE | 0X0040 | 是否interface |
ACC_ABSTRACT | 0X080 | 是否abstract |
ACC_SYNTHETIC | 0X0100 | 是否非用户代码产生 |
ACC_ANNOTATION | 0X0400 | 是否是一个注解 |
ACC_ENUM | 0X0800 | 是否是一个枚举 |
Deprecated和Synthetic属性
两者都属于标志类型的布尔属性,只存在有和没有的区别,没有属性值的概念。Deprecated代表已经不再推荐使用;Synthetic代表字段或者方法并不是由Java源码直接产生的,而是由编译器自行添加的。
其属性表结构如下:
名称 | 类型 | 数量 |
---|---|---|
attribute_name_index | u2 | 1 |
attribute_length | u2 | 1 |
StackMapTable属性
在JDK1.6发布后增加到Class文件规范中,它是一个复杂的变长属性,位于Code属性的属性表中,会在虚拟机类加载的字节码验证阶段被新类型检查验证器(Type Checker)使用,目的在于代替以前比较消耗性能的基于数据流分析的类型推导验证器;一个方法的Code属性最多只能有一个StackMapTable属性。
StackMapTable属性表结构如下:
名称 | 类型 | 数量 |
---|---|---|
attribute_name_index | u2 | 1 |
attribute_length | u4 | 1 |
number_of_entries | u2 | 1 |
stack_map_frame | stack_map_frame entries | 1 |
费了九牛二虎之力,终于把理论部分整理完毕,是时候该整个demo让咱们一起玩玩真实的。说句实话,能把上面的部分看完,看到这里的是真英雄,会有一种“众里寻他千百度,蓦然回首那人却在灯火阑珊处”的感觉。
demo会分三个部分展示,其一是Java源文件;jdk提供的用于分析class文件的工具javap命令,用"javap -v xxx.class"打开查看字节码情况;其三使用文档工具打开字节码文件的16进制查看,Windows推荐notepad++(需要安装插件),Mac推荐sublime,这里使用sublime,具体演示情况请下阅。
demo展示Java源文件如下:
package org.xwz.xwzdemo.jvm.classfile;
import java.io.Serializable;
/**
* 解析class文件的演示类
* 1/继承一个类,实现两个接口
* */
public class ReadClass extends Exception implements Runnable,Serializable {
//常量信息
private final static String XWZ_NAME = "阿伦故事";
//public修饰int
public int xwz_age = 29;
//transient修饰boolean
transient boolean isMale = true;
//private变量
private String address;
//静态代码块
static {
long girlFriends = 8;
}
//有参构造
ReadClass(String address){
this.address = address;
}
//类方法
static void initXwz(){
System.out.println("---initXwz method---");
}
//普通方法
public void whoAmI(){
System.out.println("---who am i---");
}
//实现方法
@Override
public void run() {
System.out.println("---this is run method");
}
}
javap展示字节码文件如下:
Classfile /Users/bjqxdn0921/xwz-test/xwz-demo/src/main/java/org/xwz/xwzdemo/jvm/classfile/ReadClass.class
Last modified 2019-8-13; size 846 bytes
MD5 checksum f534aebea36571f07dcc45a7b348cc1b
Compiled from "ReadClass.java"
public class org.xwz.xwzdemo.jvm.classfile.ReadClass extends java.lang.Exception implements java.lang.Runnable,java.io.Serializable
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #12.#34 // java/lang/Exception."<init>":()V
#2 = Fieldref #11.#35 // org/xwz/xwzdemo/jvm/classfile/ReadClass.xwz_age:I
#3 = Fieldref #11.#36 // org/xwz/xwzdemo/jvm/classfile/ReadClass.isMale:Z
#4 = Fieldref #11.#37 // org/xwz/xwzdemo/jvm/classfile/ReadClass.address:Ljava/lang/String;
#5 = Fieldref #38.#39 // java/lang/System.out:Ljava/io/PrintStream;
#6 = String #40 // ---who am i---
#7 = Methodref #41.#42 // java/io/PrintStream.println:(Ljava/lang/String;)V
#8 = String #43 // ---this is run method
#9 = Long 8l
#11 = Class #44 // org/xwz/xwzdemo/jvm/classfile/ReadClass
#12 = Class #45 // java/lang/Exception
#13 = Class #46 // java/lang/Runnable
#14 = Class #47 // java/io/Serializable
#15 = Utf8 XWZ_NAME
#16 = Utf8 Ljava/lang/String;
#17 = Utf8 ConstantValue
#18 = String #48 // 阿伦故事
#19 = Utf8 xwz_age
#20 = Utf8 I
#21 = Utf8 isMale
#22 = Utf8 Z
#23 = Utf8 address
#24 = Utf8 <init>
#25 = Utf8 (Ljava/lang/String;)V
#26 = Utf8 Code
#27 = Utf8 LineNumberTable
#28 = Utf8 whoAmI
#29 = Utf8 ()V
#30 = Utf8 run
#31 = Utf8 <clinit>
#32 = Utf8 SourceFile
#33 = Utf8 ReadClass.java
#34 = NameAndType #24:#29 // "<init>":()V
#35 = NameAndType #19:#20 // xwz_age:I
#36 = NameAndType #21:#22 // isMale:Z
#37 = NameAndType #23:#16 // address:Ljava/lang/String;
#38 = Class #49 // java/lang/System
#39 = NameAndType #50:#51 // out:Ljava/io/PrintStream;
#40 = Utf8 ---who am i---
#41 = Class #52 // java/io/PrintStream
#42 = NameAndType #53:#25 // println:(Ljava/lang/String;)V
#43 = Utf8 ---this is run method
#44 = Utf8 org/xwz/xwzdemo/jvm/classfile/ReadClass
#45 = Utf8 java/lang/Exception
#46 = Utf8 java/lang/Runnable
#47 = Utf8 java/io/Serializable
#48 = Utf8 阿伦故事
#49 = Utf8 java/lang/System
#50 = Utf8 out
#51 = Utf8 Ljava/io/PrintStream;
#52 = Utf8 java/io/PrintStream
#53 = Utf8 println
{
public int xwz_age;
descriptor: I
flags: ACC_PUBLIC
transient boolean isMale;
descriptor: Z
flags: ACC_TRANSIENT
org.xwz.xwzdemo.jvm.classfile.ReadClass(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags:
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: invokespecial #1 // Method java/lang/Exception."<init>":()V
4: aload_0
5: bipush 29
7: putfield #2 // Field xwz_age:I
10: aload_0
11: iconst_1
12: putfield #3 // Field isMale:Z
15: aload_0
16: aload_1
17: putfield #4 // Field address:Ljava/lang/String;
20: return
LineNumberTable:
line 23: 0
line 13: 4
line 15: 10
line 24: 15
line 25: 20
public void whoAmI();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #6 // String ---who am i---
5: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 28: 0
line 29: 8
public void run();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #8 // String ---this is run method
5: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 33: 0
line 34: 8
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=2, locals=2, args_size=0
0: ldc2_w #9 // long 8l
3: lstore_0
4: return
LineNumberTable:
line 20: 0
line 21: 4
}
SourceFile: "ReadClass.java"
sublime展示字节码文件如下:
cafe babe 0000 0034 0036 0a00 0c00 2209
000b 0023 0900 0b00 2409 000b 0025 0900
2600 2708 0028 0a00 2900 2a08 002b 0500
0000 0000 0000 0807 002c 0700 2d07 002e
0700 2f01 0008 5857 5a5f 4e41 4d45 0100
124c 6a61 7661 2f6c 616e 672f 5374 7269
6e67 3b01 000d 436f 6e73 7461 6e74 5661
6c75 6508 0030 0100 0778 777a 5f61 6765
0100 0149 0100 0669 734d 616c 6501 0001
5a01 0007 6164 6472 6573 7301 0006 3c69
6e69 743e 0100 1528 4c6a 6176 612f 6c61
6e67 2f53 7472 696e 673b 2956 0100 0443
6f64 6501 000f 4c69 6e65 4e75 6d62 6572
5461 626c 6501 0006 7768 6f41 6d49 0100
0328 2956 0100 0372 756e 0100 083c 636c
696e 6974 3e01 000a 536f 7572 6365 4669
6c65 0100 0e52 6561 6443 6c61 7373 2e6a
6176 610c 0018 001d 0c00 1300 140c 0015
0016 0c00 1700 1007 0031 0c00 3200 3301
000e 2d2d 2d77 686f 2061 6d20 692d 2d2d
0700 340c 0035 0019 0100 152d 2d2d 7468
6973 2069 7320 7275 6e20 6d65 7468 6f64
0100 276f 7267 2f78 777a 2f78 777a 6465
6d6f 2f6a 766d 2f63 6c61 7373 6669 6c65
2f52 6561 6443 6c61 7373 0100 136a 6176
612f 6c61 6e67 2f45 7863 6570 7469 6f6e
0100 126a 6176 612f 6c61 6e67 2f52 756e
6e61 626c 6501 0014 6a61 7661 2f69 6f2f
5365 7269 616c 697a 6162 6c65 0100 0ce9
98bf e4bc a6e6 9585 e4ba 8b01 0010 6a61
7661 2f6c 616e 672f 5379 7374 656d 0100
036f 7574 0100 154c 6a61 7661 2f69 6f2f
5072 696e 7453 7472 6561 6d3b 0100 136a
6176 612f 696f 2f50 7269 6e74 5374 7265
616d 0100 0770 7269 6e74 6c6e 0021 000b
000c 0002 000d 000e 0004 001a 000f 0010
0001 0011 0000 0002 0012 0001 0013 0014
0000 0080 0015 0016 0000 0002 0017 0010
0000 0004 0000 0018 0019 0001 001a 0000
003d 0002 0002 0000 0015 2ab7 0001 2a10
1db5 0002 2a04 b500 032a 2bb5 0004 b100
0000 0100 1b00 0000 1600 0500 0000 1700
0400 0d00 0a00 0f00 0f00 1800 1400 1900
0100 1c00 1d00 0100 1a00 0000 2500 0200
0100 0000 09b2 0005 1206 b600 07b1 0000
0001 001b 0000 000a 0002 0000 001c 0008
001d 0001 001e 001d 0001 001a 0000 0025
0002 0001 0000 0009 b200 0512 08b6 0007
b100 0000 0100 1b00 0000 0a00 0200 0000
2100 0800 2200 0800 1f00 1d00 0100 1a00
0000 2100 0200 0200 0000 0514 0009 3fb1
0000 0001 001b 0000 000a 0002 0000 0014
0004 0015 0001 0020 0000 0002 0021