Files
reverse-skills-max/vendor-new/rev-skills/.claude/skills/re-nim/references/layout.md

4.5 KiB
Raw Blame History

Nim 二进制布局:入口链 / 字符串结构 / GC 与异常实现

Nim 产物是 C 代码生成 + 运行时库链接:入口链、字符串布局、GC 与异常实现都随编译器版本与 GC 模式变化。先确认"版本 + GC 模式"再选布局是分析前提。

入口链(ELF,Linux 默认 C 后端)

C main
  └─ NimMain          (运行时初始化:GC 启动、堆栈底部设置)
       └─ NimMainInner
            └─ NimMainModule   (各模块初始化:全局变量构造、模块级代码)
                 └─ 业务 main(用户定义的 main 过程)
  • 符号形态(实测 nim 2.2.10):NimMain/NimMainInner/NimMainModule 均为导出函数;main 是 C 入口
  • 模块级函数名带模块前缀与哈希后缀:main__hello_u8、check__hello_u5(<proc>__<module>_<id> 形态)
  • 字符串等运行时类型以 C struct 形态存在于调试类型:NimStringV2/NimStrPayload(stripped 后消失,靠布局表恢复)

字符串结构(按 GC 模式分叉)

orc/arc(2.x 默认 orc;实测 2.2.10 生成的 C 结构)

struct NimStringV2 {          /* 8/16 字节(64 位):len + 指针 */
    NI len;                   /* 字节长度(不含结尾 \0) */
    NimStrPayload* p;         /* 堆上 payload;len==0 时可 nil(字面量 p 指向静态区) */
};
struct NimStrPayload {
    NI cap;                   /* 容量;cap 高位 bit(62) 为 strlitFlag(字面量标记) */
    NIM_CHAR data[];          /* 内联字符数组 */
};
  • strlitFlag = 1 shl (sizeof(int)*8 - 2)(64 位下 bit 62):cap 带此位 = 字符串字面量(静态区,不可变)
  • 分配函数:rawNewString(1.x 与 2.x 均为此 importc 名,实测 1.6 与 2.2 一致)
  • 内存图:NimStringV2 可内联在栈/对象里,payload 独立堆分配(含 cap 前缀)

refc(1.x 默认与 2.x --gc:refc;NimStringDesc)

struct NimStringDesc {        /* len/reserved + 内联字符 */
    NI len;
    NI reserved;              /* 已分配容量(高位带 seqShallowFlag/strlitFlag 标记) */
    NIM_CHAR data[];          /* 内联 */
};
  • 字符串本体即结构体,无独立 payload;NimString = ptr NimStringDesc

判别表(先判 GC 模式再选布局)

判别线索 orc/arc(v2 字符串) refc(NimStringDesc)
编译默认 Nim 2.x 默认 Nim 1.x 默认 / 2.x 显式 --gc:refc
GC 符号(debug) nimIncRefCyclic nimDecRefIsLastCyclicDyn nimGC_setStackBottom nimGCunref nimGCvisit
字符串分配函数 rawNewString(2.x) rawNewString(1.x 经 newStringOfCap importc,同名)
字符串比较 eqStrings(两者都有,debug 可见) 同左

GC 与所有权

  • refc:引用计数 + 周期收集(nimGC_* 家族);显式 inc/dec 调用点多
  • orc/arc:ARC 语义 + 周期收集(nimIncRefCyclic/nimDecRefIsLastCyclicDyn);retain/release 由编译器在赋值/作用域边界插入,无显式计数器
  • release 构建(-d:release)下 GC 辅助函数常内联消除——符号表在 debug 构建完整,release 靠行为特征

异常实现(版本差异大)

版本/开关 机制 可见符号
Nim 2.2.x 默认(Linux x86-64,orc) goto 式异常表(--exceptions:goto) 无 setjmp;raiseExceptionEx/raiseExceptionAux
老默认(--exceptions:setjmp,Nim 2.0 及以前) setjmp/longjmp nimSetjmp
--exceptions:cpp C++ 异常(C++ 后端) __cxa_throw 等
  • 实测(2.2.10 orc):二进制含 .eh_frame 但无 .gcc_except_table、无 __gxx_personality_v0、无 setjmp——"有异常处理但看不见 setjmp"是 2.2+ 的常态
  • 定位 catch:从 raiseExceptionEx 调用点回看异常表/分支结构;异常对象含类型字段(Nim 异常是引用对象,首字段类型指针)

版本演进要点

  • 1.x:默认 refc + NimStringDesc + setjmp 异常
  • 2.0.x:orc 成为默认 GC,strs_v2(v2 字符串)随 orc 启用;--gc:refc 仍是老布局
  • 2.2.x:Linux amd64 默认 goto 式异常(无 setjmp 符号);rawNewString 取代 newString1 命名
  • 跨版本复用的方法:入口链定位、字符串比较点追踪、异常路径还原;不复用的:具体符号名、结构偏移

使用注意

  • 静态分析无需沙箱;Nim 产物跨平台(Linux ELF / Windows PE / macOS Mach-O),符号形态一致(C 后端)
  • 与 re-analyze/analysis-contract(符号表传递)、re-cpp-abi(C 混合侧)配合使用