C/C++与汇编混合编程中的函数声明
该文章根据 CC-BY-4.0 协议发表,转载请遵循该协议。
本文地址:https://fenying.net/post/2014/08/04/missing-reserving-registers-when-mixing-c-and-assembly/
最近写一个C小程序时尝试了下用汇编写一部分代码,封装成函数供 VC (2013)调用。Debug模式一切正常,但是到了Release模式下就直接崩溃了。
举个例子,memcpy 函数原型:
1MemCpyB PROC stdcall, pDst: DWORD, pSrc: DWORD, dwItems: DWORD
2
3 cld
4
5 mov esi, pSrc
6
7 mov edi, pDst
8
9 mov ecx, dwItems
10
11 rep movsb
12
13 mov eax, pDst
14
15 ret
16
17MemCpyB ENDP
一段示例代码:
1#include <stdio.h>
2extern "C" unsigned int __stdcall MemCpyB(void *pDst, void *pSrc, unsigned int uBytes);
3#pragma comment(lib, "mem.lib") /*把汇编代码编译成 lib 使用*/
4int main() {
5 char k[255];
6 MemCpyB(k, "Hello World!", 13);
7 printf("%s\n", k);
8 return 0;
9}
这个函数在静态栈里面工作完全正常,Release 模式也一样。但是如果是动态内存那就不一样了,比如:
1#include <stdio.h>
2extern "C" unsigned int __stdcall MemCpyB(void *pDst, void *pSrc, unsigned int uBytes);
3
4#pragma comment(lib, "mem.lib") /*把汇编代码编译成 lib 使用*/
5
6int main() {
7 char *k;
8 k = (char *)malloc(255);
9 MemCpyB(k, "Hello World!", 13);
10 printf("%s\n", k);
11 free(k);
12 return 0;
13}
查看了下VC的汇编输出,如下图:
1; Listing generated by Microsoft (R) Optimizing Compiler Version 18.00.30501.0
2
3 TITLE E:\Coding\test\test\test.cpp
4 .686P
5 .XMM
6 include listing.inc
7 .model flat
8
9INCLUDELIB OLDNAMES
10
11PUBLIC ??_C@_05CNOPHDHD@?$CF08x?6?$AA@ ; `string'
12PUBLIC ??_C@_0N@GCDOMLDM@Hello?5World?$CB?$AA@ ; `string'
13PUBLIC ??_C@_03OFAPEBGM@?$CFs?6?$AA@ ; `string'
14EXTRN _MemCpyB@12:PROC
15EXTRN __imp__printf:PROC
16EXTRN __imp__malloc:PROC
17EXTRN __imp__free:PROC
18; COMDAT ??_C@_03OFAPEBGM@?$CFs?6?$AA@
19CONST SEGMENT
20??_C@_03OFAPEBGM@?$CFs?6?$AA@ DB '%s', 0aH, 00H ; `string'
21CONST ENDS
22; COMDAT ??_C@_0N@GCDOMLDM@Hello?5World?$CB?$AA@
23CONST SEGMENT
24??_C@_0N@GCDOMLDM@Hello?5World?$CB?$AA@ DB 'Hello World!', 00H ; `string'
25CONST ENDS
26; COMDAT ??_C@_05CNOPHDHD@?$CF08x?6?$AA@
27CONST SEGMENT
28??_C@_05CNOPHDHD@?$CF08x?6?$AA@ DB '%08x', 0aH, 00H ; `string'
29CONST ENDS
30PUBLIC _main
31; Function compile flags: /Ogtp
32; File e:\coding\test\test\test.cpp
33; COMDAT _main
34_TEXT SEGMENT
35_main PROC ; COMDAT
36
37; 8 : int main() {
38
39 00000 56 push esi
40 00001 57 push edi
41
42; 9 : char *k;
43; 10 : k = (char *)malloc(255);
44
45 00002 68 ff 00 00 00 push 255 ; 000000ffH
46 00007 ff 15 00 00 00
47 00 call DWORD PTR __imp__malloc
48
49; 11 : printf("%08x\n", k);
50
51 0000d 8b 35 00 00 00
52 00 mov esi, DWORD PTR __imp__printf
53 00013 8b f8 mov edi, eax
54 00015 57 push edi
55 00016 68 00 00 00 00 push OFFSET ??_C@_05CNOPHDHD@?$CF08x?6?$AA@
56 0001b ff d6 call esi
57 0001d 83 c4 0c add esp, 12 ; 0000000cH
58
59; 12 : MemCpyB(k, "Hello World!", 13);
60
61 00020 6a 0d push 13 ; 0000000dH
62 00022 68 00 00 00 00 push OFFSET ??_C@_0N@GCDOMLDM@Hello?5World?$CB?$AA@
63 00027 57 push edi
64 00028 e8 00 00 00 00 call _MemCpyB@12
65
66; 13 : printf("%08x\n", k);
67
68 0002d 57 push edi
69 0002e 68 00 00 00 00 push OFFSET ??_C@_05CNOPHDHD@?$CF08x?6?$AA@
70 00033 ff d6 call esi
71
72; 14 : printf("%s\n", k);
73
74 00035 57 push edi
75 00036 68 00 00 00 00 push OFFSET ??_C@_03OFAPEBGM@?$CFs?6?$AA@
76 0003b ff d6 call esi
77
78; 15 : free(k);
79
80 0003d 57 push edi
81 0003e ff 15 00 00 00
82 00 call DWORD PTR __imp__free
83 00044 83 c4 14 add esp, 20 ; 00000014H
84
85; 16 : return 0;
86
87 00047 33 c0 xor eax, eax
88 00049 5f pop edi
89 0004a 5e pop esi
90
91; 17 : }
92
93 0004b c3 ret 0
94_main ENDP
95_TEXT ENDS
96END
Release版本的代码是经过优化的,所以很多地方已经看不出原来的痕迹了。但是注意到一个很关键的东西,那就是edi, esi寄存器的使用。在我的函数里也用到了它,显然修改了它们的值又没有恢复回去,于是程序本身的节奏就错乱了。这些寄存器都应该在函数入口处保护好,如下:
1MemCpyB PROC stdcall USES edi esi ecx , pDst: DWORD, pSrc: DWORD, dwItems: DWORD
2
3 cld
4
5 mov esi, pSrc
6
7 mov edi, pDst
8
9 mov ecx, dwItems
10
11 rep movsb
12
13 mov eax, pDst
14
15 ret
16
17MemCpyB ENDP
comments powered by Disqus