第18部分-Linux x86 64位汇编 AT&T实现栈
我们根据上篇中AT&T汇编语法改造之前栈篇的代码如下。
主要好处是通过as编译后,可以通过gdb进行debug。
.section .data
.equ SYS_WRITE,1
.equ STD_IN,1
.equ SYS_EXIT,60
.equ EXIT_CODE,0
.equ NEW_LINE,0xa
WRONG_ARGC: .ascii "Must be two command line argument\n"
.section .text
.global _start
_start:
pop %rcx ;//栈的第一个保存的是参数的数量,数量不为3则跳转到argcError处退出。
cmp $3, %rcx ;//两个参数,外加一个程序名,程序名也是参数。
jne argcError
addq $8, %rsp;//[rsp+8]保存的是第一个参数argv[0],跳过程序名这个变量。
popq %rsi;//将第一个参数赋值给rsi。
call str_to_int;//调用函数str_to_int,将参数转换为整型,保存于rax。
movq %rax, %r10; //保存到r10寄存器中
popq %rsi; //将第二个参数赋值为rsi
call str_to_int; //调用函数str_to_int,字符串保存的,例如“123\0”
movq %rax,%r11; //保存到r11寄存器中
addq %r11, %r10; //完成加法。
movq %r10, %rax
xor %r12, %r12
jmp int_to_str; //调用函数int_to_str函数,整型转换为字符串
argcError:
movq $1,%rax
movq $1,%rdi
movq $WRONG_ARGC, %rsi
movq $34, %rdx
syscall
jmp exit
str_to_int:; //负责将字符串转换为整型
xor %rax, %rax; //清空rax寄存器
movq $10,%rcx; //赋值rcx为10
next:
cmpb $0,(%rsi); //对比参数的低位字节,是否为0,字符串最后一个为’\0’。
je return_str; //为0,则调用函数return_str返回。
movb (%rsi),%bl; //否则将低8位赋值给bl。
subb $48, %bl; //参数减去48,ASCII码中,字符和数字相差48。
mulq %rcx; //乘以10,将已处理的字符往高位挪。
addq %rbx, %rax;
inc %rsi; //增加rsi,即变为下一个字节。
jmp next; //调到函数next
return_str:
ret; //直接返回。
int_to_str:
movq $0, %rdx
movq $10, %rbx
divq %rbx; //除以10,获取个位数余数在rdx,商在rax。
addq $48,%rdx
;addq $0x0,%rdx
pushq %rdx
inc %r12; //首次迭代为0,记录字符的个数,用于后续输出。
cmp $0x0,%rax; //商是否为0,为0则退出,跳转到print进行输出。
jne int_to_str; //商不为0,则继续输出。
jmp print
print:
movq $1,%rax
mulq %r12
movq $8,%rax;//计算长度,因为入栈是8个字节一次的,所以每个字符输出都是8个字节了
mulq %r12
movq %rax,%rdx
movq $SYS_WRITE,%rax
movq $STD_IN,%rdi
movq %rsp,%rsi
syscall; //调用sys_write输出结果
jmp exit
exit:
movq $SYS_EXIT,%rax
movq $EXIT_CODE,%rdi
syscall
编译:
# as -g -o addsum_att.o addsum_att.s
#ld -o addsum_att addsum_att.o