在汇编过程中,难免出现问题,需要调试,如果每次测试,都要打印可能有问题的变量,效率会非常低。掌握调试的技巧非常重要。这里使用gdb调试器,gdb是是开源组织gnu发布的,功能强大,可以调试多种语言的代码。
以下面的test5_1.asm为例,展示基本的调试技巧。
; test5_1.asm
; nasm -f elf64 -g -o test5_1.o test5_1.asm
; ld -o test5_1 test5_1.o
section .data
array dq 1,2,3,4,5
section .text
global _start
_start:
mov rsi, 0
mov rax, 0
L1:
cmp rsi, 5
jge L1_END
add rax, [array+8*rsi]
inc rsi
jmp L1
L1_END:
mov rax, 60 ; exit
syscall
因为要使用gdb对程序进行调试,因此在使用nasm编译的时候加上选项-g,开启调试信息。
在.data数据段,定义了一个数组array,类型是dq,8个字节,内容是1,2,3,4,5.
在.text段中,将rsi和rax设置为0,rsi用于数组索引,rax存放数组和。接着是标签L1,比较rsi和5,当rsi>=5时,跳转到L1_END(jge L1_END),结束程序;add rax, [array+8*rsi],这里用到了add指令,进行相加,中括号里面的内容表示地址,中括号表示取里面地址的值,add的两个操作数大小是要相等的。array+8*rsi表示取数组array的第rsi个元素,数组中的元素大小是8个字节,因此8*rsi,去取第rsi个元素的首地址。在地址array+8*rsi处,取8个字节,组成一个dq,然后将其与寄存器rax相加,结果保存到rax中。inc是自增指令,inc rsi,表示rsi=rsi+1,jmp L1,跳转到标号L1处。
这是一段比较简单的求和代码,现在对其进行调试,编译,链接后,使用下面的命令调试:
gdb test5_1
关于gdb的使用,网上有大量的介绍文章。这里列举几个常用的命令。
break:加断点,后面可以跟行号:break 10,也可以跟函数或标号的名字:break *main,简写为b
print:打印变量值 print $rax,打印寄存器rax的值,print/x $rax,以十六进制打印,print/t $rax,以二进制打印,print/c $rax,以字符格式打印,print简写为p
x:打印内存内容,后面跟内存地址。
next/step:执行一行代码,注意两者的差别,如果该行代码是执行函数,step进入该函数,而next执行函数调用,并不进入函数。
nexti/stepi:指向一行代码,在汇编中。
info:显示各种信息。info reg:显示寄存器信息。
continue:继续执行,到下一个断点或结束。
调试汇编代码时,会用到的命令是nexti或stepi,执行一行汇编代码。
break *L1
run
stepi
print $rax
使用上面的命令进行调试test4_1,在L1处添加断点,然后运行程序,之后一步步执行,然后打印rax的值,同时也可以对寄存器rsi的值进行观察。