程序集 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 找到这个的:通过在 myPrint
和 endPrint
处设置断点,我发现 %rsp
在 ret
处与进入时不同.它的值只能来自 %rbp
,所以我做了 watch $rbp
让调试器在 %rbp
改变时中断,它直接指向 formatString
中的违规指令。 (我也可以通过搜索源代码找到 %rbp
。)
此外,文件顶部的 .text
放错了位置,因此所有代码都放在了 .data
部分。这 actually works 但这肯定不是您想要的。
我是汇编的新手,我知道我的汇编代码可能效率不高或可能更好。由于不断变化,大会的评论可能会有点乱。目标是单独打印字符串的每个字符,当遇到像 %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 找到这个的:通过在 myPrint
和 endPrint
处设置断点,我发现 %rsp
在 ret
处与进入时不同.它的值只能来自 %rbp
,所以我做了 watch $rbp
让调试器在 %rbp
改变时中断,它直接指向 formatString
中的违规指令。 (我也可以通过搜索源代码找到 %rbp
。)
此外,文件顶部的 .text
放错了位置,因此所有代码都放在了 .data
部分。这 actually works 但这肯定不是您想要的。