Simple c vs 汇编

你贴出的程序是一个典型的 Linux x86-64 NASM 无 libc Hello World 示例。整体逻辑正确,不过有几个更深入的点值得补充:

1. 程序执行流程

这个程序并不是从 main() 开始运行,而是:

Linux Kernel
     |
     v
_start
     |
     +--> syscall write()
     |
     +--> syscall exit()
     |
     v
Kernel terminates process

因为使用:

global _start

并直接链接:

ld hello.o -o hello

所以没有经过 C Runtime(crt)启动代码。

普通 C 程序实际上类似:

_start
  |
  v
__libc_start_main()
  |
  v
main()
  |
  v
exit()

而你的汇编跳过了 libc。


2. len equ $ - msg 的含义

这一行:

len equ $ - msg

是 NASM 的汇编期计算。

假设:

msg db 'Hello, world!', 0xA

占用:

H e l l o , _ w o r l d ! \n
|                         |
msg                       $

当前地址 $ 减去起始地址 msg

len = 当前地址 - msg地址

得到:

13 个字符 + 1 个换行 = 14 bytes

所以等价于:

write(1, "Hello, world!\n", 14);

3. Linux x86-64 syscall ABI

你的寄存器使用符合 Linux System V ABI:

参数寄存器
syscall number rax
第1参数 rdi
第2参数 rsi
第3参数 rdx
第4参数 r10
第5参数 r8
第6参数 r9

因此:

write

C:

write(fd, buffer, count);

汇编:

mov rax,1       ; SYS_write
mov rdi,1       ; stdout
mov rsi,msg
mov rdx,len
syscall

对应:

rax = 1
rdi = 1
rsi = msg地址
rdx = 长度

exit

C:

exit(0);

汇编:

mov rax,60
xor rdi,rdi
syscall

对应:

rax = SYS_exit
rdi = 返回值

4. 可以进一步优化

你的代码已经很小,但还能略微调整:

section .data
    msg db "Hello, world!", 10
    len equ $-msg

section .text
    global _start

_start:

    mov eax,1        ; SYS_write
    mov edi,1        ; stdout
    mov rsi,msg
    mov edx,len
    syscall

    mov eax,60       ; SYS_exit
    xor edi,edi
    syscall

原因:

  • x86-64 中写 eax/edi/edx 会自动清零高 32 位;

  • 指令编码更短;

  • 不需要写完整 rax/rdi/rdx


5. 查看生成的 ELF

可以观察它没有 libc:

ldd hello

输出:

not a dynamic executable

查看入口:

readelf -h hello

类似:

Entry point address: 0x401000

查看 syscall:

strace ./hello

输出类似:

write(1, "Hello, world!\n", 14) = 14
exit(0)
+++ exited with 0 +++

6. 与 C 编译结果比较

如果写:

int main()
{
    write(1,"Hello, world!\n",14);
}

然后:

gcc hello.c -o hello

程序会包含:

  • ELF 文件

  • 动态链接器

  • libc

  • crt 启动代码

而 NASM 版本:

ELF
 |
 +-- .text
 |
 +-- .data
 |
 +-- syscall

没有额外运行时。

这也是学习操作系统、编译器、逆向工程时常用的第一个实验。你给出的版本就是 Linux x86-64 汇编直接调用内核接口的最小经典示例。

posted on 2026-07-12 18:11  小镇-做题家  阅读(3)  评论(0)    收藏  举报

导航