这个 x86 Hello World using 32-bit int 0x80 Linux system calls from _start 的解释是什么?

What is the explanation of this x86 Hello World using 32-bit int 0x80 Linux system calls from _start?

section .text
    global _start       ;must be declared for using gcc
_start:                     ;tell linker entry point
    mov edx, len    ;message length
    mov ecx, msg    ;message to write
    mov ebx, 1      ;file descriptor (stdout)
    mov eax, 4      ;system call number (sys_write)
    int 0x80        ;call kernel
    mov eax, 1      ;system call number (sys_exit)
    int 0x80        ;call kernel

section .data

msg db  'Hello, world!',0xa ;our dear string
len equ $ - msg         ;length of our dear string

这是一个基本的 32 位 x86 Linux 汇编代码,用于在屏幕上打印 "Hello, World!"(标准输出)。用

构建+ 运行
nasm -felf -g -Fdwarf hello.asm
gcc -g -m32 -nostdlib -static -o hello hello.o
./hello

(编者注:或 gdb ./hello 调试/单步执行。这就是我们使用 nasm -g -Fdwarfgcc -g 的原因。或者在 GDB 中使用 layout reg 进行反汇编+注册不依赖于调试符号的视图。请参阅 https://whosebug.com/tags/x86/info)

的底部

现在我想问一下这段代码在幕后是如何工作的。就像所有这些说明需要什么

_start:                     ;tell linker entry point
        mov edx, len    ;message length
        mov ecx, msg    ;message to write
        mov ebx, 1      ;file descriptor (stdout)
        mov eax, 4      ;system call number (sys_write)
        int 0x80        ;call kernel
        mov eax, 1      ;system call number (sys_exit)
        int 0x80        ;call kernel

只是为了打印 "Hello, World!" 和声明

_start:

以上!是主函数吗?

和声明

int 0x80

为什么要使用它?你们能不能给我深入解释一下这个程序的基本工作原理。

在机器代码中,没有函数。至少,处理器对函数一无所知。程序员可以按照自己的喜好构造代码。 _start 是一种叫做 symbol 的东西,它只是程序中某个位置的名称。符号用于指代您还不知道其地址的位置。它们在链接期间被解析。符号 _start 用作 入口点 (参见 ),这是操作系统跳转以启动程序的地方。除非您通过其他方式指定入口点,否则每个程序都必须包含 _start。您的程序使用的其他符号是 msg,它由链接器解析为字符串 Hello, world! 所在的地址,lenmsg.[= 的长度。 40=]

程序的其余部分执行以下操作:

  1. 为系统调用write(1, msg, len)设置寄存器。 write 有存储在 eax 中的系统调用号 4,让操作系统知道你想要系统调用 4。这个系统调用将数据写入文件。提供的文件描述符编号为 1,代表标准输出。
  2. 使用 int [=20=]x80 执行系统调用。该指令会中断您的程序,操作系统会选择它并执行编号存储在 eax 中的函数。这就像调用 OS 内核的函数调用。调用约定与其他函数不同,with args passed in registers.
  3. 为系统调用_exit(?)设置寄存器。它的系统调用号是 1,进入 eax。遗憾的是,代码忘记为 _exit 设置参数,该参数应为 0 以指示成功。相反,使用之前 ebx 中的任何内容,这似乎是 1.
  4. 使用 int [=20=]x80 执行系统调用。因为 _exit 结束程序,它不会 return。您的程序到此结束。

指令db告诉汇编程序将以下数据放入我们当前所在的程序中。这会将字符串 Hello, world! 后跟一个换行符放入程序中,这样我们就可以告诉 write 系统调用写入该字符串。

len equ $ - msg 告诉汇编程序比 len is the difference between $ (where we currently are) and msg。这是定义的,因此我们可以传递给 write 我们要打印的文本的长度。

程序中分号 (;) 后的所有内容都是 注释,被汇编程序忽略。