Files
Interpreter/vm/src/Image.cpp
T
Admin 7aa2bbefcf stb 头字段偏移常量表:消除 vm/compiler 两侧裸魔法数
- vm/Image.cpp:匿名空间 constexpr 偏移常量(kOffMagic..kOffModelId + 段内行偏移),
  from/model_id/model_matches/func_row/const_entry 全部替换
- compiler/Stb.h:同值 inline constexpr 表(Codegen 写侧 + Stb 读侧共用)
- compiler/Codegen.cpp assemble_image:put_* 头字段/行内偏移替换
- compiler/Stb.cpp StbView:读侧偏移替换
- 两侧 static_assert:头大小 = 72 + 型号标识、字段偏移单调、行内布局自洽
2026-08-24 08:55:57 +08:00

423 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file Image.cpp
* @brief vm 自带的 .stb 只读视图实现(执行器侧;compiler 各有实现)
* @author
* @date 2026-08-21
*/
#include "vm/Image.h"
#include <cstring>
#include <string>
namespace vm {
namespace {
/// 映像魔数:"STSC" 小端。
const uint32_t kMagic = 0x43545353u;
/// 映像格式版本。
const uint32_t kVersion = 1;
/// 映像头字节数(72 原字段 + 型号标识[32] @7212.13 修订)。
const size_t kHeaderSize = 104;
/// 常量表一行字节数(tag:4 + value:8)。
const size_t kConstEntrySize = 12;
/// 函数表一行字节数(nregs/code_offset/code_len 各 4)。
const size_t kFuncRowSize = 12;
/// SHA-256 摘要长度(文件尾)。
const size_t kSha256Size = 32;
/// 型号标识长度(头内 @72,不足补 '\0')。
const size_t kModelIdSize = 32;
// ---- 头字段偏移(与 Doc/isa/指令与映像.md 字段表一致;compiler 侧另有同值一份)----
constexpr size_t kOffMagic = 0; ///< 魔数 "STSC"
constexpr size_t kOffVersion = 4; ///< 格式版本
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 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 kConstTagOff = 0; ///< 常量表行内:tag
constexpr size_t kConstValueOff = 4; ///< 常量表行内:value8 字节)
constexpr size_t kFuncNregsOff = 0; ///< 函数表行内:nregs
constexpr size_t kFuncCodeOff = 4; ///< 函数表行内:code_offset(字节)
constexpr size_t kFuncLenOff = 8; ///< 函数表行内:code_len(指令条数)
// 防漂移:头布局自洽性(与 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<uint32_t>(p[0])
| (static_cast<uint32_t>(p[1]) << 8)
| (static_cast<uint32_t>(p[2]) << 16)
| (static_cast<uint32_t>(p[3]) << 24);
}
/// 小端读 64 位。
uint64_t get_le64(const uint8_t* p) {
uint64_t v = 0;
for (int i = 0; i < 8; ++i) {
v |= static_cast<uint64_t>(p[i]) << (8 * i);
}
return v;
}
// ---- 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,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
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}; ///< 初始哈希值
uint64_t total = 0; ///< 已吸收字节数(final 时编码进长度域)
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) {
const size_t n = (block_len < 64) ? (64 - block_len) : 0;
const size_t take = len < n ? len : n;
if (take == 0) {
break;
}
for (size_t i = 0; i < take; ++i) {
block[block_len + i] = data[i];
}
block_len += take;
data += take;
len -= take;
if (block_len == 64) {
process();
block_len = 0;
}
}
}
/// 压缩一个满块(64 字节,w[0..63] 展开 + 64 轮)。
void process() {
uint32_t w[64];
for (int i = 0; i < 16; ++i) {
w[i] = get_be32(block + i * 4);
}
for (int i = 16; i < 64; ++i) {
const uint32_t s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >> 3);
const uint32_t s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >> 10);
w[i] = w[i - 16] + s0 + w[i - 7] + s1;
}
uint32_t a = h[0], b = h[1], c = h[2], d = h[3];
uint32_t e = h[4], f = h[5], g = h[6], hh = h[7];
for (int i = 0; i < 64; ++i) {
const uint32_t s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
const uint32_t ch = (e & f) ^ (~e & g);
const uint32_t t1 = hh + s1 + ch + kShaK[i] + w[i];
const uint32_t s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
const uint32_t maj = (a & b) ^ (a & c) ^ (b & c);
const uint32_t t2 = s0 + maj;
hh = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
h[0] += a; h[1] += b; h[2] += c; h[3] += d;
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;
update(&pad, 1);
const uint8_t zeros[64] = {0};
while (block_len != 56) {
const size_t n = (block_len < 56) ? (56 - block_len) : (64 - block_len);
update(zeros, n);
}
for (int i = 0; i < 8; ++i) {
const uint8_t b2[1] = {static_cast<uint8_t>((bitlen >> (56 - 8 * i)) & 0xFF)};
update(b2, 1);
}
for (int i = 0; i < 8; ++i) {
out[i * 4 + 0] = static_cast<uint8_t>((h[i] >> 24) & 0xFF);
out[i * 4 + 1] = static_cast<uint8_t>((h[i] >> 16) & 0xFF);
out[i * 4 + 2] = static_cast<uint8_t>((h[i] >> 8) & 0xFF);
out[i * 4 + 3] = static_cast<uint8_t>(h[i] & 0xFF);
}
}
/// 大端读 32 位。
static uint32_t get_be32(const uint8_t* p) {
return (static_cast<uint32_t>(p[0]) << 24) | (static_cast<uint32_t>(p[1]) << 16) |
(static_cast<uint32_t>(p[2]) << 8) | static_cast<uint32_t>(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) {
out[i] = i < id.size() ? id[i] : '\0';
}
}
} // namespace
/**
* @brief 从原始字节构造只读视图(不拷贝,调用方保证生命周期)。
* @param buf 映像缓冲;可为 nullptr
* @param len 缓冲字节数
* @return 校验结果;ok() 判成功,error() 取失败原因
*/
Image Image::from(const uint8_t* buf, size_t len) {
Image v;
v.buf_ = buf;
v.len_ = len;
if (buf == nullptr) {
v.err_ = "null buffer";
return v;
}
if (len < kHeaderSize) {
v.err_ = "image too short";
return v;
}
if (get_le32(buf + kOffMagic) != kMagic) {
v.err_ = "bad magic";
return v;
}
if (get_le32(buf + kOffVersion) != kVersion) {
v.err_ = "bad version";
return v;
}
ImageHeader& h = v.hdr_;
h.cycle_limit = get_le32(buf + kOffCycleLimit);
h.dt_ms = get_le32(buf + kOffDtMs);
h.project_hash = get_le64(buf + kOffProjectHash);
h.entry_fn_id = get_le32(buf + kOffEntryFnId);
h.n_globals = get_le32(buf + kOffNGlobals);
h.n_i = get_le32(buf + kOffNI);
h.n_q = get_le32(buf + kOffNQ);
h.n_m = get_le32(buf + kOffNM);
h.n_consts = get_le32(buf + kOffNConsts);
h.n_funcs = get_le32(buf + kOffNFuncs);
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);
// 段校验(数据段之后是 SHA-256 文件尾;这里仅保证有尾,sha_ok() 做完整性校验)
if (len < static_cast<size_t>(h.offset_data) + 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};
for (int i = 0; i < 5; ++i) {
if (offs[i] < kHeaderSize || offs[i] > len - kSha256Size) {
v.err_ = "segment offset out of range";
return v;
}
if (i > 0 && offs[i] < offs[i - 1]) {
v.err_ = "segment offsets not monotonic";
return v;
}
}
if (offs[1] - offs[0] != static_cast<uint64_t>(h.n_consts) * kConstEntrySize) {
v.err_ = "const table size mismatch";
return v;
}
if (offs[2] - offs[1] != static_cast<uint64_t>(h.n_funcs) * kFuncRowSize) {
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";
return v;
}
if (h.entry_fn_id >= h.n_funcs && h.n_funcs != 0) {
v.err_ = "entry fn_id out of range";
return v;
}
v.ok_ = true;
v.err_.clear();
return v;
}
/**
* @brief 从 vector 构造只读视图。
* @param buf 映像缓冲(引用 data(),调用方保证生命周期)
* @return 同 from(const uint8_t*, size_t)
*/
Image Image::from(const std::vector<uint8_t>& buf) {
return from(buf.data(), buf.size());
}
/**
* @brief 读函数表一行。
* @param i 函数下标
* @return 该行字段;越界(或校验失败)时全 0
*/
FuncRow Image::func_row(size_t i) const {
FuncRow r;
if (ok_ && i < hdr_.n_funcs) {
const uint8_t* p = buf_ + hdr_.offset_funcs + i * kFuncRowSize;
r.nregs = get_le32(p + kFuncNregsOff);
r.code_offset = get_le32(p + kFuncCodeOff);
r.code_len = get_le32(p + kFuncLenOff);
}
return r;
}
/**
* @brief 读常量表一项。
* @param i 常量下标
* @return 该条目;越界(或校验失败)时全 0
*/
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 字节码段起点。
* @return 段指针;校验失败返回 nullptr
*/
const uint8_t* Image::code_bytes() const {
return ok_ ? buf_ + hdr_.offset_code : nullptr;
}
/**
* @brief 数据段起点。
* @return 段指针;校验失败返回 nullptr
*/
const uint8_t* Image::data_bytes() const {
return ok_ ? buf_ + hdr_.offset_data : nullptr;
}
/**
* @brief 数据段字节数。
* @return 段长度(不含 SHA-256 尾);校验失败返回 0
*/
size_t Image::data_len() const {
return ok_ ? (len_ - kSha256Size) - hdr_.offset_data : 0;
}
/**
* @brief 型号标识字符串。
* @return 头 @72 起 32 字节,去 '\0' 截断;校验失败返回空串
*/
std::string Image::model_id() const {
if (!ok_) {
return "";
}
std::string s(reinterpret_cast<const char*>(buf_ + kOffModelId), kModelIdSize);
const size_t z = s.find('\0');
if (z != std::string::npos) {
s.resize(z);
}
return s;
}
/**
* @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];
fill_model_id(name, version, want);
return std::memcmp(buf_ + kOffModelId, want, kModelIdSize) == 0;
}
/**
* @brief 文件尾 SHA-256 完整性校验。
* @return 对 len-32 字节内容算摘要,与文件尾 32 字节相等
*/
bool Image::sha_ok() const {
if (!ok_) {
return false;
}
const size_t content_len = len_ - kSha256Size;
uint8_t digest[32];
sha256(buf_, content_len, digest);
return std::memcmp(buf_ + content_len, digest, 32) == 0;
}
} // namespace vm