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

Assembly x86 64 Linux AT&T: print routine segmentation error

我是汇编的新手,我知道我的汇编代码可能效率不高或可能更好。由于不断变化,大会的评论可能会有点乱。目标是单独打印字符串的每个字符,当遇到像 %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    , %rax 
movq    [=11=], %rdi 

syscall 



myPrint: 
pushq   %rbp 
movq    %rsp, %rbp

pushq   %rsi  
pushq   %rdx 
pushq   %rcx 
pushq   %r8 
pushq   %r9 

movq    %rdi, %r12 
regPush: 
movq    [=11=], %rbx 

#rbx: counter 
printLooper: 
movb    (%r12), %r14b    #Get a byte of r12 to r14 

cmpb    [=11=], %r14b        #Check if r14 is a null byte 
je      endPrint        #If it is a null byte then go to 'endPrint'

cmpb    , %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    , %r13        #For the number of byte 

printer: 
movq    [=11=], %rsi        #Clearing rsi 
mov     %rcx, %rsi      #Move the address to rsi 
movq    , %rax        #Sys write 
movq    , %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    5, %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    , %rbx 
movq    , %rax 
mulq    %rbx 
subq    %rax, %rbp 
movq    (%rbp), %r15
pushq   %r15            ### into the stack 
movq    [=11=], %r13        ### Byte counter 


formatStringLoop: 
movb   (%r15), %r14b    #Move char into r14 

cmpb    [=11=], %r14b        #Compare r14 with null byte 
je      formatStringEnd #If it is equal, go to 'formatStringEnd'

incq    %r15            #Increment to next char 
addq    , %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 就在函数 returns 之前时,你最终 %rsp 指向其他地方,所以你得到错误的 return 地址。

我猜你正在从 %rbp 中减去一些偏移量以在堆栈上得到一些 space。这似乎不安全,因为你在那里推送了很多其他东西。使用堆栈指针下方最多 128 个字节是安全的,因为这是 ,但使用 %rsp 的偏移量会更自然。使用 SIB 寻址,您可以访问 %rsp 的常量或可变偏移量的数据,而无需实际更改其值。

我是如何用 gdb 找到这个的:通过在 myPrintendPrint 处设置断点,我发现 %rspret 处与进入时不同.它的值只能来自 %rbp,所以我做了 watch $rbp 让调试器在 %rbp 改变时中断,它直接指向 formatString 中的违规指令。 (我也可以通过搜索源代码找到 %rbp。)


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