You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何编译需用-static选项?汇编与C代码链接遇段错误原因

问题描述

我编写了如下汇编代码:

global   main 
        extern   println
    
        section  .text
    
main:
        mov rdi, message
        call     println
        ret

message:
        db "Hello, world!", 0

同时搭配如下C代码进行链接:

#include <stdio.h>

void println(char *line) {
    puts(line);
}

执行以下命令编译并链接时程序能正常运行:

nasm -f elf64 helloWorld.asm -o helloWorld.asm.o
gcc -c helloWorld.c -o helloWorld.c.o
gcc -static helloWorld.c.o helloWorld.asm.o -o helloWorld

运行./helloWorld输出正常,但如果移除-static选项,程序就会出现段错误,这是为什么?


原因分析

核心问题是违反了x86-64 System V调用约定的栈对齐要求:

x86-64的System V ABI明确规定,在执行call指令调用函数前,栈指针rsp必须是16字节对齐的。

当C运行时(crt0)调用你的main函数时,call main指令会将8字节的返回地址压入栈中,导致进入main时,rsp的值是16字节对齐地址减去8——也就是此时栈处于8字节偏移的未对齐状态。你的汇编代码直接在这个状态下调用println,违反了栈对齐要求。

  • 静态链接时,libc的实现对栈对齐的检查较宽松,或者内部未使用依赖16字节对齐的指令(比如SSE指令),所以程序能侥幸运行。
  • 动态链接时,glibc的puts函数内部使用了需要严格16字节栈对齐的指令,未对齐的栈会直接触发段错误。

修复方法

只需要在main函数中调整栈到16字节对齐状态,再调用函数即可。常见的两种方式:

方式1:建立标准栈帧

global   main 
        extern   println
    
        section  .text
    
main:
        push rbp        ; 压入rbp,rsp减8,此时rsp变为16字节对齐
        mov rbp, rsp    ; 建立规范栈帧(可选)
        mov rdi, message
        call     println
        pop rbp         ; 恢复rbp
        ret

message:
        db "Hello, world!", 0

方式2:直接调整栈指针

global   main 
        extern   println
    
        section  .text
    
main:
        sub rsp, 8      ; rsp减8,补全16字节对齐
        mov rdi, message
        call     println
        add rsp, 8      ; 恢复栈指针
        ret

message:
        db "Hello, world!", 0

修改后,无论是否使用-static选项,程序都能正常运行。


内容的提问来源于stack exchange,提问作者doliphin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 07:15:33