第一节
Hello,world!
翻译自:https://asmtutor.com/
背景知识
汇编语言是最基本的。程序员在实际硬件之上的唯一接口是内核本身。为了在汇编中构建有用的程序,我们需要使用内核提供的 Linux 系统调用。这些系统调用是内置于操作系统中的库,可提供诸如从键盘读取输入和将输出写入屏幕等功能。
当您调用系统调用时,内核将立即暂停执行您的程序。然后它将执行您请求的任务所需的必要驱动程序,最后再将控制权返回给您的程序。
Note: 驱动程序 (Drivers) 之所以称为“驱动程序”,是因为内核确实是使用它们来“驱动”硬件。
我们可以在汇编中完成这一切,方法是将我们要执行的函数号(即 OPCODE)加载到 EAX
中,并用我们要传递给系统调用的参数填充剩余的寄存器。最后使用 INT
指令请求软件中断,内核将接管并使用我们的参数调用库中的函数。
例如,当 EAX=1
时请求中断将调用 sys_exit
,而当 EAX=4
时请求中断将调用 sys_write
。如果函数需要,EBX
、ECX
和 EDX
将作为参数传递。单击此处查看 Linux 系统调用表及其相应 OPCODES 的示例。
写程序
首先,我们在 .data
部分创建一个变量“msg”
,并为其分配我们想要输出的字符串,在本例中为“Hello, world!”
。在我们的 .text
部分中,我们通过为内核提供全局标签 _start
: 来指示程序入口点来告诉内核从哪里开始执行。
我们使用系统调用 sys_write
将信息输出到控制台窗口。此函数在 Linux 系统调用表中指定为 OPCODE 4
。在请求执行任务的软件中断之前,该函数还接受 3 个参数,这些参数依次加载到 EDX
、ECX
和 EBX
中。
下面这些就是传入的参数:
EDX
字符串的长度ECX
在.data
段创建的字符串变量EBX
想要写入的文件,例如STDOUT
传递的参数的数据类型和含义可以在函数的定义中找到。
使用以下命令编译、链接和运行程序。
; Hello, world!; 文件名:helloworld.asm; 编译:nasm -f elf helloworld.asm; 链接(64位系统需要 elf_i386 选项):ld -m elf_i386 helloworld.o -o helloworld; 运行:./helloworld SECTION .datamsg db 'Hello World!', 0Ah ; 初始化变量msg SECTION .textglobal _start _start: mov edx, 13 ; 待写入的字节数(每个字母一个字节,再加上换行符0Ah) mov ecx, msg ; 将msg的内存地址移入ecx mov ebx, 1 ; 表示写入到标准输出STDOUT mov eax, 4 ; 调用SYS_WRITE(OPCODE是4) int 80h
~$ nasm -f elf helloworld.asm~$ ld -m elf_i386 helloworld.o -o helloworld~$ ./helloworldHello World!Segmentation fault