i386架构无main函数静态可执行文件在多系统崩溃问题问询
无main函数的静态i386可执行文件在Debian/Fedora上崩溃的问题
问题现象
测试代码:
#include <unistd.h> void _start(void) { _exit(0); }
使用编译命令 gcc -lc -nostdlib -static -Os 在Debian 12.6 i386系统编译后,运行时直接崩溃。gdb调试显示崩溃点为:
0x804905b <_exit+59> call *%gs:0x10
此时$gs寄存器的值为0。即使移除_exit调用,程序依然会崩溃。
该程序在amd64架构的FreeBSD 14.0(clang编译)、i386架构FreeBSD上均可正常运行,但在Fedora 39 amd64(glibc 2.39、GCC 14.2.1)上也出现相同崩溃问题。
原因分析
崩溃的核心原因是glibc的_exit函数依赖线程本地存储(TLS)的初始化,而-nostdlib参数跳过了crt0启动代码的执行:
- crt0是C运行时的启动代码,负责初始化TLS环境,其中包括设置GS段寄存器指向TLS区域。
- 当使用
-nostdlib时,程序直接从自定义的_start入口执行,GS段寄存器未被初始化(值为0),此时访问%gs:0x10相当于访问虚拟地址0x10,属于无效内存,触发崩溃。 - 即使移除
_exit调用,_start函数执行完毕后没有正确的退出路径(x86架构下程序入口执行完后需要主动调用系统退出指令或返回给内核),同样会导致崩溃。
解决方法
要让程序正常运行,有两种可行方案:
方案1:直接调用系统调用而非glibc的_exit
绕过glibc的封装,直接使用i386的系统调用完成退出操作,不需要依赖glibc的TLS初始化:
void _start(void) { // i386架构下exit系统调用号为1,参数0表示退出码 asm volatile ( "movl $1, %%eax\n" "movl $0, %%ebx\n" "int $0x80\n" ); // 死循环防止编译器生成多余代码(不会执行到) for(;;); }
编译时无需链接glibc,命令改为:
gcc -nostdlib -static -Os test.c -o test
方案2:保留crt0启动代码,不使用-nostdlib
如果需要依赖glibc的函数,不要使用-nostdlib,而是通过-e _start指定入口函数,让crt0完成初始化后再进入自定义的_start:
编译命令改为:
gcc -static -Os -e _start test.c -o test
此时crt0会正常初始化GS段和TLS环境,调用_exit不会触发崩溃。
内容的提问来源于stack exchange,提问作者Paul Floyd
相关产品推荐
相关产品推荐

