学东西,不做笔记,相当于没学。
听到的,不是你学会的;讲出来的,才是你学会的。

CPU获取数据

CPU只是一个单纯的运算设备,并不存储数据,按照速率快慢,依次从:寄存器、一级缓存、二级缓存,取数据。
而寄存器、一级缓存与二级缓存,都从内存中获取数据。

hello world

不知道从何说起,就从解释hello world开始吧。
一下是汇编hello world代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
section.text
global _start ;must be declared for linker (ld)

_start: ;tells 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 ;string to be printed
len equ $-msg ;length of the string

汇编程序

汇编程序多种多样,例如
微软汇编程序——MASM
Borland Turbo Assembler——TASM
GNU 汇编程序——GAS

而本文是使用NASM汇编器

汇编的三个部分

汇编程序的三个部分

data部分用于声明初始化数据或常量,该数据在运行时不会改变。

1
section.data

bss部分用于声明变量。

1
section.bss

text部分用于保存实际代码,该部分以global _start开头,告诉内核程序执行的开始位置。

1
2
3
section.text
global _start
_start:

PS.注释
以(;)开头

1
; this is an annotation

汇编的内存段

数据段 - .data与.bss
代码段 - .text
堆栈 - 该段包含传递给程序内的函数和过程的数据值

寄存器

处理器操作主要涉及处理数据,为了加速处理器操作,处理器包含一些内部存储器存储位置,称为寄存器。

  • 处理器寄存器
    • 通用寄存器
      • 数据寄存器
      • 指针寄存器
      • 索引寄存器
    • 控制寄存器
    • 段寄存器

通用寄存器

数据寄存器

四个32位的数据寄存器,用于算术、逻辑和其他运算。这些32位寄存器可以通过三种方式使用

  • 作为完整的32位数据寄存器:EAX、EBX、ECX、EDX
  • 32位寄存器的下半部分可用作四个16位数据寄存器:AX、BX、CX、DX
  • 上述4个16位寄存器的下半部分和上半部分可以用作8个8位数据寄存器:AH、AL、BH、BL、CH、CL、DH、DL
    (盗图,这个图真的很好,我在文章尾部添加了参考信息)
    数据寄存器

    AX为主累加器,用于输入/输出和大多数算数指令
    BX为基址寄存器,用于索引寻址
    CX为计数寄存器,与ECX一样,存储迭代操作中的循环计数
    DX为数据寄存器,用于输入/输出操作。与AX寄存器和DX一起使用,用于设计大值的乘法和除法运算

其他寄存器

其他寄存器,暂时还未熟悉,后续看到相关内容再添加

汇编-系统调用

系统调用是用户空间和内核空间之间接口的API。
可以在汇编程序中使用Linux系统调用,在程序中需要执行以下步骤

  • 将系统调用号放入EAX寄存器中
  • 将系统调用的参数存储在寄存器EBX、ECX等中
  • 调用相关中断(80h)
  • 结果通常返回到EAX寄存器中
    六个寄存器存储所使用的系统调用的参数,EBX、ECX、EDX、ESI、EDI、EBP
    例如,系统调用sys_exit的使用
    1
    2
    mov	eax,1		; system call number (sys_exit)
    int 0x80 ; call kernel
    系统调用sys_write的使用
    1
    2
    3
    4
    5
    mov	edx,4		; 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
    所有系统调用及其编号(在调用 int 80h 之前放入 EAX 中的值)都列在 /usr/include/asm/unistd.h 中
    部分系统调用
%eax Name %ebx %ecx %edx %esx %edi
1 sys_exit int - - - -
2 sys_fork struct pt_regs - - - -
3 sys_read unsigned int char * size_t - -
4 sys_write unsigned int const char * size_t - -
5 sys_open const char * int int - -
6 sys_close unsigned int - - - -

边蒙边分析helloworld

现在,我们终于可以分析理解hello world的每一行代码了

  1. 一开始看到的
    1
    2
    3
    4
    section.text
    global _start

    _start:
    为代码的入口
  2. 之后是赋值语句
    1
    2
    3
    4
    mov	edx,len
    mov ecx,msg
    mov ebx,1
    mov eax,4
    之前有了解过,所以知道mov是赋值语句,将逗号之后的内容,赋予逗号之前的地址。
    那么这里就是将len赋值于edx,其他类似
    查看之前的系统调用,eax为4时,系统调用为sys_write;同时ebx类型为unsigned int,暂时不知ebx赋值为1的作用;ecx被赋值msg,edx被赋值edx
  3. 之后是
    1
    int 0x80
    已知含义为调用相关中断
    之前学习单片机的时候就了解中断的概念,就是单个cpu进行正常运行时,当遇上中断触发,需要先运行终端中的内容,完成之后,再返回原本的程序继续运行
    此处调用相关中断,实质内容就是参考寄存器,输出内容
  4. 2中的msg与len这两个常量是哪里来的呢,就是
    1
    2
    3
    section.data
    msg db 'Hello, world!',0xa
    len equ $-msg
    此处,即猜测出来,也提前看了后文字符串部分内容印证,知道为赋值语句
    将Hello, world!赋值给msg,暂时不了解之后的0xa的作用
    将msg的长度赋值给len

参考

[1] https://www.w3schools.cn/assembly/index.html