JVM字节码数据结构总览和读取

JVM字节码数据结构总览和读取

前言

最近写J2ME深感各种java反编译不得我心,决定自己尝试写一个自己的java反编译,第一步实现javap。现在把字节码的数据结构贴出来一部分,以及我的实现思路。

CLASSFILE

官网文档如下

cpp 复制代码
ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

常量池

Constant Kind Tag Section
CONSTANT_Class 7 §4.4.1
CONSTANT_Fieldref 9 §4.4.2
CONSTANT_Methodref 10 §4.4.2
CONSTANT_InterfaceMethodref 11 §4.4.2
CONSTANT_String 8 §4.4.3
CONSTANT_Integer 3 §4.4.4
CONSTANT_Float 4 §4.4.4
CONSTANT_Long 5 §4.4.5
CONSTANT_Double 6 §4.4.5
CONSTANT_NameAndType 12 §4.4.6
CONSTANT_Utf8 1 §4.4.7
CONSTANT_MethodHandle 15 §4.4.8
CONSTANT_MethodType 16 §4.4.9
CONSTANT_Dynamic 17 §4.4.10
CONSTANT_InvokeDynamic 18 §4.4.10
CONSTANT_Module 19 §4.4.11
CONSTANT_Package 20 §4.4.12

代码结构

宏定义

c 复制代码
#define u2 unsigned short
#define u4 unsigned int
#define u1 unsigned char

#define s2  short
#define s4  int
#define s1  char

// 既然是java,多一个null也是很合理的
#define null nullptr

ClassFile.h

cpp 复制代码
#ifndef CLASSFILE_H
#define CLASSFILE_H

/**
 * @since 2025-11-27
 * @author authorZhao
 */

#include <iosfwd>
#include <string>
#include "ConstPool.h"
#include "AttributeInfo.h"

// 前向声明
class field_info;
class method_info;

typedef field_info* FieldInfo;
typedef method_info* MethodInfo;

/**
 * 字段信息类 - 表示类或接口中声明的字段
 *  字段的属性如下所示
 *  {@link ConstantValue }
 *  {@link Synthetic }
 *  {@link Deprecated }
 *  {@link Signature }
 *  {@link RuntimeVisibleAnnotations }
 *  {@link RuntimeInvisibleAnnotations }
 *  {@link RuntimeVisibleTypeAnnotations }
 *  {@link RuntimeInvisibleTypeAnnotations }
 */
class field_info {
public:
    u2 access_flags;         // 访问标志
    u2 name_index;           // 字段名在常量池中的索引
    u2 descriptor_index;     // 字段描述符在常量池中的索引
    u2 attributes_count;     // 属性数量
    AttributeInfo* attributes; // 属性表attributes[attributes_count];

    // 辅助信息
    std::string flagName;
    std::string fieldName;
    std::string fieldDescriptor;
    std::string javaField;

    field_info() : access_flags(0), name_index(0), descriptor_index(0), attributes_count(0), attributes(nullptr) {}
    ~field_info();

    friend std::ostream& operator<<(std::ostream& os, const field_info& obj);

    std::string getFlagName();
    std::string getFieldName();
    std::string getFieldDescriptor();
    std::string getJavaField();

    void parseFiledInfo(const ConstPool* constPool);
    // 属性解析暂时忽略
};

/**
 *  方法信息类 - 表示类或接口中声明的方法
 *  方法的属性有下面集中类型
 *  {@link Code }
 *  {@link Exceptions }
 *  {@link RuntimeVisibleParameterAnnotations }
 *  {@link RuntimeInvisibleParameterAnnotations }
 *  {@link AnnotationDefault }
 *  {@link MethodParameters }
 *  {@link Synthetic }
 *  {@link Deprecated }
 *  {@link Signature }
 *  {@link RuntimeVisibleAnnotations }
 *  {@link RuntimeInvisibleAnnotations }
 *  {@link RuntimeVisibleTypeAnnotations }
 *  {@link RuntimeInvisibleTypeAnnotations }
 */
class method_info {
public:
    u2 access_flags;         // 访问标志
    u2 name_index;           // 方法名在常量池中的索引
    u2 descriptor_index;     // 方法描述符在常量池中的索引
    u2 attributes_count;     // 属性数量
    AttributeInfo* attributes; // 属性表 attributes[attributes_count];


    // 辅助信息,暂时和field分开
    std::string flagName;
    std::string methodName;
    std::string methodDescriptor;
    std::string javaMethod;
    std::string getFlagName();
    std::string getMethodName();
    std::string getMethodDescriptor();
    std::string getJavaMethod();

    void parseMethodInfo(const ConstPool* constPool);


    method_info() : access_flags(0), name_index(0), descriptor_index(0), attributes_count(0), attributes(nullptr) {}
    ~method_info();

    friend std::ostream& operator<<(std::ostream& os, const method_info& obj);
};

/**
 * 类文件格式,其中属性如下
 * {@link SourceFile }
 * {@link InnerClasses }
 * {@link EnclosingMethod }
 * {@link SourceDebugExtension }
 * {@link BootstrapMethods }
 * {@link Module }
 * {@link ModulePackages }
 * {@link ModuleMainClass }
 * {@link NestHost }
 * {@link NestMembers }
 * {@link Record }
 * {@link PermittedSubclasses }
 * {@link Synthetic }
 * {@link Deprecated }
 * {@link Signature }
 * {@link RuntimeVisibleAnnotations }
 * {@link RuntimeInvisibleAnnotations }
 * {@link RuntimeVisibleTypeAnnotations }
 * {@link RuntimeInvisibleTypeAnnotations }
 *
 */
class ClassFile {
public:
    // 构造函数
    ClassFile();

    // 析构函数
    ~ClassFile();

    u4 magic;                    // 魔数 0xCAFEBABE
    u2 minor_version;            // 次版本号 1.1 = 45
    u2 major_version;            // 主版本号
    u2 constant_pool_count;      // 常量池计数
    ConstPool* constant_pool;    // 常量池对象指针 constant_pool[constant_pool_count-1];
    u2 access_flags;             // 访问标志
    u2 this_class;               // 当前类在常量池中的索引
    u2 super_class;              // 父类在常量池中的索引
    u2 interfaces_count;         // 接口数量
    u2* interfaces;              // 接口索引数组 interfaces[interfaces_count];
    u2 fields_count;             // 字段数量
    FieldInfo fields;            // 字段信息数组 fields[fields_count];
    u2 methods_count;            // 方法数量
    MethodInfo methods;          // 方法信息数组 methods[methods_count];
    u2 attributes_count;         // 属性数量
    AttributeInfo* attributes;    // 属性信息数组  attributes[attributes_count];

    friend std::ostream& operator<<(std::ostream& os, const ClassFile& obj);
};


#endif //CLASSFILE_H

常量池代码设计

从上到下第一个核心结构就是常量池,我这里直接使用指针表示

cpp 复制代码
ConstPool* constant_pool;

常量池管理常量,其核心结构图下

cpp 复制代码
class ConstPool {
private:
    std::vector<std::shared_ptr<constant_pool_info>> constants; // 常量池项数组
    u2 constant_pool_count; // 常量池项数量(包括dummy项)
// 省略部分方法
}

constant_pool_info

cpp 复制代码
/**
 * 常量池项基类 - 所有常量池项的抽象基类
 * 定义了常量池项的基本接口和公共属性
 */
class constant_pool_info {
public:
    u1 tag; // 常量池项类型标识

    virtual ~constant_pool_info() = default;

    // 获取常量池项的字符串表示
    virtual std::string toString() const = 0;

    // 获取常量池项的大小(字节)
    virtual size_t getSize() const = 0;

    // 验证常量池项的有效性
    virtual bool validate() const = 0;

    // 从文件流中读取数据
    virtual void readFrom(std::istream& file) = 0;
};

具体常量下一期在水

字段和方法

cpp 复制代码
u2 access_flags;
u2 name_index;的索引
u2 descriptor_index;
u2 attributes_count;
AttributeInfo* attributes;

其中字段和方法结构基本一样,但是不做管理,等待javap实现完毕,需要增删改查字段和方法的时候在进行管理

目前仅需简单的使用数组管理,在代码里面退化为指针。

AttributeInfo

这个属性恐怕才是字节码里面最为复杂的,整个attribute_info起码还能水几十章,暂放,设计思路和常量池一样

cpp 复制代码
class AttributeInfo {
private:
    std::vector<std::shared_ptr<attribute_info>> attributes; // 常量池项数组
    u2 attributes_count; // 属性数量(包括dummy项)
    // 省略...
}

我在classfile里面注释了每个属性出现的位置,特别是字段和方法,虽然属性很多,但不是都是通用的。

javap读取代码思路

我自己实现的

cpp 复制代码
ClassFile* ClassFileReader::readFile(const std::string& filename) {
    std::ifstream file(filename, std::ios::binary);
    if (!file.is_open()) {
        std::cerr << "Error: Cannot open file " << filename << std::endl;
        return nullptr;
    }
    auto classFile = new ClassFile();

    try {
        // 读取魔数
        classFile->magic = IoUtil::readU4(file);

        // 检查魔数
        if (classFile->magic != 0xCAFEBABE) {
            std::cerr << "Error: Invalid magic number: 0x" << std::hex << classFile->magic << std::dec << std::endl;
            delete classFile;
            file.close();
            return nullptr;
        }

        // 读取版本号
        classFile->minor_version = IoUtil::readU2(file);
        classFile->major_version = IoUtil::readU2(file);

        // 读取常量池
        readConstantPool(file, classFile);

        // 读取访问标志
        classFile->access_flags = IoUtil::readU2(file);
        // 访问标识符暂时也不贴出来,下期再水
        auto classFlag = AccessFlags(classFile->access_flags).getClassFlags();
        for (auto class_flag : classFlag) {
            std::cout << "classFlag=" << class_flag << std::endl;
        }


        // 读取类信息
        classFile->this_class = IoUtil::readU2(file);
        classFile->super_class = IoUtil::readU2(file);

        // 读取接口
        classFile->interfaces_count = IoUtil::readU2(file);
        readInterfaces(file, classFile);

        // 读取字段
        readFields(file, classFile);

        // 读取方法
        readMethods(file, classFile);

        // 读取属性
        readAttributes(file, classFile);

        file.close();
        return classFile;
    }
    catch (const std::exception& e) {
        std::cerr << "Error reading class file: " << e.what() << std::endl;
        delete classFile;
        file.close();
        return nullptr;
    }
}

工具类

IoUtil

cpp 复制代码
u1 IoUtil::readU1(std::istream &file) {
    u1 value;
    file.read(reinterpret_cast<char *>(&value), 1);
    if (file.gcount() != 1) {
        throw std::runtime_error("Failed to read u1");
    }
    return value;
}

u2 IoUtil::readU2(std::istream &file) {
    u1 high = readU1(file);
    u1 low = readU1(file);
    return (high << 8) | low;
}

u4 IoUtil::readU4(std::istream &file) {
    u2 high = readU2(file);
    u2 low = readU2(file);
    return (high << 16) | low;
}

参考文档以及链接

Chapter 4. The class File Format

JVM字节码attribute_info

JVM StackMapTable 属性详解

相关推荐
程序员梁白开2 小时前
从源码到实战:线程池处理任务的完整流程解析
java·jvm·spring·java-ee
safestar20125 小时前
Elasticsearch ILM实战:从数据热恋到冷静归档的自动化管理
java·开发语言·jvm·elasticsearch·es
北郭guo6 小时前
垃圾回收底层原理【深入了解】
java·jvm·算法
7***47717 小时前
【SQL】掌握SQL查询技巧:数据分组与排序
java·jvm·sql
一只小透明啊啊啊啊8 小时前
JVM内存
jvm
嘟嘟w8 小时前
JVM的内存结构
java·jvm
平原人9 小时前
JVM字节码attribute_info
jvm·javap·class字节码·attribute_info
她说..10 小时前
Spring AOP 操作日志框架(CV可用)
java·jvm·spring·springboot·springcloud
p***s9110 小时前
java进阶1——JVM
java·开发语言·jvm