Zig 二进制布局:启动路径 / 错误联合 / panic 链 / 符号可见性
Zig 产物与 C 产物的最大差异在错误处理(error union 而非异常表)与符号可见性(业务函数 LOCAL)。先确认 Zig 版本再读布局——panic 链命名与错误联合细节随版本演进。
启动路径(ELF,x86-64 实测 0.17.0-dev)
_start 是唯一默认导出的业务无关 GLOBAL 函数;export fn 才产生其他导出
- 反汇编入口链:
_start 短桩(16 字节级)→ std.start 初始化函数(大,含安全特性安装)
符号可见性(与 C 的重要差异)
| 构建模式 |
业务函数符号 |
说明 |
| Debug / ReleaseSafe |
LOCAL(符号表保留) |
nm 可见但非导出;_start 为 GLOBAL |
| ReleaseFast / ReleaseSmall |
可被 strip |
按行为特征恢复 |
export fn |
GLOBAL |
显式导出(C ABI 边界,宿主调用点) |
- 判别命令:
readelf -s sample | grep FUNC | awk '$7=="GLOBAL"' 数全局函数
- hook/注入面按导出表(GLOBAL + 动态符号)算,LOCAL 符号只是分析线索
错误联合(error union)布局
E!T(错误联合)编译为"错误码 + 载荷":错误码恒为 u16(0 = 无错),布局按载荷大小分两种。
载荷 ≤ 8 字节:整体一个槽位,错误码在载荷之后(高位侧)
| 载荷类型 |
槽位大小(实测 0.17) |
错误码位置 |
| u8 / bool |
4 字节 |
载荷之后(u8 在 offset 1) |
| u32 |
8 字节 |
offset +4(u16) |
| u64 |
8 字节 |
高位 u16 |
载荷 > 8 字节:错误码在前 2 字节,载荷按自身对齐内联
| 载荷类型 |
槽位大小(实测 0.17) |
| u128(对齐 16) |
32 字节 |
| struct { u64, u64 }(对齐 8) |
24 字节 |
- 反汇编识别:调用点后
cmpw $0, 槽位+offset + 分支 = catch/orelse 的错误码检查;错误返回路径 call lang.returnError(0.17 命名)后装入错误表地址
@errorName(e) 在字符串池留错误名(__zig_tag_name_* 符号/字符串),可还原错误语义
panic 链
| 版本 |
链形态 |
| 0.14+(实测 0.17.0-dev) |
@panic/断言 → debug.panicExtra/debug.FullPanic.defaultPanic → debug.panicking 状态 + 打印 + abort;溢出检查 debug.FullPanic(...).integerOverflow |
| 更早版本 |
std.debug.panic(直接命名) |
- 定位方式不依赖符号名:找"打印 + abort"序列(
__zig_probe_stack 附近的栈检查调用也常见)
debug.panicking 是 TLS 状态(panic 期间再 panic 短路)
C ABI 边界
- C ABI 只在
extern / export 或显式 callconv(.c) 的边界上假定——编译器定义:c 是本目标 C 调用的别名,extern/export 函数默认获得它;普通 Zig fn 用 Zig 默认约定(既非 export 也非 inline 时使用,不对寄存器/栈对齐作保证),逆向内部函数时不能套 C ABI 的参数与返回值规则
.c 在各 target 上有各自的映射(x86-64 Linux 为 SysV AMD64、Windows 为 x64 约定、AArch64 为 AAPCS64)——这是边界上的映射,不是 Zig 内部约定本身
- 导入:
@extern/@cImport → 动态符号表(readelf -d NEEDED + UND 符号)
- 导出:
export fn → GLOBAL(导出表)
- 混合产物判别:Zig 侧无 RTTI/异常表符号(
_ZTV*/__gxx_personality_v0 缺失),C++ 侧有
版本差异要点
- panic 链命名:0.14 前后差异大(
debug.panicExtra vs std.debug.panic)
- 错误联合布局:u16 错误码不变;槽位尺寸/对齐随版本微调(以实测为准,examples 有 0.17 实测)
main 符号形态稳定(LOCAL 普通函数);_start 稳定(GLOBAL)
- ReleaseFast 可能去除 panic 链(
-Drelease-fast 下安全检查关闭)
使用注意