Visual Studio内嵌汇编转独立汇编过程后计数异常排查求助
原有内嵌汇编的C++程序功能正常,将其重构为独立的__stdcall调用约定汇编过程后出现计数错误,怀疑问题出在loop_2循环中,但无法定位。
可正常运行的内嵌汇编代码
#include <iostream> int main() { const int N = 5; // количество элементов для подсчета количества значений int raznost = N / 2; // рассчитывается смещение для отрицательных чисел int arr[] = { 1,2,2 }; int freq[N] = { 0 }; int n = sizeof(arr) / sizeof(arr[0]); int* result = new int[n]; _asm { xor eax, eax xor ebx, ebx xor ecx, ecx lea eax, arr lea ebx, freq mov edx, raznost imul edx, 4 add ebx, edx loop_1 : cmp ecx, n je loopend_1 mov edx, [eax + ecx * 4] inc[ebx + edx * 4] inc ecx jmp loop_1 loopend_1 : lea ebx, freq xor edx, edx // для количества элементов в новом массиве xor ecx, ecx outer_loop : cmp ecx, n je outer_loopend push ecx xor ecx, ecx // счетчик xor eax, eax // максимальное значение xor esi, esi // количество mov eax, 0 loop_2: cmp ecx, N je loopend_2 cmp esi, [ebx + ecx * 4] jg end_if mov esi, [ebx + ecx * 4] mov eax, ecx end_if : inc ecx jmp loop_2 loopend_2 : xor ecx, ecx push ebx mov ebx, eax push ebx mov ebx, result sub eax, raznost loop_3 : cmp esi, ecx je loopend_3 mov[ebx + edx * 4], eax inc edx inc ecx jmp loop_3 loopend_3: pop ebx mov ecx, ebx pop ebx push eax mov eax, 0 mov[ebx + ecx * 4], eax pop eax pop ecx inc ecx jmp outer_loop outer_loopend: } for (int i = 0; i < n; i++) { std::cout << result[i] << " "; } return 0; }
运行异常的独立汇编代码
C++调用部分
#include <iostream> using namespace std; extern "C" int __stdcall Proc1(int* arr, int* freq, int raznost, int n, int N, int* result); int main() { const int N = 5;//2000; // количество элементов для подсчета количества значений int raznost = N / 2; // рассчитывается смещение для отрицательных чисел int arr[] = { 1,1,2}; int freq[N] = { 0 }; int n = sizeof(arr) / sizeof(arr[0]); int* result = new int[n]; Proc1(arr, freq, raznost, n, N, result); for (int i = 0; i < n; i++) { std::cout << result[i] << " "; } return 0; }
汇编实现部分
.386 PUBLIC _Proc1@24 .model flat .data .code _Proc1@24 proc push ebp mov ebp, esp xor eax, eax xor ebx, ebx xor ecx, ecx xor edx, edx mov eax, [ebp + 8] mov ebx, [ebp + 12] mov edx, [ebp + 16] imul edx, 4 add ebx, edx loop_1 : cmp ecx, [ebp + 20] je loopend_1 mov edx, [eax + ecx * 4] inc dword ptr [ebx + edx * 4] inc ecx jmp loop_1 loopend_1 : xor edx, edx ; для количества элементов в новом массиве xor ecx, ecx outer_loop : cmp ecx, [ebp + 20] je outer_loopend push ecx xor ecx, ecx ; счетчик xor eax, eax ; максимальное значение xor esi, esi ; количество mov eax, 0 loop_2: cmp ecx, [ebp + 24] je loopend_2 cmp esi, [ebx + ecx * 4] jg end_if mov esi, [ebx + ecx * 4] mov eax, ecx end_if : inc ecx jmp loop_2 loopend_2 : xor ecx, ecx push ebx mov ebx, eax push ebx mov ebx, [ebp + 28] sub eax, edx loop_3 : cmp esi, ecx je loopend_3 mov[ebx + edx * 4], eax inc edx inc ecx jmp loop_3 loopend_3: pop ebx mov edi, ebx pop ebx push eax mov eax, 0 mov [ebx + edi * 4], eax pop eax pop ecx inc ecx jmp outer_loop outer_loopend: mov esp,ebp pop ebp ret 24 _Proc1@24 endp end
错误排查结果
loop_1结束后未恢复ebx指向freq初始地址
内嵌汇编中loopend_1后有lea ebx, freq,将ebx重置为freq数组的起始地址;但独立汇编中没有这一步,此时ebx还是loop_1中偏移后的地址(ebx + raznost*4),导致后续loop_2访问的不是原始freq数组,而是偏移后的错误位置。
修复:在loopend_1后添加mov ebx, [ebp + 12],重新加载freq的初始地址到ebx。loop_3中误用edx代替raznost
内嵌汇编中sub eax, raznost用于还原原始数值,但独立汇编中写成了sub eax, edx,此时edx是用于统计新数组元素数量的变量,不是raznost。需要提前保存raznost的值:
修复:在函数开头将raznost存入非易失性寄存器(比如edi),例如:mov edi, [ebp + 16] ; 保存raznost到edi然后在loop_3中改为
sub eax, edi。寄存器保存不符合__stdcall约定
__stdcall要求函数保存非易失性寄存器(ebp, ebx, esi, edi),当前代码中修改了ebx、esi、edi但未保存,可能导致调用者环境被破坏。
修复:在函数开头添加push ebx、push esi、push edi,在函数结束前对应pop edi、pop esi、pop ebx。未初始化edi却直接使用
代码中mov edi, ebx使用了edi,但edi未初始化,需要确保寄存器使用前正确赋值或保存。
内容的提问来源于stack exchange,提问作者Zarzd

