У меня есть эта функция, которая состоит в основном из встроенного ассемблера.
long *toarrayl(int members, ...){
__asm{
push esp
mov eax, members
imul eax, 4
push eax
call malloc
mov edx, eax
mov edi, eax
xor ecx, ecx
xor esi, esi
loopx:
cmp ecx, members
je done
mov esi, 4
imul esi, ecx
add esi, ebp
mov eax, [esi+0xC]
mov [edi], eax
inc ecx
add edi, 4
jmp loopx
done:
mov eax, edx
pop esp
ret
}
}
И после запуска я получаю нарушение прав доступа по инструкции возврата.
Я использую VC ++ 6, и иногда это может означать указание на строку выше, что возможно на «pop esp».
Если бы вы могли помочь мне, это было бы здорово.
Спасибо, Идомо.
Вы не можете правильно управлять указателем стека. В частности, ваш звонок malloc
разбалансирует стек, и ваш pop esp
в итоге выдает неправильное значение в esp
, Следовательно, нарушение прав доступа происходит при попытке ret
из недопустимого стека (ЦП не может прочитать адрес возврата). Непонятно, почему вы толкаете и высовываетесь esp
; это ничего не дает.
Как вы заметили, вы никогда не должны использовать инструкцию POP ESP — когда вы видите это в коде, вы знаете, что произошло что-то крайне неправильное. Конечно, вызов malloc внутри кода на ассемблере тоже довольно плохая вещь — вы, например, забыли проверить, вернул ли он NULL, так что вы вполне можете потерпеть крах. Помните, что за пределами вашего ассемблера — и проверьте NULL, гораздо проще отладить «Не удалось выделить память в строке 54 в файле mycode.c», чем «Где-то в ассемблере, мы получили
Вот несколько советов по улучшению, которые должны немного ускорить ваш цикл:
long *toarrayl(int members, ...){
__asm{
mov eax, members
imul eax, 4
push eax
call malloc
add esp, 4
mov edx, eax
mov edi, eax
mov ecx, members
lea esi, [ebp+0xc]
loopx:
mov eax, [esi]
mov [edi], eax
add edi, 4
add esi, 4
dec ecx
jnz loopx
mov lret, eax
ret
}
}
Улучшения: Убрать умножение на четыре в каждом цикле. Просто приращение esi
вместо. Используйте декремент в ecx вместо инкремента и загрузите его членами до цикла. Это позволяет использовать только один прыжок в цикле, а не два. Удалить избыточный ход от edx к eax. Используйте eax напрямую.
Я разобрался с ответом самостоятельно.
Для тех, у кого была такая же или похожая проблема:
Фактическое исключение произошло после пользовательского кода, когда vc ++ автоматически выдвигает / восстанавливает регистры в их состояния до вызова функции. Так как я неправильно выравнивал указатель стека при вызове malloc, при извлечении из стека произошло нарушение доступа. Я не смог увидеть это в редакторе, потому что это был не мой код, поэтому он был показан как последний из моего кода в функции.
Чтобы исправить это, просто добавьте add esp (размер параметров для предыдущего звонка) после звонков, которые вы делаете.
Фиксированный код:
long *toarrayl(int members, ...){
__asm{
mov eax, members
imul eax, 4
push eax
call malloc
add esp, 4
mov edx, eax
mov edi, eax
xor ecx, ecx
xor esi, esi
loopx:
cmp ecx, members
je done
mov esi, 4
imul esi, ecx
add esi, ebp
mov eax, [esi+0xC]
mov [edi], eax
inc ecx
add edi, 4
jmp loopx
done:
mov eax, edx
ret
}
//return (long*)0;
}
Оптимизированный код:
long *toarrayl(int members, ...){
__asm{
mov eax, members
shl eax, 2
push eax
call malloc
add esp, 4
;cmp eax, 0
;je _error
mov edi, eax
mov ecx, members
lea esi, [ebp+0xC]
loopx:
mov edx, [esi]
mov [edi], edx
add edi, 4
add esi, 4
dec ecx
jnz loopx
}
}