сбой на инструкции fpu и код asm не работают - PullRequest
0 голосов
/ 29 января 2012

извините за мой слабый английский

Я пытаюсь улучшить свои способности АСМ, и я нашел легко Точка входа в работу с ним с помощью машинного кода из кода c

Я использую его таким образом

    char asmRoutineData2[]  =
    {
    0xC8, 0x00, 0x00, 0x00,         // enter 0, 0
    0xB8, 0xff, 0x00 ,0x00 ,0x00,    // mov eax, 65538
    0xC9,                           // leave
    0xc3                            // ret
    };

 int (*asmRoutine)(void) = (int (*)(void)) asmRoutineData;
 int ret = asmRoutine();

и он отлично работает для некоторых подпрограмм - например, выше

некоторые другие не работают:

1) У меня возникла проблема, и я не могу получить значение, переданное стеком

такая процедура

    char asmRoutine_body[]  =
    {

    0xC8, 0x00, 0x00, 0x00,      //enter
    0x8B, 0x45, 0x08,          // mov eax, [ebp+8]
    0xC9,               //leave
    0xC3
    };

и

 int ( *asmRoutine)(int, int, int) = ( int (*)(int, int, int)) asmRoutine_body;
 int ret = asmRoutine(77,66,55);

должно работать, насколько я знаю, но это не

Я посмотрел в asm, сгенерированном компилятором, и, похоже, это правильно

mov       eax,offset _asmRoutineData
push      55
push      66
push      77
call      eax
add       esp,12  


    _asmRoutineData label byte
db  200     //enter
db  0
db  0
db  0       
db  139     //  mov  eax, dword [ebp+8H]  ; 8B. 45, 08
db  69
db  8
db  201       //leave
db  195        //ret

не знаю, что не так (возвращает значения, отличные от моих ожидаемых 77 (или 66 или 55 для ebp + 12 ebp + 16)

2) Вторая проблема в том, что этот способ вызова машинного кода работает для арифметических инструкций от меня, но это не работает приложение (какой-то способ системного исключения) в инструкциях fpu или sse

почему? и что я должен сделать, чтобы это работало на меня (я хотел бы написать сборку рутины таким образом)

ель

// EDIT

это стандартная процедура, которая должна получить float4 * вектор a и b сделать скалярное произведение и поместить результат в float4 * c (float4 - это структура или таблица из 4 чисел с плавающей запятой)

(странный случай, он должен получить два вектора и вернуть число с плавающей точкой по eax, но я получил, если форма Интернет возможно, и не было ни минуты протестируйте и перепишите его)

    /*
    enter   0, 0                                    ; 0034 _ C8, 0000, 00
    mov     eax, dword [ebp+8H]                     ; 0038 _ 8B. 45, 08
    mov     ebx, dword [ebp+0CH]                    ; 003B _ 8B. 5D, 0C
    mov     ecx, dword [ebp+10H]                    ; 003E _ 8B. 4D, 10
    movups  xmm0, oword [eax]                       ; 0041 _ 0F 10. 00
    movups  xmm1, oword [ebx]                       ; 0044 _ 0F 10. 0B
    mulps   xmm0, xmm1                              ; 0047 _ 0F 59. C1
    movhlps xmm1, xmm0                              ; 004A _ 0F 12. C8
    addps   xmm1, xmm0                              ; 004D _ 0F 58. C8
    movaps  xmm0, xmm1                              ; 0050 _ 0F 28. C1
    shufps  xmm1, xmm1, 1                           ; 0053 _ 0F C6. C9, 01
    addss   xmm0, xmm1                              ; 0057 _ F3: 0F 58. C1
    movss   dword [ecx], xmm0                       ; 005B _ F3: 0F 11. 01
    leave                                           ; 005F _ C9
    ret                                             ; 0060 _ C3
    */

    char asmDot_body[] =
    {
     0xC8, 0x00, 0x00, 0x00,

     0x8B, 0x45, 0x08,
     0x8B, 0x5D, 0x0C,
     0x8B, 0x4D, 0x10,

     0x0F, 0x10, 0x00,
     0x0F, 0x10, 0x0B,

     0x0F, 0x59, 0xC1,
     0x0F, 0x12, 0xC8,
     0x0F, 0x58, 0xC8,
     0x0F, 0x28, 0xC1,
     0x0F, 0xC6, 0xC9, 0x01,
     0xF3, 0x0F, 0x58, 0xC1,
     0xF3, 0x0F, 0x11, 0x01,
     0xC9,
     0xC3
     };


    void (*asmAddSSE)(float4*, float4*, float4*) = (void (*)(float4*, float4*, float4*)) asmDot_body;

    float4 a = {1,2,1,0};
    float4 b = {1,2,3,0};
    float4 c = {0,0,0,0};

    asmAddSSE(&a,&b,&c);

// РЕДАКТИРОВАТЬ L8R

НАЙДЕНО! и это работает очень круто и здорово (передача аргументов, а также fpu и даже sse) Я счастлив

tnx necrolis для подтверждения того, что он работает в вашей системе,

Я начал пробовать с переключателями компилятора, чтобы настроить выравнивание и также отключите некоторые из них, и это был -pr (используйте fastcall), который был выключил и я должен выключить его

(получил два файла compile.bat - один для обычной компиляции и второй для генерации olso и без ключа -pr в второй, так что ассемблерный код, который я написал выше, хорошо - но мой нормальный compile.bat сгенерировал вызовы fastcall, и это стало глупостью!)

1 Ответ

4 голосов
/ 29 января 2012

Ваша первая проблема в том, что вы предполагаете, что код является исполняемым, если вам повезло, DEP выключен, и вы можете выполнять код из своего стека, но обычно (99,99% времени) вам нужно выделить исполняемую память для этого. Во-вторых, написание чистого машинного кода, как вы делаете, ужасно и подвержено ошибкам. Если вы чувствуете, что не можете использовать встроенный ассемблер, предоставляемый вашим компилятором, используйте что-то вроде AsmJIT (или любой другой ассемблер памяти).

Тем не менее, ваш код работает нормально (при вызове с использованием __cdecl), когда после устранения этих проблем он все же небезопасен. (Я запустил его и получил ожидаемый результат 77, после , поместив его в исполняемую память). Скорее всего, в будущем вы столкнетесь с проблемами, связанными с исправлением виртуальных и абсолютных вызовов / длинных прыжков, что усложнит задачу на 1010 * больше .

Ваши сбои в инструкциях FPU и SSE, скорее всего, связаны с проблемами выравнивания, но невозможно определить без системного кода, вашей сборки или того, какой процессор вы используете, и в подобных случаях лучше всего использовать отладчик, например как ollydbg (который бесплатный ) и пошаговый код.


полукорректированный код:

static char asmRoutine_body[]  =
{

0xC8, 0x00, 0x00, 0x00,      //enter
0x8B, 0x45, 0x08,          // mov eax, [ebp+8]
0xC9,               //leave
0xC3
};

void* p = (void*)VirtualAlloc(NULL,sizeof(asmRoutine_body),MEM_COMMIT,PAGE_EXECUTE_READWRITE);
memcpy(p,asmRoutine_body,sizeof(asmRoutine_body));
int ( *asmRoutine)(int, int, int) = ( int (*)(int, int, int))p;
int ret = asmRoutine(77,66,55);
VirtualFree(p,sizeof(asmRoutine_body),MEM_RELEASE);
printf("%d\n",ret);

выходы: 77

...