本篇介绍
本篇介绍下汇编中的宏和IO操作,其中IO操作包括控制台IO和文件IO
宏
首先宏并不是汇编支持的,而是nasm 汇编器支持的,这个也容易想到,汇编本身是一套指令,而宏就是将若干指令替换成一个符号,在编译的时候再展开到代码中,这完全是编译层面的能力。 接下来看一个代码例子,nasm汇编的使用;
代码语言:javascript复制; macro.asm
extern printf
�fine double_it(r) sal r, 1 ; single line macro
%macro prntf 2 ; multiline macro with 2 arguments
section .data
%%arg1 db %1,0 ; first argument
%%fmtint db "%s %ld",10,0 ; formatstring
section .text ; the printf arguments
mov rdi, %%fmtint
mov rsi, %%arg1
mov rdx,[%2] ; second argument
mov rax,0 ; no floating point
call printf
%endmacro
section .data
number dq 15
section .bss
section .text
global main
main:
push rbp
mov rbp,rsp
prntf "The number is", number
mov rax, [number]
double_it(rax)
mov [number],rax
prntf "The number times 2 is", number
leave
ret
结果如下:
The number is 15
The number times 2 is 30
可以看到有2种宏,一种是单行的,这时候用�fine 就可以了。另外一种是多行的,这时候需要用%macro 和 %endmacro。而且对于多行的宏,可以指定参数的个数,%n表示第n个入参地址,如果宏内部需要用变量,变量名字前就需要用%% 做前缀,比如%%arg1,%%fmtint。%% 就是告诉nasm每次调用宏就需要创建新的变量实例,这样就不会遇到变量重复定义的问题了。 接下来也可以反编译看下结果:
代码语言:javascript复制0000000000401130 <main>:
401130: 55 push rbp
401131: 48 89 e5 mov rbp,rsp
401134: 48 bf 46 40 40 00 00 movabs rdi,0x404046
40113b: 00 00 00
40113e: 48 be 38 40 40 00 00 movabs rsi,0x404038
401145: 00 00 00
401148: 48 8b 14 25 30 40 40 mov rdx,QWORD PTR ds:0x404030
40114f: 00
401150: b8 00 00 00 00 mov eax,0x0
401155: e8 d6 fe ff ff call 401030 <printf@plt>
40115a: 48 8b 04 25 30 40 40 mov rax,QWORD PTR ds:0x404030
401161: 00
401162: 48 d1 e0 shl rax,1
401165: 48 89 04 25 30 40 40 mov QWORD PTR ds:0x404030,rax
40116c: 00
40116d: 48 bf 64 40 40 00 00 movabs rdi,0x404064
401174: 00 00 00
401177: 48 be 4e 40 40 00 00 movabs rsi,0x40404e
40117e: 00 00 00
401181: 48 8b 14 25 30 40 40 mov rdx,QWORD PTR ds:0x404030
401188: 00
401189: b8 00 00 00 00 mov eax,0x0
40118e: e8 9d fe ff ff call 401030 <printf@plt>
401193: c9 leave
401194: c3 ret
这样就可以看到宏被展开后的结果了,基本可以和把宏用指令替换对上。
控制台 IO
有时候我们需要直接从控制台读写,这时候除了用c的io函数外,也可以直接用read,write的系统调用,下面是一个例子,用来从控制台读取内容,然后显示到控制台上:
代码语言:javascript复制; console2.asm
section .data
msg1 db "Hello, World!",10,0
msg2 db "Your turn (only a-z): ",0
msg3 db "You answered: ",0
inputlen equ 10 ;inputbuffer
NL db 0xa
section .bss
input resb inputlen 1 ;provide space for ending 0
section .text
global main
main:
push rbp
mov rbp,rsp
mov rdi, msg1 ; print first string
call prints
mov rdi, msg2 ; print second string, no NL
call prints
mov rdi, input ; address of inputbuffer
mov rsi, inputlen ; length of inputbuffer
call reads ; wait for input
mov rdi, msg3 ; print third string and add the input string
call prints
mov rdi, input ; print the inputbuffer
call prints
mov rdi,NL ; print NL
call prints
leave
ret
;----------------------------------------------------------
prints:
push rbp
mov rbp, rsp
push r12 ; callee saved
; Count characters
xor rdx, rdx ; length in rdx
mov r12, rdi
.lengthloop:
cmp byte [r12], 0
je .lengthfound
inc rdx
inc r12
jmp .lengthloop
.lengthfound: ; print the string, length in rdx
cmp rdx, 0 ; no string (0 length)
je .done
mov rsi,rdi ; rdi contains address of string
mov rax, 1 ; 1 = write
mov rdi, 1 ; 1 = stdout
syscall
.done:
pop r12
leave
ret
;----------------------------------------------------------
reads:
section .data
section .bss
.inputchar resb 1
section .text
push rbp
mov rbp, rsp
push r12 ; callee saved
push r13 ; callee saved
push r14 ; callee saved
mov r12, rdi ; address of stringbuffer
mov r13, rsi ; max length in r13
xor r14, r14 ; character counter
.readc:
mov rax, 0 ; read
mov rdi, 1 ; stdin
lea rsi, [.inputchar] ; address of input
mov rdx, 1 ; # of characters to read
syscall
mov al, [.inputchar] ; char is NL?
cmp al, byte[NL]
je .done ; NL end
cmp al, 97 ; lower than a?
jl .readc ; ignore it
cmp al, 122 ; higher than z?
jg .readc ; ignore it
inc r14 ; inc counter
cmp r14, r13
ja .readc ; buffer max reached, ignore
mov byte [r12], al ; safe the char in the buffer
inc r12 ; point to next char in buffer
jmp .readc
.done:
inc r12
mov byte [r12],0 ; add end 0 to stringbuffer
pop r14 ; callee saved
pop r13 ; callee saved
pop r12 ; callee saved
leave
ret
输出:
./console2
Hello, World!
Your turn (only a-z): hello
You answered: hello
这儿的关键点在于读写时候的系统调用,比如写的时候也是用rdi,rsi,rdx来传参的,用rax指定系统调用号,用代码中可以看到,读的系统调用号是0,写是1。
文件IO
接下来是一段文件IO 的例子,涉及到创建文件,读写文件,关闭删除文件,代码比较多,不过逻辑比较简单:
代码语言:javascript复制; file.asm
section .data
; expressions used for conditional assembly
CREATE equ 1
OVERWRITE equ 1
APPEND equ 1
O_WRITE equ 1
READ equ 1
O_READ equ 1
DELETE equ 1
; syscall symbols
NR_read equ 0
NR_write equ 1
NR_open equ 2
NR_close equ 3
NR_lseek equ 8
NR_create equ 85
NR_unlink equ 87
; creation and status flags
O_CREAT equ 00000100q
O_APPEND equ 00002000q
; access mode
O_RDONLY equ 000000q
O_WRONLY equ 000001q
O_RDWR equ 000002q
; create mode (permissions)
S_IRUSR equ 00400q ;user read permission
S_IWUSR equ 00200q ;user write permission
NL equ 0xa
bufferlen equ 64
fileName db "testfile.txt",0
FD dq 0 ; file descriptor
text1 db "1. Hello...to everyone!",NL,0
len1 dq $-text1-1 ;remove 0
text2 db "2. Here I am!",NL,0
len2 dq $-text2-1 ;remove 0
text3 db "3. Alife and kicking!",NL,0
len3 dq $-text3-1 ;remove 0
text4 db "Adios !!!",NL,0
len4 dq $-text4-1
error_Create db "error creating file",NL,0
error_Close db "error closing file",NL,0
error_Write db "error writing to file",NL,0
error_Open db "error opening file",NL,0
error_Append db "error appending to file",NL,0
error_Delete db "error deleting file",NL,0
error_Read db "error reading file",NL,0
error_Print db "error printing string",NL,0
error_Position db "error positioning in file",NL,0
success_Create db "File created and opened",NL,0
success_Close db "File closed",NL,NL,0
success_Write db "Written to file",NL,0
success_Open db "File opened for reading/(over)writing/updating",NL,0
success_Append db "File opened for appending",NL,0
success_Delete db "File deleted",NL,0
success_Read db "Reading file",NL,0
success_Position db "Positioned in file",NL,0
section .bss
buffer resb bufferlen
section .text
global main
main:
push rbp
mov rbp,rsp
%IF CREATE
;CREATE AND OPEN A FILE, THEN CLOSE -----------------------------------------
; create and open file
mov rdi, fileName
call createFile
mov qword [FD], rax ; save descriptor
; write to file #1
mov rdi, qword [FD]
mov rsi, text1
mov rdx, qword [len1]
call writeFile
; close file
mov rdi, qword [FD]
call closeFile
%ENDIF
%IF OVERWRITE
;OPEN AND OVERWRITE A FILE, THEN CLOSE ---------------------------------------
; open file
mov rdi, fileName
call openFile
mov qword [FD], rax ; save file descriptor
; write to file #2 OVERWRITE!
mov rdi, qword [FD]
mov rsi, text2
mov rdx, qword [len2]
call writeFile
; close file
mov rdi, qword [FD]
call closeFile
%ENDIF
%IF APPEND
;OPEN AND APPEND TO A FILE, THEN CLOSE ---------------------------------------
; open file to append
mov rdi, fileName
call appendFile
mov qword [FD], rax ; save file descriptor
; write to file #3 APPEND!
mov rdi, qword [FD]
mov rsi, text3
mov rdx, qword [len3]
call writeFile
; close file
mov rdi, qword [FD]
call closeFile
%ENDIF
%IF O_WRITE
;OPEN AND OVERWRITE AT AN OFFSET IN A FILE, THEN CLOSE -----------------------
; open file to write
mov rdi, fileName
call openFile
mov qword [FD], rax ; save file descriptor
; position file at offset
mov rdi, qword[FD]
mov rsi, qword[len2] ;offset at this location
mov rdx, 0
call positionFile
; write to file at offset
mov rdi, qword[FD]
mov rsi, text4
mov rdx, qword [len4]
call writeFile
; close file
mov rdi, qword [FD]
call closeFile
%ENDIF
%IF READ
;OPEN AND READ FROM A FILE, THEN CLOSE ---------------------------------------
; open file to read
mov rdi, fileName
call openFile
mov qword [FD], rax ; save file descriptor
; read from file
mov rdi, qword [FD]
mov rsi, buffer
mov rdx, bufferlen
call readFile
mov rdi,rax
call printString
; close file
mov rdi, qword [FD]
call closeFile
%ENDIF
%IF O_READ
;OPEN AND READ AT AN OFFSET FROM A FILE, THEN CLOSE ---------------------------------------
; open file to read
mov rdi, fileName
call openFile
mov qword [FD], rax ; save file descriptor
; position file at offset
mov rdi, qword[FD]
mov rsi, qword[len2] ;skip the first line
mov rdx, 0
call positionFile
; read from file
mov rdi, qword [FD]
mov rsi, buffer
mov rdx, 10 ;number of characters to read
call readFile
mov rdi,rax
call printString
; close file
mov rdi, qword [FD]
call closeFile
%ENDIF
%IF DELETE
;DELETE A FILE --------------------------------------------------
; delete file UNCOMMENT NEXT LINES TO USE
mov rdi, fileName
call deleteFile
%ENDIF
leave
ret
; FILE MANIPULATION FUNCTIONS-------------------------------------
;-----------------------------------------------------------------
global readFile
readFile:
mov rax, NR_read
syscall ; rax contains # of characters read
cmp rax, 0
jl readerror
mov byte [rsi rax],0 ; add a terminating zero to the string
mov rax, rsi
mov rdi, success_Read
push rax ; caller saved
call printString
pop rax ; caller saved
ret
readerror:
mov rdi, error_Read
call printString
ret
;-----------------------------------------------------------------
global deleteFile
deleteFile:
mov rax, NR_unlink
syscall
cmp rax, 0
jl deleteerror
mov rdi, success_Delete
call printString
ret
deleteerror:
mov rdi, error_Delete
call printString
ret
;-----------------------------------------------------------------
global appendFile
appendFile:
mov rax, NR_open
mov rsi, O_RDWR|O_APPEND
syscall
cmp rax, 0
jl appenderror
mov rdi, success_Append
push rax ; caller saved
call printString
pop rax ; caller saved
ret
appenderror:
mov rdi, error_Append
call printString
ret
;-----------------------------------------------------------------
global openFile
openFile:
mov rax, NR_open
mov rsi, O_RDWR
syscall
cmp rax, 0
jl openerror
mov rdi, success_Open
push rax ; caller saved
call printString
pop rax ; caller saved
ret
openerror:
mov rdi, error_Open
call printString
ret
;-----------------------------------------------------------------
global writeFile
writeFile:
mov rax, NR_write
syscall
cmp rax, 0
jl writeerror
mov rdi, success_Write
call printString
ret
writeerror:
mov rdi, error_Write
call printString
ret
;-----------------------------------------------------------------
global positionFile
positionFile:
mov rax, NR_lseek
syscall
cmp rax, 0
jl positionerror
mov rdi, success_Position
call printString
ret
positionerror:
mov rdi, error_Position
call printString
ret
;-----------------------------------------------------------------
global closeFile
closeFile:
mov rax, NR_close
syscall
cmp rax, 0
jl closeerror
mov rdi, success_Close
call printString
ret
closeerror:
mov rdi, error_Close
call printString
ret
;-----------------------------------------------------------------
global createFile
createFile:
mov rax, NR_create
mov rsi, S_IRUSR |S_IWUSR
syscall
cmp rax, 0 ; file descriptor in rax
jl createerror
mov rdi, success_Create
push rax ; caller saved
call printString
pop rax ; caller saved
ret
createerror:
mov rdi, error_Create
call printString
ret
; PRINT FEEDBACK
;-----------------------------------------------------------------
global printString
printString:
; Count characters
mov r12, rdi
mov rdx, 0
strLoop:
cmp byte [r12], 0
je strDone
inc rdx ;length in rdx
inc r12
jmp strLoop
strDone:
cmp rdx, 0 ; no string (0 length)
je prtDone
mov rsi,rdi
mov rax, 1
mov rdi, 1
syscall
prtDone:
ret
运行结果:
File created and opened
Written to file
File closed
File opened for reading/(over)writing/updating
Written to file
File closed
File opened for appending
Written to file
File closed
File opened for reading/(over)writing/updating
Positioned in file
Written to file
File closed
File opened for reading/(over)writing/updating
Reading file
2. Here I am!
Adios !!!
3. Alife and kicking!
File closed
File opened for reading/(over)writing/updating
Positioned in file
Reading file
Adios !!!
File closed
File deleted
这儿都是通过系统调用操作的IO,libc的文件IO本质上也是这样,当然libc还会有一层cache。从这段代码可以看出系统调用的返回值也是通过rax传递的。