C/C++与汇编混合编程中的函数声明
该文章迁移自作者的旧博客站点。
源地址:http://fenying.blog.163.com/blog/static/102055993201474111228639/。
源地址:http://fenying.blog.163.com/blog/static/102055993201474111228639/。
最近写一个C小程序时尝试了下用汇编写一部分代码,封装成函数供 VC (2013)调用。Debug模式一切正常,但是到了Release模式下就直接崩溃了。
举个例子,memcpy 函数原型:
MemCpyB PROC stdcall, pDst: DWORD, pSrc: DWORD, dwItems: DWORD
cld
mov esi, pSrc
mov edi, pDst
mov ecx, dwItems
rep movsb
mov eax, pDst
ret
MemCpyB ENDP
一段示例代码:
#include <stdio.h>
extern "C" unsigned int __stdcall MemCpyB(void *pDst, void *pSrc, unsigned int uBytes);
#pragma comment(lib, "mem.lib") /*把汇编代码编译成 lib 使用*/
int main() {
char k[255];
MemCpyB(k, "Hello World!", 13);
printf("%s\n", k);
return 0;
}
这个函数在静态栈里面工作完全正常,Release 模式也一样。但是如果是动态内存那就不一样了,比如:
#include <stdio.h>
extern "C" unsigned int __stdcall MemCpyB(void *pDst, void *pSrc, unsigned int uBytes);
#pragma comment(lib, "mem.lib") /*把汇编代码编译成 lib 使用*/
int main() {
char *k;
k = (char *)malloc(255);
MemCpyB(k, "Hello World!", 13);
printf("%s\n", k);
free(k);
return 0;
}
查看了下VC的汇编输出,如下图:
; Listing generated by Microsoft (R) Optimizing Compiler Version 18.00.30501.0
TITLE E:\Coding\test\test\test.cpp
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB OLDNAMES
PUBLIC ??_C@_05CNOPHDHD@?$CF08x?6?$AA@ ; `string'
PUBLIC ??_C@_0N@GCDOMLDM@Hello?5World?$CB?$AA@ ; `string'
PUBLIC ??_C@_03OFAPEBGM@?$CFs?6?$AA@ ; `string'
EXTRN _MemCpyB@12:PROC
EXTRN __imp__printf:PROC
EXTRN __imp__malloc:PROC
EXTRN __imp__free:PROC
; COMDAT ??_C@_03OFAPEBGM@?$CFs?6?$AA@
CONST SEGMENT
??_C@_03OFAPEBGM@?$CFs?6?$AA@ DB '%s', 0aH, 00H ; `string'
CONST ENDS
; COMDAT ??_C@_0N@GCDOMLDM@Hello?5World?$CB?$AA@
CONST SEGMENT
??_C@_0N@GCDOMLDM@Hello?5World?$CB?$AA@ DB 'Hello World!', 00H ; `string'
CONST ENDS
; COMDAT ??_C@_05CNOPHDHD@?$CF08x?6?$AA@
CONST SEGMENT
??_C@_05CNOPHDHD@?$CF08x?6?$AA@ DB '%08x', 0aH, 00H ; `string'
CONST ENDS
PUBLIC _main
; Function compile flags: /Ogtp
; File e:\coding\test\test\test.cpp
; COMDAT _main
_TEXT SEGMENT
_main PROC ; COMDAT
; 8 : int main() {
00000 56 push esi
00001 57 push edi
; 9 : char *k;
; 10 : k = (char *)malloc(255);
00002 68 ff 00 00 00 push 255 ; 000000ffH
00007 ff 15 00 00 00
00 call DWORD PTR __imp__malloc
; 11 : printf("%08x\n", k);
0000d 8b 35 00 00 00
00 mov esi, DWORD PTR __imp__printf
00013 8b f8 mov edi, eax
00015 57 push edi
00016 68 00 00 00 00 push OFFSET ??_C@_05CNOPHDHD@?$CF08x?6?$AA@
0001b ff d6 call esi
0001d 83 c4 0c add esp, 12 ; 0000000cH
; 12 : MemCpyB(k, "Hello World!", 13);
00020 6a 0d push 13 ; 0000000dH
00022 68 00 00 00 00 push OFFSET ??_C@_0N@GCDOMLDM@Hello?5World?$CB?$AA@
00027 57 push edi
00028 e8 00 00 00 00 call _MemCpyB@12
; 13 : printf("%08x\n", k);
0002d 57 push edi
0002e 68 00 00 00 00 push OFFSET ??_C@_05CNOPHDHD@?$CF08x?6?$AA@
00033 ff d6 call esi
; 14 : printf("%s\n", k);
00035 57 push edi
00036 68 00 00 00 00 push OFFSET ??_C@_03OFAPEBGM@?$CFs?6?$AA@
0003b ff d6 call esi
; 15 : free(k);
0003d 57 push edi
0003e ff 15 00 00 00
00 call DWORD PTR __imp__free
00044 83 c4 14 add esp, 20 ; 00000014H
; 16 : return 0;
00047 33 c0 xor eax, eax
00049 5f pop edi
0004a 5e pop esi
; 17 : }
0004b c3 ret 0
_main ENDP
_TEXT ENDS
END
Release版本的代码是经过优化的,所以很多地方已经看不出原来的痕迹了。但是注意到一个很关键的东西,那就是edi, esi寄存器的使用。在我的函数里也用到了它,显然修改了它们的值又没有恢复回去,于是程序本身的节奏就错乱了。这些寄存器都应该在函数入口处保护好,如下:
MemCpyB PROC stdcall USES edi esi ecx , pDst: DWORD, pSrc: DWORD, dwItems: DWORD
cld
mov esi, pSrc
mov edi, pDst
mov ecx, dwItems
rep movsb
mov eax, pDst
ret
MemCpyB ENDP
comments powered by Disqus