4.5 KiB
4.5 KiB
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 混合侧)配合使用