From dbedc170322908fe20c1e36c9438af8e904cbbc2 Mon Sep 17 00:00:00 2001 From: chentianya Date: Wed, 26 Aug 2026 09:59:18 +0800 Subject: [PATCH] =?UTF-8?q?=E9=98=B6=E6=AE=B54-2a=EF=BC=9Avm/Image=20V2?= =?UTF-8?q?=EF=BC=88=E5=A4=B4=20128B/6=20=E6=AE=B5=E6=A0=A1=E9=AA=8C/?= =?UTF-8?q?=E6=A7=BD=E8=A1=A8=E8=AE=BF=E9=97=AE=E5=99=A8=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ImageHeader:V2 字段(offset_slots/offset_values + n_slots/values_size/ offset_meta/max_stack);kModelVersion=2(STATOR2) - 段校验:const/funcs/code/slots/values + meta=0 空段处理、尺寸一致性、对齐 - 常量表 8B 无 tag;FuncRow.code_len 字节;slot_addr/slots_bytes 访问器 - 验证:V2 产物解析 ok、model_matches(STATOR,2)/sha 校验通过 - 状态:Machine.cpp 仍破(4-2b) --- vm/include/vm/Image.h | 69 ++++++++++------- vm/src/Image.cpp | 167 +++++++++++++++++++----------------------- 2 files changed, 117 insertions(+), 119 deletions(-) diff --git a/vm/include/vm/Image.h b/vm/include/vm/Image.h index 33bb5bd..e6614d9 100644 --- a/vm/include/vm/Image.h +++ b/vm/include/vm/Image.h @@ -1,11 +1,12 @@ /** * @file Image.h - * @brief vm 自带的 .stb 只读视图(执行器侧;compiler 各有实现) + * @brief vm 自带的 .stb 只读视图(执行器侧;compiler 各有实现,V2) * @author * @date 2026-08-21 * - * @details 格式契约见 Doc/isa/指令与映像.md(12.13 修订:头 104 = 72 + 型号标识[32], - * 文件尾 SHA-256[32])。解析头与段、补型号匹配与 SHA-256 校验都在本模块完成。 + * @details 格式契约见 Doc/isa/指令与映像.md(V2:头 128B、version=2、型号 STATOR2、 + * 常量表 8B 无 tag、槽表段 8B/条 {addr,预留}、值段变宽紧凑、文件尾 SHA-256[32])。 + * 解析头与段、补型号匹配与 SHA-256 校验都在本模块完成。 */ #pragma once @@ -19,11 +20,11 @@ namespace vm { /// 执行器内建支持型号(与 machine.toml [meta] 对齐;.stb 型号不匹配直接拒绝)。 static const char* const kModelName = "STATOR"; - /// 执行器内建支持型号版本(与 machine.toml [meta] 对齐)。 - static const uint32_t kModelVersion = 1; + /// 执行器内建支持型号版本(V2;型号标识 = "STATOR2")。 + static const uint32_t kModelVersion = 2; /** - * @brief 映像头字段(104 字节;字段顺序与 Doc/isa/指令与映像.md 一致)。 + * @brief 映像头字段(V2:128 字节;字段顺序与 Doc/isa/指令与映像.md §8.1 一致)。 * @details 全部为小端值;型号标识 32 字节 @72 不在此结构内(见 model_id())。 */ struct ImageHeader { @@ -31,41 +32,45 @@ namespace vm { uint32_t dt_ms = 0; ///< 周期时长(毫秒,定时器/计数器推进步长) uint64_t project_hash = 0; ///< 工程哈希(FNV-1a 64,只读展示) uint32_t entry_fn_id = 0; ///< 入口函数(PROGRAM MAIN)在函数表的下标 - uint32_t n_globals = 0; ///< 全局变量槽数(数据段最前段) - uint32_t n_i = 0; ///< 输入变量(I)槽数 - uint32_t n_q = 0; ///< 输出变量(Q)槽数 - uint32_t n_m = 0; ///< 中间变量(M)槽数 + uint32_t n_globals = 0; ///< 全局变量槽数(槽表最前段) + uint32_t n_i = 0; ///< 输入槽数(保留恒 0) + uint32_t n_q = 0; ///< 输出槽数(保留恒 0) + uint32_t n_m = 0; ///< 中间槽数(保留恒 0) uint32_t n_consts = 0; ///< 常量表条目数 uint32_t n_funcs = 0; ///< 函数表行数 uint32_t offset_const = 0; ///< 常量表段起点(相对文件头) uint32_t offset_funcs = 0; ///< 函数表段起点 uint32_t offset_code = 0; ///< 字节码段起点 - uint32_t offset_fb = 0; ///< FB 表段起点 - uint32_t offset_data = 0; ///< 数据段起点(之后为 SHA-256 文件尾) + uint32_t offset_slots = 0; ///< 槽表段起点 + uint32_t offset_values = 0; ///< 值段起点(16 对齐) + uint32_t n_slots = 0; ///< 槽表条目数(@104) + uint32_t values_size = 0; ///< 值段字节数(@108) + uint32_t offset_meta = 0; ///< 元数据段偏移(@112;0 = 空段) + uint32_t max_stack = 0; ///< 栈区字节数(@116,编译期算) }; /** - * @brief 函数表一行(12 字节)。 + * @brief 函数表一行(12 字节;code_len 为字节数,变长指令)。 */ struct FuncRow { uint32_t nregs = 0; ///< 本函数寄存器数(函数头 nregs) uint32_t code_offset = 0; ///< 相对字节码段起点(字节) - uint32_t code_len = 0; ///< 指令条数 + uint32_t code_len = 0; ///< 字节码长度(字节) }; /** - * @brief 常量表一项(12 字节)。 + * @brief 常量表一项(8 字节:原始位模式,无 tag——LOADK 按 func 位段解释)。 */ struct ConstEntry { - uint32_t tag = 0; ///< 类型标记(isa::types::TypeTag:0=BOOL、1=INT、2=TIME) - uint64_t value = 0; ///< 值(LOADK 时按 tag 解释) + uint64_t value = 0; ///< 原始 64 位位模式 }; /** * @brief 只读视图:指向外部缓冲的校验过的 .stb 映像。 - * @details from() 完成魔数/版本/头/各段/尺寸一致性校验;型号匹配与 SHA-256 - * 由 model_matches() / sha_ok() 另行查询(Machine::create 里一起做)。 - * 所有方法在校验失败(ok() == false)时返回安全默认值。 + * @details from() 完成魔数/版本/头/六段/尺寸一致性校验(元数据段 0 = 空段); + * 型号匹配与 SHA-256 由 model_matches() / sha_ok() 另行查询 + * (Machine::create 里一起做)。所有方法在校验失败(ok() == false) + * 时返回安全默认值。 */ class Image { public: @@ -74,9 +79,9 @@ namespace vm { * @param buf 映像缓冲;可为 nullptr * @param len 缓冲字节数 * @return 校验结果;ok() 判成功,error() 取失败原因 - * @details 校验项:非空、长度 ≥ 头、魔数 STSC、版本 1、五个段偏移单调且在 - * SHA-256 尾之前、常量表/函数表尺寸一致、代码段 4 字节对齐、 - * 入口 fn_id 在函数表内。 + * @details 校验项:非空、长度 ≥ 头、魔数 STSC、版本 2、六段偏移单调且在 + * SHA-256 尾之前、常量表/函数表/槽表尺寸一致、值段大小一致、 + * 代码段对齐、入口 fn_id 在函数表内。 */ static Image from(const uint8_t* buf, size_t len); @@ -104,19 +109,29 @@ namespace vm { FuncRow func_row(size_t i) const; /** - * @brief 读常量表一项。 + * @brief 读常量表一项(8B 原始值)。 * @param i 常量下标 * @return 该条目;越界(或校验失败)时全 0 */ ConstEntry const_entry(size_t i) const; + /** + * @brief 读槽表一项的地址(值段内偏移)。 + * @param i 槽号 + * @return addr;越界(或校验失败)时 0 + */ + uint32_t slot_addr(size_t i) const; + + /// @return 槽表段起点;校验失败返回 nullptr。 + const uint8_t* slots_bytes() const; + /// @return 字节码段起点;校验失败返回 nullptr。 const uint8_t* code_bytes() const; - /// @return 数据段起点;校验失败返回 nullptr。 + /// @return 值段起点;校验失败返回 nullptr。 const uint8_t* data_bytes() const; - /// @return 数据段字节数(不含 SHA-256 尾);校验失败返回 0。 + /// @return 值段字节数(不含 SHA-256 尾);校验失败返回 0。 size_t data_len() const; /// @return 型号标识字符串(头 @72 的 32 字节,去 '\0' 截断)。 @@ -125,7 +140,7 @@ namespace vm { /** * @brief 型号标识是否匹配。 * @param name 期望型号名(如 "STATOR") - * @param version 期望版本(如 1) + * @param version 期望版本(如 2) * @return 头内 32 字节与 "name + version" 补零后逐字节相等 */ bool model_matches(const std::string& name, uint32_t version) const; diff --git a/vm/src/Image.cpp b/vm/src/Image.cpp index 3bc49a9..2568ce4 100644 --- a/vm/src/Image.cpp +++ b/vm/src/Image.cpp @@ -1,6 +1,6 @@ /** * @file Image.cpp - * @brief vm 自带的 .stb 只读视图实现(执行器侧;compiler 各有实现) + * @brief vm 自带的 .stb 只读视图实现(执行器侧;compiler 各有实现,V2) * @author * @date 2026-08-21 */ @@ -16,56 +16,52 @@ namespace { /// 映像魔数:"STSC" 小端。 const uint32_t kMagic = 0x43545353u; - /// 映像格式版本。 - const uint32_t kVersion = 1; - /// 映像头字节数(72 原字段 + 型号标识[32] @72,12.13 修订)。 - const size_t kHeaderSize = 104; - /// 常量表一行字节数(tag:4 + value:8)。 - const size_t kConstEntrySize = 12; + /// 映像格式版本(V2)。 + const uint32_t kVersion = 2; + /// 映像头字节数(V2:128)。 + const size_t kHeaderSize = 128; + /// 常量表一行字节数(value:8,无 tag)。 + const size_t kConstEntrySize = 8; /// 函数表一行字节数(nregs/code_offset/code_len 各 4)。 const size_t kFuncRowSize = 12; + /// 槽表一行字节数(addr:4 + 预留:4)。 + const size_t kSlotRowSize = 8; /// SHA-256 摘要长度(文件尾)。 const size_t kSha256Size = 32; /// 型号标识长度(头内 @72,不足补 '\0')。 const size_t kModelIdSize = 32; - // ---- 头字段偏移(与 Doc/isa/指令与映像.md 字段表一致;compiler 侧另有同值一份)---- + // ---- 头字段偏移(与 Doc/isa/指令与映像.md §8.1 一致;compiler 侧另有同值一份)---- constexpr size_t kOffMagic = 0; ///< 魔数 "STSC" - constexpr size_t kOffVersion = 4; ///< 格式版本 + constexpr size_t kOffVersion = 4; ///< 格式版本(V2 = 2) constexpr size_t kOffCycleLimit = 8; ///< 每周期指令数上限 constexpr size_t kOffDtMs = 12; ///< 周期时长(毫秒) constexpr size_t kOffProjectHash = 16; ///< 工程哈希(8 字节) constexpr size_t kOffEntryFnId = 24; ///< 入口函数 fn_id constexpr size_t kOffNGlobals = 28; ///< 全局槽数 - constexpr size_t kOffNI = 32; ///< 输入(I)槽数 - constexpr size_t kOffNQ = 36; ///< 输出(Q)槽数 - constexpr size_t kOffNM = 40; ///< 中间(M)槽数 + constexpr size_t kOffNI = 32; ///< 输入(I)槽数(保留恒 0) + constexpr size_t kOffNQ = 36; ///< 输出(Q)槽数(保留恒 0) + constexpr size_t kOffNM = 40; ///< 中间(M)槽数(保留恒 0) constexpr size_t kOffNConsts = 44; ///< 常量表条目数 constexpr size_t kOffNFuncs = 48; ///< 函数表行数 constexpr size_t kOffConst = 52; ///< 常量表段偏移 constexpr size_t kOffFuncs = 56; ///< 函数表段偏移 constexpr size_t kOffCode = 60; ///< 字节码段偏移 - constexpr size_t kOffFb = 64; ///< FB 表段偏移 - constexpr size_t kOffData = 68; ///< 数据段偏移 - constexpr size_t kOffModelId = 72; ///< 型号标识[32](12.13 修订) + constexpr size_t kOffSlots = 64; ///< 槽表段偏移 + constexpr size_t kOffValues = 68; ///< 值段偏移 + constexpr size_t kOffModelId = 72; ///< 型号标识[32] + constexpr size_t kOffNSlots = 104; ///< 槽表条目数 + constexpr size_t kOffValuesSize = 108; ///< 值段字节数 + constexpr size_t kOffMeta = 112; ///< 元数据段偏移(0 = 空段) + constexpr size_t kOffMaxStack = 116; ///< 栈区字节数 // ---- 段内行偏移 ---- - constexpr size_t kConstTagOff = 0; ///< 常量表行内:tag - constexpr size_t kConstValueOff = 4; ///< 常量表行内:value(8 字节) - constexpr size_t kFuncNregsOff = 0; ///< 函数表行内:nregs - constexpr size_t kFuncCodeOff = 4; ///< 函数表行内:code_offset(字节) - constexpr size_t kFuncLenOff = 8; ///< 函数表行内:code_len(指令条数) + constexpr size_t kConstValueOff = 0; ///< 常量表行内:value(8 字节) + constexpr size_t kFuncNregsOff = 0; ///< 函数表行内:nregs + constexpr size_t kFuncCodeOff = 4; ///< 函数表行内:code_offset(字节) + constexpr size_t kFuncLenOff = 8; ///< 函数表行内:code_len(字节) + constexpr size_t kSlotAddrOff = 0; ///< 槽表行内:addr - // 防漂移:头布局自洽性(与 Doc/isa/指令与映像.md 冻结表核对) - static_assert(kHeaderSize == kOffModelId + kModelIdSize, "header size = 72 + model id[32]"); - static_assert(kOffData > kOffFb && kOffFb > kOffCode && kOffCode > kOffFuncs && - kOffFuncs > kOffConst, - "header field offsets monotonic"); - static_assert(kOffModelId == kOffData + 4, "model id follows data offset field"); - static_assert(kConstEntrySize == kConstValueOff + 8, "const row: tag + value:8"); - static_assert(kFuncRowSize == kFuncLenOff + 4, "func row: nregs/code_offset/code_len"); - - /// 小端读 32 位。 uint32_t get_le32(const uint8_t* p) { return static_cast(p[0]) | (static_cast(p[1]) << 8) @@ -73,7 +69,6 @@ namespace { | (static_cast(p[3]) << 24); } - /// 小端读 64 位。 uint64_t get_le64(const uint8_t* p) { uint64_t v = 0; for (int i = 0; i < 8; ++i) { @@ -84,7 +79,6 @@ namespace { // ---- SHA-256(执行器侧实现,与 compiler 各一份)---- - /// SHA-256 轮常量 K[0..63]。 const uint32_t kShaK[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, @@ -99,14 +93,8 @@ namespace { 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, }; - /// 循环右移。 inline uint32_t rotr(uint32_t x, uint32_t n) { return (x >> n) | (x << (32 - n)); } - /** - * @brief SHA-256 增量状态机。 - * @details 标准 FIPS 180-4 实现:update() 吸收任意长度字节流,final() 输出 - * 32 字节大端摘要。按 64 字节块 process()。 - */ struct Sha256 { uint32_t h[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; ///< 初始哈希值 @@ -114,11 +102,6 @@ namespace { uint8_t block[64]; ///< 当前块缓冲 size_t block_len = 0; ///< 块缓冲已用字节数 - /** - * @brief 吸收数据。 - * @param data 输入字节 - * @param len 字节数 - */ void update(const uint8_t* data, size_t len) { total += len; while (len > 0) { @@ -140,7 +123,6 @@ namespace { } } - /// 压缩一个满块(64 字节,w[0..63] 展开 + 64 轮)。 void process() { uint32_t w[64]; for (int i = 0; i < 16; ++i) { @@ -173,10 +155,6 @@ namespace { h[4] += e; h[5] += f; h[6] += g; h[7] += hh; } - /** - * @brief 结束并输出摘要。 - * @param out 32 字节输出缓冲(大端) - */ void final(uint8_t out[32]) { const uint64_t bitlen = total * 8; const uint8_t pad = 0x80; @@ -198,31 +176,18 @@ namespace { } } - /// 大端读 32 位。 static uint32_t get_be32(const uint8_t* p) { return (static_cast(p[0]) << 24) | (static_cast(p[1]) << 16) | (static_cast(p[2]) << 8) | static_cast(p[3]); } }; - /** - * @brief 一次性 SHA-256。 - * @param data 输入字节 - * @param len 字节数 - * @param out 32 字节摘要输出 - */ void sha256(const uint8_t* data, size_t len, uint8_t out[32]) { Sha256 s; s.update(data, len); s.final(out); } - /** - * @brief 按型号标识约定填充 32 字节:name + version(如 "STATOR1"),余下补 '\0'。 - * @param name 型号名 - * @param version 版本号 - * @param out 32 字节输出缓冲 - */ void fill_model_id(const std::string& name, uint32_t version, char out[32]) { const std::string id = name + std::to_string(version); for (size_t i = 0; i < 32; ++i) { @@ -234,9 +199,6 @@ namespace { /** * @brief 从原始字节构造只读视图(不拷贝,调用方保证生命周期)。 - * @param buf 映像缓冲;可为 nullptr - * @param len 缓冲字节数 - * @return 校验结果;ok() 判成功,error() 取失败原因 */ Image Image::from(const uint8_t* buf, size_t len) { Image v; @@ -272,16 +234,20 @@ Image Image::from(const uint8_t* buf, size_t len) { h.offset_const = get_le32(buf + kOffConst); h.offset_funcs = get_le32(buf + kOffFuncs); h.offset_code = get_le32(buf + kOffCode); - h.offset_fb = get_le32(buf + kOffFb); - h.offset_data = get_le32(buf + kOffData); + h.offset_slots = get_le32(buf + kOffSlots); + h.offset_values = get_le32(buf + kOffValues); + h.n_slots = get_le32(buf + kOffNSlots); + h.values_size = get_le32(buf + kOffValuesSize); + h.offset_meta = get_le32(buf + kOffMeta); + h.max_stack = get_le32(buf + kOffMaxStack); - // 段校验(数据段之后是 SHA-256 文件尾;这里仅保证有尾,sha_ok() 做完整性校验) - if (len < static_cast(h.offset_data) + kSha256Size) { + // 段校验(V2:值段之后是 SHA-256 文件尾;元数据段 0 = 空段) + if (len < static_cast(h.offset_values) + kSha256Size) { v.err_ = "missing sha256 tail"; return v; } - const uint64_t offs[5] = {h.offset_const, h.offset_funcs, h.offset_code, h.offset_fb, - h.offset_data}; + const uint64_t offs[5] = {h.offset_const, h.offset_funcs, h.offset_code, h.offset_slots, + h.offset_values}; for (int i = 0; i < 5; ++i) { if (offs[i] < kHeaderSize || offs[i] > len - kSha256Size) { v.err_ = "segment offset out of range"; @@ -300,8 +266,22 @@ Image Image::from(const uint8_t* buf, size_t len) { v.err_ = "function table size mismatch"; return v; } - if ((h.offset_fb - h.offset_code) % 4 != 0) { - v.err_ = "code segment not 4-byte aligned"; + if (offs[4] - offs[3] != static_cast(h.n_slots) * kSlotRowSize) { + v.err_ = "slot table size mismatch"; + return v; + } + // 值段大小:meta 段为 0(空段)时值段到 SHA 尾;否则到 meta 起点 + if (h.offset_meta != 0) { + if (static_cast(h.offset_meta) - offs[4] != h.values_size) { + v.err_ = "values segment size mismatch"; + return v; + } + } else if (static_cast(len - kSha256Size) - offs[4] != h.values_size) { + v.err_ = "values segment size mismatch"; + return v; + } + if ((h.offset_slots - h.offset_code) % 4 != 0) { + v.err_ = "code segment not aligned"; return v; } if (h.entry_fn_id >= h.n_funcs && h.n_funcs != 0) { @@ -315,8 +295,6 @@ Image Image::from(const uint8_t* buf, size_t len) { /** * @brief 从 vector 构造只读视图。 - * @param buf 映像缓冲(引用 data(),调用方保证生命周期) - * @return 同 from(const uint8_t*, size_t) */ Image Image::from(const std::vector& buf) { return from(buf.data(), buf.size()); @@ -324,8 +302,6 @@ Image Image::from(const std::vector& buf) { /** * @brief 读函数表一行。 - * @param i 函数下标 - * @return 该行字段;越界(或校验失败)时全 0 */ FuncRow Image::func_row(size_t i) const { FuncRow r; @@ -339,47 +315,58 @@ FuncRow Image::func_row(size_t i) const { } /** - * @brief 读常量表一项。 - * @param i 常量下标 - * @return 该条目;越界(或校验失败)时全 0 + * @brief 读常量表一项(8B 原始值)。 */ ConstEntry Image::const_entry(size_t i) const { ConstEntry e; if (ok_ && i < hdr_.n_consts) { const uint8_t* p = buf_ + hdr_.offset_const + i * kConstEntrySize; - e.tag = get_le32(p + kConstTagOff); e.value = get_le64(p + kConstValueOff); } return e; } +/** + * @brief 读槽表一项的地址(值段内偏移)。 + */ +uint32_t Image::slot_addr(size_t i) const { + if (ok_ && i < hdr_.n_slots) { + const uint8_t* p = buf_ + hdr_.offset_slots + i * kSlotRowSize; + return get_le32(p + kSlotAddrOff); + } + return 0; +} + +/** + * @brief 槽表段起点。 + */ +const uint8_t* Image::slots_bytes() const { + return ok_ ? buf_ + hdr_.offset_slots : nullptr; +} + /** * @brief 字节码段起点。 - * @return 段指针;校验失败返回 nullptr */ const uint8_t* Image::code_bytes() const { return ok_ ? buf_ + hdr_.offset_code : nullptr; } /** - * @brief 数据段起点。 - * @return 段指针;校验失败返回 nullptr + * @brief 值段起点。 */ const uint8_t* Image::data_bytes() const { - return ok_ ? buf_ + hdr_.offset_data : nullptr; + return ok_ ? buf_ + hdr_.offset_values : nullptr; } /** - * @brief 数据段字节数。 - * @return 段长度(不含 SHA-256 尾);校验失败返回 0 + * @brief 值段字节数。 */ size_t Image::data_len() const { - return ok_ ? (len_ - kSha256Size) - hdr_.offset_data : 0; + return ok_ ? (len_ - kSha256Size) - hdr_.offset_values : 0; } /** * @brief 型号标识字符串。 - * @return 头 @72 起 32 字节,去 '\0' 截断;校验失败返回空串 */ std::string Image::model_id() const { if (!ok_) { @@ -395,9 +382,6 @@ std::string Image::model_id() const { /** * @brief 型号标识是否匹配。 - * @param name 期望型号名(如 "STATOR") - * @param version 期望版本(如 1) - * @return 头内 32 字节与 "name + version" 补零后逐字节相等 */ bool Image::model_matches(const std::string& name, uint32_t version) const { char want[kModelIdSize]; @@ -407,7 +391,6 @@ bool Image::model_matches(const std::string& name, uint32_t version) const { /** * @brief 文件尾 SHA-256 完整性校验。 - * @return 对 len-32 字节内容算摘要,与文件尾 32 字节相等 */ bool Image::sha_ok() const { if (!ok_) {