Files
Interpreter/compiler/src/Stb.cpp
T
Admin a308ae713d 阶段 B 步骤 6:.stb 写侧新格式(头 104 + 型号标识 + SHA-256 文件尾)。
- Stb:kHeaderSize 104、kSha256Size 32;SHA-256 实现(FIPS 180-4,含标准测试向量验证);
  fill_model_id(name+version 拼 32 字节补 '\0');StbView 解析新格式 +
  model_id()/model_matches()/sha_ok()(数据段不含尾、缺尾报错)
- Codegen:assemble_image 写型号标识(偏移 72)+ 文件尾 SHA-256
- main.cpp:--disasm 显示 model/sha;写后自检(ok+sha+型号匹配,失败报错)
- 测试:machine_test +SHA-256 已知向量(空串/abc)与型号标识断言(53 断言);
  codegen_test data_len 断言 +32(SHA 尾);ctest 12/12
- 验证:line1 288 字节 model=STATOR1 sha=ok;篡改一字节 sha=BAD
2026-08-21 22:20:35 +08:00

379 lines
12 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 Stb.cpp
* @brief 编译器自带的 .stb 映像规范(写侧)+ 只读视图 + FNV-1a + sidecar
* @author
* @date 2026-08-21
*/
#include "compiler/Stb.h"
#include <cstdio>
#include <cstring>
#include <fstream>
#include "compiler/Project.h"
namespace compiler {
uint64_t fnv1a64_update(uint64_t h, const uint8_t* data, size_t len) {
for (size_t i = 0; i < len; ++i) {
h ^= data[i];
h *= kFnvPrime;
}
return h;
}
uint64_t fnv1a64(const uint8_t* data, size_t len) {
return fnv1a64_update(kFnvBasis, data, len);
}
namespace {
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);
}
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-256FIPS 180-4----
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)); }
struct Sha256 {
uint32_t h[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
uint64_t total = 0;
uint8_t block[64];
size_t block_len = 0;
void update(const uint8_t* data, size_t len) {
total += len;
while (len > 0) {
const size_t take = (block_len < 64) ? (64 - block_len) : 0;
const size_t n = len < take ? len : take;
if (n > 0) {
for (size_t i = 0; i < n; ++i) {
block[block_len + i] = data[i];
}
block_len += n;
data += n;
len -= n;
if (block_len == 64) {
process();
block_len = 0;
}
} else {
break;
}
}
}
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;
}
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};
// 补零到 block_len == 56(跨块时先补满当前块再补)
while (block_len != 56) {
const size_t n = (block_len < 56) ? (56 - block_len) : (64 - block_len);
update(zeros, n);
}
// 64 位大端比特长度
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);
}
}
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]);
}
};
} // namespace
void sha256(const uint8_t* data, size_t len, uint8_t out[kSha256Size]) {
Sha256 s;
s.update(data, len);
s.final(out);
}
void fill_model_id(const std::string& name, uint32_t version, char out[kModelIdSize]) {
const std::string id = name + std::to_string(version);
for (size_t i = 0; i < kModelIdSize; ++i) {
out[i] = i < id.size() ? id[i] : '\0';
}
}
StbView StbView::from(const uint8_t* buf, size_t len) {
StbView 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 + 0) != kMagic) {
v.err_ = "bad magic";
return v;
}
if (get_le32(buf + 4) != kVersion) {
v.err_ = "bad version";
return v;
}
v.cycle_limit_ = get_le32(buf + 8);
v.dt_ms_ = get_le32(buf + 12);
v.project_hash_ = get_le64(buf + 16);
v.entry_fn_id_ = get_le32(buf + 24);
v.n_globals_ = get_le32(buf + 28);
v.n_consts_ = get_le32(buf + 44);
v.n_funcs_ = get_le32(buf + 48);
v.offset_code_ = get_le32(buf + 60);
v.offset_fb_ = get_le32(buf + 64);
v.offset_data_ = get_le32(buf + 68);
// 段校验(12.13:文件尾 SHA-256[32] 在数据段之后)
if (len < static_cast<size_t>(v.offset_data_) + kSha256Size) {
v.err_ = "missing sha256 tail";
return v;
}
const uint64_t offs[5] = {get_le32(buf + 52), get_le32(buf + 56), v.offset_code_,
v.offset_fb_, v.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>(v.n_consts_) * kConstEntrySize) {
v.err_ = "const table size mismatch";
return v;
}
if (offs[2] - offs[1] != static_cast<uint64_t>(v.n_funcs_) * kFuncRowSize) {
v.err_ = "function table size mismatch";
return v;
}
if ((v.offset_fb_ - v.offset_code_) % 4 != 0) {
v.err_ = "code segment not 4-byte aligned";
return v;
}
if (v.entry_fn_id_ >= v.n_funcs_ && v.n_funcs_ != 0) {
v.err_ = "entry fn_id out of range";
return v;
}
v.ok_ = true;
v.err_.clear();
return v;
}
StbView StbView::from(const std::vector<uint8_t>& buf) {
return from(buf.data(), buf.size());
}
ConstEntry StbView::const_entry(size_t i) const {
ConstEntry e;
if (ok_ && i < n_consts_) {
const uint8_t* p = buf_ + offs_of_const() + i * kConstEntrySize;
e.tag = get_le32(p);
e.value = get_le64(p + 4);
}
return e;
}
uint32_t StbView::offs_of_const() const {
// offset_const = offs[0],由 from() 已校验的段起点
return static_cast<uint32_t>(get_le32(buf_ + 52));
}
StbView::FuncRow StbView::func_row(size_t i) const {
FuncRow r;
if (ok_ && i < n_funcs_) {
const uint8_t* p = buf_ + get_le32(buf_ + 56) + i * kFuncRowSize;
r.nregs = get_le32(p + 0);
r.code_offset = get_le32(p + 4);
r.code_len = get_le32(p + 8);
}
return r;
}
const uint8_t* StbView::code_bytes() const {
return ok_ ? buf_ + offset_code_ : nullptr;
}
size_t StbView::code_len() const {
return ok_ ? offset_fb_ - offset_code_ : 0;
}
const uint8_t* StbView::data_bytes() const {
return ok_ ? buf_ + offset_data_ : nullptr;
}
size_t StbView::data_len() const {
return ok_ ? (len_ - kSha256Size) - offset_data_ : 0;
}
std::string StbView::model_id() const {
if (!ok_) {
return "";
}
std::string s(reinterpret_cast<const char*>(buf_ + 72), kModelIdSize);
const size_t z = s.find('\0');
if (z != std::string::npos) {
s.resize(z);
}
return s;
}
bool StbView::model_matches(const std::string& name, uint32_t version) const {
char want[kModelIdSize];
fill_model_id(name, version, want);
return std::memcmp(buf_ + 72, want, kModelIdSize) == 0;
}
bool StbView::sha_ok() const {
if (!ok_) {
return false;
}
const size_t content_len = len_ - kSha256Size;
uint8_t digest[kSha256Size];
sha256(buf_, content_len, digest);
return std::memcmp(buf_ + content_len, digest, kSha256Size) == 0;
}
bool read_stb_file(const char* path, std::vector<uint8_t>* out, std::string* err) {
std::ifstream in(path, std::ios::binary);
if (!in) {
if (err) {
*err = "cannot open for read: " + std::string(path);
}
return false;
}
out->assign(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>());
return true;
}
bool write_stb_file(const char* path, const std::vector<uint8_t>& img, std::string* err) {
std::FILE* f = std::fopen(path, "wb");
if (f == nullptr) {
if (err) {
*err = "cannot open for write: " + std::string(path);
}
return false;
}
const bool ok = img.empty() || std::fwrite(&img[0], 1, img.size(), f) == img.size();
std::fclose(f);
if (!ok && err) {
*err = "write failed: " + std::string(path);
}
return ok;
}
std::string make_sidecar(const std::vector<IoBinding>& bindings) {
std::string out;
for (const IoBinding& b : bindings) {
char buf[64];
out += b.is_input ? "[[io.input]]\n" : "[[io.output]]\n";
out += "var = \"";
out += b.var;
out += "\"\n";
std::snprintf(buf, sizeof buf, "slot = %u\n", static_cast<unsigned>(b.slot));
out += buf;
std::snprintf(buf, sizeof buf, "channel = %u\n", static_cast<unsigned>(b.channel));
out += buf;
std::snprintf(buf, sizeof buf, "bit = %u\n", static_cast<unsigned>(b.bit));
out += buf;
out += "\n";
}
return out;
}
bool write_sidecar_file(const char* path, const std::vector<IoBinding>& bindings,
std::string* err) {
std::FILE* f = std::fopen(path, "wb");
if (f == nullptr) {
if (err) {
*err = "cannot open for write: " + std::string(path);
}
return false;
}
const std::string s = make_sidecar(bindings);
const bool ok = s.empty() || std::fwrite(s.data(), 1, s.size(), f) == s.size();
std::fclose(f);
if (!ok && err) {
*err = "write failed: " + std::string(path);
}
return ok;
}
} // namespace compiler