实操-先动动手
从NASM官网找并下载一个汇编器,我这里选择的是2.16.03版本,源码安装的方式。可以选择其他安装包的方式,源码安装的好处就是确保你的机器有一个编译环境。linux的编译环境就很简单了,缺啥就sudo apt install xx 啥就行了。源码下载如下图所示:

下载完成之后用tar解压
tar -xvf nasm-2.16.03.tar.gz
NASM官网下载 解压好后在解压好的文件夹下(cd nasm-2.16.03),运行源码安装三连命令:
./configure
make
make install
安装的过程中有啥直接无脑回车就行(你是大佬当我没说),等待安装完成后运行:
nasm -v
运行指令有版本输出就算安装完成了。 输出信息如下图所示:
安装好后就可以编写第一个汇编程序了,这里推荐使用VSCode,在linux环境下创建和编辑文本就很方便。 安装的话直接去官网下载一个安装就行了,这里强调的是,本示例不需要安装任何插件。
创建一个hello.asm文件,代码内容如下:
; 入门hello,world程序,
; 运行一下命令编译和链接程序(linux)
; 32位程序的编译指令
; nasm -f elf32 hello.asm
; ld -m elf_i386 hello.o -o hello
; 64位的编译指令
; nasm -f elf64 hello.asm
; ld hello.o -o hello
[section .data] ; // 数据段
strHello db "Hello,haha!,this is first asm program!!", 0Ah
STRLEN equ $ - strHello
[section .text] ; // 代码段
global _start ; // 标识入口,让连接器识别
_start:
mov edx, STRLEN ; 设置通用寄存器edx的值, 传递第三个参数
mov ecx, strHello ; 设置通用寄存器ecx的值, 传递第二个参数
mov ebx, 1 ; 设置通用寄存器ebx的值, 传递第一个参数
mov eax, 4 ; 设置通用寄存器eax的值, 4-write
int 0x80 ; 系统调用-unix内核功能,会使用前面设置寄存器的值
mov ebx, 0 ; 置通用寄存器ebx的值
mov eax, 1 ; 设置通用寄存器eax的值,1-exit
int 0x80 ; 系统调用-unix内核功能,会使用前面设置寄存器的值
为了方便,我已经把编译指令都放到代码文件里了,编译+链接指令:
nasm -f elf64 hello.asm
ld hello.o -o hello
编译完成后就可以运行这个汇编程序了。这里给出我的实例效果:

思考问答-逐渐深入
1.什么是汇编?
其实我自己理解的很简单啊,汇编就是最接近机器理解的编程语言;这句化应该对于初学者应该没什么感触哈,但是如果你有学过逻辑电路,应该会像我一样被先辈智慧折服,逻辑电路可以很轻松的完成‘加减乘除与或非’这些操作,可以把一些需要操作的数字存放在内存中,通过时钟的推动,让核心的逻辑电路自动有序的完成‘加减乘除与或非’,可执行程序就是放在内存中的二进制数,而汇编是可以直接解释这些二进制数;汇编的每一条指令都是这个逻辑电路(所谓的CPU)能够轻松完成的。即使理解这些,也是毛也没用,无法做出CPU,那理解这些知识有什么用呢?与我而言,最简单直接好处就是,我不会怀疑那是神迹,那就是人类先辈们的智慧。。。。有点抽象,开玩笑的啊,哈哈哈!!!
2.这段代码怎么就能输出hello了?
这里的汇编这么多语句,其实就等于调用一个函数,仔细观察这几个语句,除了mov就是int,其中mov语句很简单,就是给寄存器赋值;int其实也简单,就是请求中断,0x80就是系统软中断,其实就是调用系统函数;
int 0x80 调用系统函数,其中要求eax中的数值表明要调用哪个函数,后面的ebx,ecx,edx都是存放函数参数的寄存器。eax存放4时就是调用unix的写函数,我这里给出一个常用的调用号就很容易明白了。int 0x80之前,先设置哪个寄存器是没有关系的。
// int 0x80是软中断,请求unix内核功能, 通过设置eax寄存器的值来选择内核功能,
// 其他寄存器的作用是向内核功能传递参数
常见的调用号对应的功能(eax, ebx, ecx, edx, ...)
1-sys_exit 退出代码
3-sys_read 文件描述符,缓冲区地址,读取字节数
4-sys_write 文件描述符,消息地址,消息长度
5-sys_open 文件地址,打开模式,文件权限
6-sys_close 文件描述符
7-sys_waitpid 子进程PID,状态地址,选项
11-sys_execve 命令地址,参数地址,环境变量地址
更多信息可以去看unix内核开源网站或查看其源码。 参考网址
3.汇编就是二进制数的解释,那为何还要调用系统接口,直接汇编让机器输出hello,不行吗?
我们先来确定一下,汇编和二进制的对应关系,使用objdump工具查看文件内容
objdump -d -M intel hello
objdump工具ubuntu系统自带,不用特别安装,运行以上指令后就会看到文件对应的内容.(我本人比较习惯看intel的汇编语法,所以指定了汇编语法格式)
401000: b8 04 00 00 00 mov eax,0x4
401005: bb 01 00 00 00 mov ebx,0x1
40100a: b9 00 20 40 00 mov ecx,0x402000
40100f: ba 28 00 00 00 mov edx,0x28
401014: cd 80 int 0x80
401016: b8 01 00 00 00 mov eax,0x1
40101b: bb 00 00 00 00 mov ebx,0x0
401020: cd 80 int 0x80
光执行这些二进制当然不能直接在显示器上显示你准备的那些字符串,触发0x80后还会执行很多二进制文件,包括确定显示器在哪,显示的位置,字体大小,颜色等等,更接近二进制的逻辑应该还要算一下每个像素;总之中间还有很多很多需要CPU处理的指令,如果读者实在有兴趣可以去玩玩嵌入式,没有操作系统的裸板,在那里写一写汇编语言,你会有更深刻的体会。
当然了,我说一些特别业余的话哈,要是只是这么些二进制数就能显示字符串,还要什么动不动就几个G的操作系统,这里一共才几个字节啊,是吧。
完全用汇编写hello,就是CPU执行的每一个指令都是亲手编写的汇编代码,也不是不行,那么请各位加油!!
403

被折叠的 条评论
为什么被折叠?



