程序集x86 64 Linux AT&T:打印例程分段错误

问题描述

我是汇编的新手,并且知道我的汇编代码可能无效或可能更好。由于不断的变化,程序集的注释可能会有些混乱。目的是单独打印字符串的每个字符,并且遇到诸如%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中的发现方式:通过在myPrintendPrint设置断点,我发现%rsp的{​​{1}}与输入时不同。它的值只能来自ret,所以我做了%rbp以便在watch $rbp更改时使调试器中断,并且它直接指向%rbp中有问题的指令。 (这也可以通过在源代码中搜索formatString来找到。)


此外,您在文件顶部的%rbp被放错了位置,因此所有代码都放置在.text部分中。这个actually works,但肯定不是您想要的。