如何解决Solus Linux上使用CosmoNim(GCC)编译可执行文件出现段错误的问题
排查思路
第一步:对比Ubuntu和Solus上的编译参数差异
- 分别在两个系统上执行Nim编译时加上
-v参数,输出完整的C编译、链接全流程命令,对比GCC的参数差异,重点看:- Cosmopolitan要求的栈对齐、内存布局相关参数是否一致
- 有无多余的平台特有的编译/链接参数被Solus的Nim默认加上
- 检查
initStackBottomWith函数相关的宏定义在两个系统生成的C代码中是否有差异
- 对比两个系统下直接用GCC编译Cosmopolitan HelloWorld的参数,和Nim生成的编译参数做比对,把缺失的Cosmo要求的参数手动补到Nim的编译参数里再测试
第二步:GDB深入定位段错误触发点
- 先确认段错误的具体指令:在GDB中触发段错误后执行
disassemble $pc,看是访问哪块内存出的问题,是执行PreMain()的时候挂的,还是给inner赋值、调用initStackBottomWith、执行(*inner)()的时候挂的 - 单步执行NimMain里的代码:在
NimMain入口处打断点,用stepi逐指令执行,每一步执行完检查寄存器值、对应内存地址的内容是否正常:- 执行
PreMain后返回地址是否正常 - 检查
NimMainInner的地址是否合法,有没有被错误重定位 - 检查传入
initStackBottomWith的栈地址是否在合法栈区间内
- 执行
- 检查段错误时的栈布局:执行
bt full输出完整栈回溯和栈上变量值,看有无栈溢出、变量被篡改的情况
第三步:排查工具链版本差异
- 对比两个系统的GCC版本、ld版本、Nim版本,确认是否是Solus下的工具链默认开启了某个和Cosmopolitan不兼容的特性(比如默认开启的栈保护、PIE、强制的RELRO等)
- 可以手动关闭Nim默认开启的安全编译参数再测试:比如编译时加上
--passC:-fno-stack-protector --passC:-fno-pie --passL:-no-pie这类参数,排除安全机制和Cosmo的内存布局冲突的可能
第四步:验证代码生成逻辑
- 把两个系统下Nim生成的C代码分别拿出来,互换到对方系统上编译,如果在Solus上编译Ubuntu生成的C代码也正常,说明是Solus下的Nim生成C代码的逻辑有问题;如果Ubuntu上编译Solus生成的C代码也段错误,就直接对比两份C代码的差异
- 手动把Nim生成的C代码用Cosmopolitan的GCC参数直接编译,跳过Nim的自动编译流程,看是否还会段错误,如果正常就说明是Nim调用GCC的参数不对
内容的提问来源于stack exchange,提问作者GGibson
相关产品推荐
相关产品推荐

