NASM
Netwide Assembler
free
安装
Linux, sudo apt-get install nasm
Windows,下载安装包
学习汇编语言的目的
更好的理解计算机组成和程序如何运行
汇编程序更小更高效,能够用在一些环境苛刻的地方,如嵌入式
在一些软件中有使用到汇编语言,如Linux内核,能够在C/C++中嵌入汇编语言
第一个程序
编辑代码
gedit hello.asm
section .text
global _start:
_start:
mov eax, 4
mov ebx, 1
mov ecx, string
mov edx, length
int 80h
mov eax, 1
mov ebx, 0
int 80h
section .data
string: db 'Hello Word', 0Ah
length: equ 13
section .bss
var: resb 1 ; reserve 1 byte
编译
nasm -g -f elf64 -o hello.o hello.asm
连接
ld -o hello hello.o
运行
./hello
反汇编
objdump -d -M intel hello
sections in NASM
一个NASM程序包含多个不同的section
section .text
程序部分,包含可执行指令
是程序执行的开始处
section .bss
程序部分,包含未初始化的变量
程序加载前,在文件中只包含section的描述信息,不包含数据
section .data
程序部分,包含初始化的变量
程序加载前,在文件中包含section的描述信息和数据
变量声明
空间预留伪指令
变量的声明
RESx
变量定义伪指令
变量的声明和初始化
Dx
数据单元描述x
b byte 1 byte
w word 2 bytes
d double word 4 bytes
q quad word 8 bytes
t ten word 20 bytes
注释符号";"
二进制数,1010b
十六进制,10h
八进制,10o
声明数组
var: Dx 1,2,3
使用字符串进行初始化
string: db "Hello"
string: db "H","e","l","l","o"
重复定义
var: times 100 Dx 1
解引用
一切符号都被作为数据使用,使用的是符号本身
[符号],符号被作为地址使用,使用的是符号指向的数据或空间
必要时,需要描述数据类型(长度)
BYTE, WORD, DWORD, QWORD, TWORD
声明常量
var: equ 10h
$和$$
$,表示当前行汇编后的地址
$$,表示当前section汇编后的首地址
X86基本指令集
Linux下基本输入输出
调用约定
中断类型码,使用int80h,在Windows中使用int21h
中断操作码,eax,用于指定中断例程中的子程序
其他操作数,放在其他通用寄存器中
exit系统调用
mov eax, 1
mov ebx, 0
int 80h
read系统调用
mov eax, 3 ; system call number for read
mov ebx, 0 ; source keyboard
mov ecx, var ; pointer to memory location
mov edx, dword[size] ; size of the string
int 80h
write系统调用
mov eax, 4 ; system call number for write
mov ebx, 1 ; standard output device
mov ecx, msg1 ; pointer to output string
mov edx, size1 ; number of characters
int 80h
子程序
调用约定(Calling Convention)
一组传参规则
调用程序和子程序遵守的
调用C库函数
需要遵循C的调用约定
参数,由调用程序入栈,由子程序出栈
参数入栈的顺序为从右到左
例子
section .text
global main
; declaring the external functions to be used in the program
extern scanf
extern printf
getint:
push ebp
mov ebp, esp
sub esp, 2
lea eax, [ebp-2]
push eax
push fmt1
call scanf
mov ax, word [ebp-2]
mov word [num], ax
mov esp, ebp
pop ebp
ret
putint:
push ebp
mov ebp, esp
sub esp, 2
mov ax, word [num]
mov [ebp-2], ax
push fmt2
call printf
mov esp, ebp
pop ebp
ret
main:
mov eax, 4
mov ebx, 1
mov ecx, msg1
mov edx, size1
int 80h
call getint
mov ax, word[num]
mov bx, ax
mul bx
mov word [num], ax
call putint
exit:
mov ax, 1
mov bx, 0
int 80h
section .data
fmt1: db "%d", 0
fmt2: db "Square of the number is: %d", 10, 0
msg1: db "Enter an integer: "
size1: db $-msg1
section .bss
num: resw 1
编译
nasm -g -f elf64 -o hello.o hello.asm
失败,int.asm:7: error: instruction not supported in 64-bit mode
编译成x86
nasm -g -f elf -o hello.o hello.asm
nasm -g -f elf32 -o hello.o hello.asm
连接
使用GCC默认连接
gcc int.o -o int
失败,i386 architecture of input file `int.o' is incompatible with i386:x86-64 output
因为,GCC是x64的,会进行x64连接
gcc -v
Target: x86_64-linux-gnu
64位的x86,即x64
使用GCC进行x86连接
安装库
sudo apt-get install build-essential module-assistant
sudo apt-get install gcc-multilib g++-multilib
gcc -m32 int.o -o int
注意使用ld进行连接时
默认进行x64连接
若进行x86连接,可使用
ld -m elf_i386
其他
ALIGN n
汇编控制
下一地址,按照n对齐
n可以为16等
地址对齐,即是约定地址尾部为0(能被n整除),通过仅存储地址高位,实现小位数空间存大位数数据
地址按照16位对齐,低4位始终为0,只需要存储高28位
[BITS n]
汇编控制
表示section的位数,决定了跳转指令中偏移表示的位数
n可以为16,32等
16位代码段中,跳转目标的偏移用16位表示
32位代码段中,跳转目标的偏移用32位表示