问题描述
我是汇编的新手,并且知道我的汇编代码可能无效或可能更好。由于不断的变化,程序集的注释可能会有些混乱。目的是单独打印字符串的每个字符,并且遇到诸如%s之类的格式标识符时,它将使用参数之一代替%s来打印字符串。 例如:
字符串:您好,%s
参数(RSI):Foo
输出:您好,Foo
.bss
char: .byte 0
.text
.data
text1: .asciz "%s!\n"
text2: .asciz "My name is %s. I think I’ll get a %u for my exam. What does %r do? And %%?\n"
word1: .asciz "Piet"
.global main
main:
pushq %rbp # push the base pointer (and align the stack)
movq %rsp,%rbp # copy stack pointer value to base pointer
movq $text1,%rdi
movq $word1,%rsi
movq $word1,%rdx
movq $word1,%rcx
movq $word1,%r8
movq $word1,%r9
call myPrint
end:
movq %rbp,%rsp # clear local variables from stack
popq %rbp # restore base pointer location
movq $60,%rax
movq $0,%rdi
syscall
myPrint:
pushq %rbp
movq %rsp,%rbp
pushq %rsi
pushq %rdx
pushq %rcx
pushq %r8
pushq %r9
movq %rdi,%r12
regPush:
movq $0,%rbx
#rbx: counter
printLooper:
movb (%r12),%r14b #Get a byte of r12 to r14
cmpb $0,%r14b #Check if r14 is a null byte
je endPrint #If it is a null byte then go to 'endPrint'
cmpb $37,%r14b
je formatter
incq %r12 #Increment r12 to the next byte
skip:
mov $char,%r15 #Move char address to r15
mov %r14b,(%r15) #Move r14 byte into the value of r15
mov $char,%rcx #Move char address into rcx
movq $1,%r13 #For the number of byte
printer:
movq $0,%rsi #Clearing rsi
mov %rcx,%rsi #Move the address to rsi
movq $1,%rax #Sys write
movq $1,%rdi #Output
movq %r13,%rdx #Number of byte to rdx
syscall
jmp printLooper
formatter:
incq %r12 #Moving to char after "%"
movb (%r12),%r14b #Moving the char byte into r14
cmpb $115,%r14b #Compare 's' with r14
je formatString #If it is equal to 's' then jump to 'formatString'
movb -1(%r12),%r14b #Put back the prevIoUs char into r14
jmp skip
####String Formatter Start ##################################################
formatString:
addq $1,%rbx
movq $8,%rax
mulq %rbx
subq %rax,%rbp
movq (%rbp),%r15
pushq %r15 ### into the stack
movq $0,%r13 ### Byte counter
formatStringLoop:
movb (%r15),%r14b #Move char into r14
cmpb $0,%r14b #Compare r14 with null byte
je formatStringEnd #If it is equal,go to 'formatStringEnd'
incq %r15 #Increment to next char
addq $1,%r13 #Add 1 to the byte counter
jmp formatStringLoop#Loop again
formatStringEnd:
popq %rcx #Pop the address into rcx
incq %r12 #Moving r12 to next char
jmp printer
#######String Formatter End #############################################
endPrint:
movq %rbp,%rsp
popq %rbp
ret
解决方法
在formatString
中,您用%rbp
修改了subq %rax,%rbp
,忘记了要从其中恢复%rsp
。因此,当您在函数返回之前mov %rbp,%rsp
时,您会以%rsp
指向其他地方,从而得到错误的返回地址。
我想您正在从%rbp
中减去一些偏移量以在堆栈上获得一些空间。这似乎是不安全的,因为您在那里还推送了许多其他内容。在堆栈指针下方最多使用128个字节是安全的,因为它是red zone,但是使用%rsp
的偏移量会更自然。使用SIB寻址,您可以以恒定或可变偏移量访问%rsp
的数据,而无需实际更改其值。
我在gdb中的发现方式:通过在myPrint
和endPrint
设置断点,我发现%rsp
的{{1}}与输入时不同。它的值只能来自ret
,所以我做了%rbp
以便在watch $rbp
更改时使调试器中断,并且它直接指向%rbp
中有问题的指令。 (这也可以通过在源代码中搜索formatString
来找到。)
此外,您在文件顶部的%rbp
被放错了位置,因此所有代码都放置在.text
部分中。这个actually works,但肯定不是您想要的。