堆栈参数在使用 C 调用约定的程序集中消失
Stack arguments are disappearing in assembly using C calling convention
我目前正在编写一些 x86 汇编代码并使用 as
和 ld
.
将其与 glibc 链接
.section .data
str1:
.asciz "\n"
str3:
.asciz "Hello"
str4:
.asciz "Goodbye"
str5:
.asciz "Guten Tag!"
str6:
.asciz "Guten Morgen!"
.section .bss
.section .text
.globl _start
_start:
call main
movl %eax, %ebx
movl , %eax
int [=10=]x80
# void writeLine(char*);
.type writeLine, @function
writeLine:
pushl %ebp
movl %esp, %ebp
movl $str1, %ecx
pushl %ecx
movl 8(%ebp), %ecx
pushl %ecx
call strcat
movl %eax, %ecx
pushl %ecx
call puts
popl %ebx
.leaver1:
movl %ebp, %esp
popl %ebp
ret
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
movl $str3, %ecx
pushl %ecx
call writeLine
popl %ebx
movl $str4, %ecx
pushl %ecx
call writeLine
popl %ebx
movl $str5, %ecx
pushl %ecx
call writeLine
popl %ebx
movl $str6, %ecx
pushl %ecx
call writeLine
popl %ebx
.leaver3:
movl %ebp, %esp
popl %ebp
ret
问题是,与预期输出相反:
Hello
Goodbye
Guten Tag!
Guten Morgen!
我得到:
Hello
Guten Tag!
通过一些更深入的调试,当我像这样执行 printf
时,参数似乎正在消失:
pushl 8(%ebp) # Argument for writeLine
pushl $str7 # "Argument for writeLine: %s\n"
call printf # printf();
它将“消失”参数显示为空,例如:
# should be "Goodbye"
Argument for writeLine:
同样,当我以 %d
格式打印出参数时,我最终得到一个常数,它只根据字符串(str3
、str4
等)发生变化我路过。
示例:134525414
我还尝试将它与 gcc (gcc -m32 -S main.c
) 编译的程序生成的程序集进行比较,该程序应该做完全相同的事情,除了 gcc 生成的东西我找不到任何带有 swift 的代码都可以查看代码。
看来,我的论点正在消失。
strcat(str3, str1)
通过将 str1
的字节复制到从 str3
的终止空字节开始的内存中,将 str1
连接到 str3
的末尾。因此 str3
的终止空值被字符 '\n'
覆盖,并且新的终止空值被写入后续字节。但是你猜怎么着:紧跟在 str3
的终止 null 之后的字节是 str4
的第一个字节。所以你刚刚用空字节覆盖了 str4
的第一个字节。因此,当您稍后打印它时,它的行为就像一个空字符串。
您可以在每个字符串之间添加一个额外的 space 字节来避免这种情况。但更广泛地说,writeLine
函数修改它传递的字符串并不是真正明智的,因此更好的计划是重新设计它,这样它就不需要这样做了。例如,您可以通过调用 puts
来写入传递的字符串(它已经附加了一个换行符),如果您想要一个额外的换行符,请单独调用 putc
.
我目前正在编写一些 x86 汇编代码并使用 as
和 ld
.
.section .data
str1:
.asciz "\n"
str3:
.asciz "Hello"
str4:
.asciz "Goodbye"
str5:
.asciz "Guten Tag!"
str6:
.asciz "Guten Morgen!"
.section .bss
.section .text
.globl _start
_start:
call main
movl %eax, %ebx
movl , %eax
int [=10=]x80
# void writeLine(char*);
.type writeLine, @function
writeLine:
pushl %ebp
movl %esp, %ebp
movl $str1, %ecx
pushl %ecx
movl 8(%ebp), %ecx
pushl %ecx
call strcat
movl %eax, %ecx
pushl %ecx
call puts
popl %ebx
.leaver1:
movl %ebp, %esp
popl %ebp
ret
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
movl $str3, %ecx
pushl %ecx
call writeLine
popl %ebx
movl $str4, %ecx
pushl %ecx
call writeLine
popl %ebx
movl $str5, %ecx
pushl %ecx
call writeLine
popl %ebx
movl $str6, %ecx
pushl %ecx
call writeLine
popl %ebx
.leaver3:
movl %ebp, %esp
popl %ebp
ret
问题是,与预期输出相反:
Hello Goodbye Guten Tag! Guten Morgen!
我得到:
Hello Guten Tag!
通过一些更深入的调试,当我像这样执行 printf
时,参数似乎正在消失:
pushl 8(%ebp) # Argument for writeLine
pushl $str7 # "Argument for writeLine: %s\n"
call printf # printf();
它将“消失”参数显示为空,例如:
# should be "Goodbye"
Argument for writeLine:
同样,当我以 %d
格式打印出参数时,我最终得到一个常数,它只根据字符串(str3
、str4
等)发生变化我路过。
示例:134525414
我还尝试将它与 gcc (gcc -m32 -S main.c
) 编译的程序生成的程序集进行比较,该程序应该做完全相同的事情,除了 gcc 生成的东西我找不到任何带有 swift 的代码都可以查看代码。
看来,我的论点正在消失。
strcat(str3, str1)
通过将 str1
的字节复制到从 str3
的终止空字节开始的内存中,将 str1
连接到 str3
的末尾。因此 str3
的终止空值被字符 '\n'
覆盖,并且新的终止空值被写入后续字节。但是你猜怎么着:紧跟在 str3
的终止 null 之后的字节是 str4
的第一个字节。所以你刚刚用空字节覆盖了 str4
的第一个字节。因此,当您稍后打印它时,它的行为就像一个空字符串。
您可以在每个字符串之间添加一个额外的 space 字节来避免这种情况。但更广泛地说,writeLine
函数修改它传递的字符串并不是真正明智的,因此更好的计划是重新设计它,这样它就不需要这样做了。例如,您可以通过调用 puts
来写入传递的字符串(它已经附加了一个换行符),如果您想要一个额外的换行符,请单独调用 putc
.