本文由 @lonelyrains 出品,转载请注明出处。
文章链接: http://blog.csdn.net/lonelyrains/article/details/12491761
#include <stdio.h> #include <string.h> const char * GetHello() { char *rtn = "hellodjfldsjfsadlfkja;sldjflaskdjfalksdjfladjflkjsaldkjflaksdfljalsdkjfl;asjdfsadfasdlkjfaldskjf;lajdsflkja;sldjflkdsajflaksjdf;lkjdsafjiowekmfoisdjfwokneofosdijfowenfosdifjnweoifnosdifn"; return rtn; } const char * GetHello1() { char rtn[0x500]; strcpy(rtn,"hellodjfldsjfsadlfkja;sldjflaskdjfalksdjfladjflkjsaldkjflaksdfljalsdkjfl;asjdfsadfasdlkjfaldskjf;lajdsflkja;sldjflkdsajflaksjdf;lkjdsafjiowekmfoisdjfwokneofosdijfowenfosdifjnweoifnosdifn"); return rtn; } int main() { printf("%s\n%s\n",GetHello(),GetHello1())<span style="font-family:Arial,Helvetica,sans-serif">;</span> return 0; }
分别用cygwin-gcc和windows-cl编译,生成汇编代码。
cygwin-gcc:
版本截图:
生成汇编代码:
生成对应的汇编代码:
.file "funcParamTest.c" .section .rdata,"dr" .align 4 LC0: .ascii "hellodjfldsjfsadlfkja;sldjflaskdjfalksdjfladjflkjsaldkjflaksdfljalsdkjfl;asjdfsadfasdlkjfaldskjf;lajdsflkja;sldjflkdsajflaksjdf;lkjdsafjiowekmfoisdjfwokneofosdijfowenfosdifjnweoifnosdifn\0" .text .globl _GetHello .def _GetHello; .scl 2; .type 32; .endef _GetHello: pushl %ebp movl %esp, %ebp subl $16, %esp movl $LC0, -4(%ebp) movl -4(%ebp), %eax leave ret .globl _GetHello1 .def _GetHello1; .scl 2; .type 32; .endef _GetHello1: pushl %ebp movl %esp, %ebp subl $1304, %esp movl $187, 8(%esp) movl $LC0, 4(%esp) leal -1288(%ebp), %eax movl %eax, (%esp) call _memcpy leal -1288(%ebp), %eax leave ret .def ___main; .scl 2; .type 32; .endef .section .rdata,"dr" LC1: .ascii "%s\12%s\12\0" .text .globl _main .def _main; .scl 2; .type 32; .endef _main: pushl %ebp movl %esp, %ebp andl $-16, %esp pushl %ebx subl $28, %esp call ___main call _GetHello1 movl %eax, %ebx call _GetHello movl %ebx, 8(%esp) movl %eax, 4(%esp) movl $LC1, (%esp) call _printf movl $0, %eax addl $28, %esp popl %ebx movl %ebp, %esp popl %ebp ret .def _memcpy; .scl 2; .type 32; .endef .def _printf; .scl 2; .type 32; .endef
生成执行文件:
运行执行文件:
windows-cl:版本截图:生成汇编代码同时生成链接文件和执行文件: (几个操作使用一条命令即完成)
生成对应的汇编代码:
TITLE funcParamTest.c .386P include listing.inc if @Version gt 510 .model FLAT else _TEXT SEGMENT PARA USE32 PUBLIC 'CODE' _TEXT ENDS _DATA SEGMENT DWORD USE32 PUBLIC 'DATA' _DATA ENDS CONST SEGMENT DWORD USE32 PUBLIC 'CONST' CONST ENDS _BSS SEGMENT DWORD USE32 PUBLIC 'BSS' _BSS ENDS _TLS SEGMENT DWORD USE32 PUBLIC 'TLS' _TLS ENDS FLAT GROUP _DATA, CONST, _BSS ASSUME CS: FLAT, DS: FLAT, SS: FLAT endif PUBLIC _GetHello _DATA SEGMENT $SG601 DB 'hellodjfldsjfsadlfkja;sldjflaskdjfalksdjfladjflkjsaldkjf' DB 'laksdfljalsdkjfl;asjdfsadfasdlkjfaldskjf;lajdsflkja;sldjflkds' DB 'ajflaksjdf;lkjdsafjiowekmfoisdjfwokneofosdijfowenfosdifjnweoi' DB 'fnosdifn', 00H _DATA ENDS _TEXT SEGMENT _rtn$ = -4 _GetHello PROC NEAR ; 5 : { push ebp mov ebp, esp push ecx ; 6 : char *rtn = "hellodjfldsjfsadlfkja;sldjflaskdjfalksdjfladjflkjsaldkjflaksdfljalsdkjfl;asjdfsadfasdlkjfaldskjf;lajdsflkja;sldjflkdsajflaksjdf;lkjdsafjiowekmfoisdjfwokneofosdijfowenfosdifjnweoifnosdifn"; mov DWORD PTR _rtn$[ebp], OFFSET FLAT:$SG601 ; 7 : return rtn; mov eax, DWORD PTR _rtn$[ebp] ; 8 : } mov esp, ebp pop ebp ret 0 _GetHello ENDP _TEXT ENDS PUBLIC _GetHello1 EXTRN _strcpy:NEAR _DATA SEGMENT ORG $+1 $SG605 DB 'hellodjfldsjfsadlfkja;sldjflaskdjfalksdjfladjflkjsaldkjf' DB 'laksdfljalsdkjfl;asjdfsadfasdlkjfaldskjf;lajdsflkja;sldjflkds' DB 'ajflaksjdf;lkjdsafjiowekmfoisdjfwokneofosdijfowenfosdifjnweoi' DB 'fnosdifn', 00H _DATA ENDS _TEXT SEGMENT _rtn$ = -1280 _GetHello1 PROC NEAR ; 11 : { push ebp mov ebp, esp sub esp, 1280 ; 00000500H ; 12 : char rtn[0x500]; ; 13 : strcpy(rtn,"hellodjfldsjfsadlfkja;sldjflaskdjfalksdjfladjflkjsaldkjflaksdfljalsdkjfl;asjdfsadfasdlkjfaldskjf;lajdsflkja;sldjflkdsajflaksjdf;lkjdsafjiowekmfoisdjfwokneofosdijfowenfosdifjnweoifnosdifn"); push OFFSET FLAT:$SG605 lea eax, DWORD PTR _rtn$[ebp] push eax call _strcpy add esp, 8 ; 14 : return rtn; lea eax, DWORD PTR _rtn$[ebp] ; 15 : } mov esp, ebp pop ebp ret 0 _GetHello1 ENDP _TEXT ENDS PUBLIC _main EXTRN _printf:NEAR _DATA SEGMENT ORG $+1 $SG608 DB '%s', 0aH, '%s', 0aH, 00H _DATA ENDS _TEXT SEGMENT _main PROC NEAR ; 20 : { push ebp mov ebp, esp ; 21 : printf("%s\n%s\n",GetHello(),GetHello1()); call _GetHello1 push eax call _GetHello push eax push OFFSET FLAT:$SG608 call _printf add esp, 12 ; 0000000cH ; 22 : return 0; xor eax, eax ; 23 : } pop ebp ret 0 _main ENDP _TEXT ENDS END
运行执行文件:
分析:
1、之所以定义这么长的字符串,是担心编译器会允许几个字符的越界操作,而意外正常执行。
2、gcc在编译时会对GetHello1产生警告,执行时只打印第一个函数的结果。对比发现,直接定义字符串比先定义再拷贝字符串的操作多了lea操作和_memcpy调用。可能是lea操作导致函数传参时被检测出来是局部变量。而不包含lea操作时,直接引用ascii的地址传出。
3、cl默认不会产生警告,汇编内嵌_strcpy调用,而不是_memcpy。同样调用了lea,但是没有检测出局部变量的传参,两个函数的打印居然都成功了。分别试验了反复100次调用打印、在main和打印之间增加新一层调用函数test、在test内定义const char *test = GetHello1();free(test);在windows-cl仍能‘正常’打印所有的字符串。跟不同的编译器内存调度有关吧。可能cl的运行时在这些条件下都不清空其它函数的局部变量临时内存。而gcc会马上清空。