文章目录
HelloWorld
代码
section .text ;代码段起始
global _start ;导出_start符号到目标文件符号表,给链接器使用
_start: ;入口地址
mov edx,len ;长度立即数放入edx
mov ecx,msg ;字符串地址放入ecx
mov ebx,1 ;准备使用stdout输出
mov eax,4 ;sys_write系统调用号
int 0x80 ;触发软中断,内核根据eax调用sys_write,根据ebx写到stdout输出,内容为字符串地址后len长度的字节。
mov eax,1 ;内核返回后eax存放实际写入的字节数14,已经没用了,所以用1覆盖,准备调用sys_exit
int 0x80 ;调内核终止进程
section .data ;数据段起始
msg db 'Hello, world!', 0xa ;声明msg变量,db表示define byte,按字节定义以下内容:字符串、0xa换行
len equ $ - msg ;msg长度计算,等于地址之差
编译脚本
#!/bin/bash
nasm -f elf hello.asm
ld -m elf_i386 -s -o hello hello.o
./hello
int 0x80 寄存器约定
寄存器 | 用途 | 对应参数 |
---|---|---|
eax |
系统调用号 | 4 = sys_write |
ebx |
第 1 个参数 | 文件描述符 fd |
ecx |
第 2 个参数 | 缓冲区地址 buf |
edx |
第 3 个参数 | 字节数 count |
变量定义
伪指令 | 位数 | 典型用途 |
---|---|---|
db |
8 bit | 字节串、ASCII 字符串 |
dw |
16 bit | 字、Unicode 字符 |
dd |
32 bit | 双字、32 位整数/地址 |
dq |
64 bit | 四字、64 位整数 |
dt |
80 bit | 扩展精度浮点(BCD) |
equ用法
equ 是等值宏,类似C的 #define,在编译时原地替换为立即数,不占运行时内存。
如果想把 len 定义为变量,可以使用dd定义32位整数。
len dd 14
代码段相应修改,表示取 len 内容放入edx。
mov edx,[len]
字符串数值比较
section .text
global _start ;must be declared for using gcc
_start: ;tell linker entry point
mov ecx, [num1] ;num1先放到ecx
cmp ecx, [num2] ;比较num1>num2则跳转到check_third_num比较num1和num3,否则用num2放到ecx
jg check_third_num
mov ecx, [num2]
check_third_num:
cmp ecx, [num3] ;比较ecx>num3则直接跳转到_exit,否则用num3放到ecx.
jg _exit
mov ecx, [num3]
_exit:
mov [largest], ecx ;保证ecx中一定是最大值,mov到largest变量中。
mov ecx,msg ;输出msg
mov edx, len
mov ebx,1 ;file descriptor (stdout)
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
mov ecx,largest ;输出最大值
mov edx, 2
mov ebx,1 ;file descriptor (stdout)
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
mov eax, 1
int 80h
section .data
msg db "The largest digit is: ", 0xA,0xD
len equ $- msg
num1 dd 47
num2 dd 22
num3 dd 31
segment .bss
largest resb 2
循环打印0-9
section .text
global _start ;must be declared for using gcc
_start: ;tell linker entry point
mov ecx,10 ;循环次数
mov eax, '0' ;起始字符
l1:
mov [num], eax
mov eax, 4
mov ebx, 1
push ecx ;循环计数器值入栈防止被破坏
mov ecx, num ;打印字符
mov edx, 1
int 0x80
mov eax, [num] ;取回打印后的字符
sub eax, '0' ;字符转数字'1' - '0' = 1
inc eax ;数字自增1
add eax, '0' ;数字转字符
pop ecx ;恢复循环计数器
loop l1 ;ecx-- 不等于0时跳回l1(loop指令假定ecx包含循环计数值,直至减为0停止)
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .bss
num resb 1
求和函数(过程)
section .text
global _start ;must be declared for using gcc
_start: ;tell linker entry point
mov ecx,'4'
sub ecx, '0'
mov edx, '5'
sub edx, '0' ;字符转数字
call sum ;调求和函数
mov [res], eax ;结果写入内存
mov ecx, msg ;打印消息
mov edx, len
mov ebx,1 ;file descriptor (stdout)
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
mov ecx, res ;打印结果
mov edx, 1
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
sum: ;数字相加后转字符
mov eax, ecx
add eax, edx
add eax, '0'
ret ;返回callee继续执行
section .data
msg db "The sum is:", 0xA,0xD
len equ $- msg
segment .bss
res resb 1
宏
汇编的宏等价于复制粘贴代码块,区别于函数,宏没有call/ret过程,只在编译阶段展开。
语法为
%macro macro_name number_of_params ;关键字 宏名 参数数量
<macro body>
%endmacro
示例
; A macro with two parameters
; Implements the write system call
%macro write_string 2 ;包装打印字符串的宏
mov eax, 4
mov ebx, 1
mov ecx, %1 ;第一个参数
mov edx, %2 ;第二个参数
int 80h
%endmacro
section .text
global _start ;must be declared for using gcc
_start: ;tell linker entry point
write_string msg1, len1 ;宏名 第一个参数 第二个参数
write_string msg2, len2
write_string msg3, len3
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .data
msg1 db 'Hello, programmers!',0xA,0xD
len1 equ $ - msg1
msg2 db 'Welcome to the world of,', 0xA,0xD
len2 equ $- msg2
msg3 db 'Linux assembly programming! '
len3 equ $- msg3