Форум программистов, компьютерный форум, киберфорум
Наши страницы

Assembler для начинающих

Войти
Регистрация
Восстановить пароль
 
 
Saraharas
0 / 0 / 0
Регистрация: 14.10.2014
Сообщений: 53
#1

Ассемблерная вставка на Си, ошибка Run-Time check failure #0 - Assembler

01.06.2015, 00:05. Просмотров 1064. Ответов 22
Метки нет (Все метки)

Здравствуйте! Помогите, пожалуйста, с реализацией ассемблерной вставки в Си. У меня есть программа на Си, нужно написать такую же программу в виде ассемблерной вставки (использую ассемблерный листинг сишной версии). Компилируется без ошибок, но потом выдает ошибку "Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention" Подскажите, пожалуйста, как исправить ошибку. Прилагаю Сишную программу и ее вариант на ассемблере
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<memory.h>
#include <time.h>
 
const int max_sqrt_n = 100000;
const int S = 100000;
bool nprime[max_sqrt_n], bloc[S];
int primes[max_sqrt_n], cnt;
 
int main()
{
    int n;
    scanf("%d", &n);
    clock_t start_time;
    start_time = clock();
    int nsqrt = (int)sqrt((double)n);
    for (int i = 2; i <= nsqrt; ++i)
        if (!nprime[i])
        {
            primes[cnt++] = i;
            if (i <= n)
                for (int j = i * i; j <= nsqrt; j += i)
                    nprime[j] = true;
        }
    int result = 0;
    for (int k = 0, maxk = n / S; k <= maxk; ++k)
    {
        memset(bloc, 0, sizeof bloc);
        int start = k * S;
        for (int i = 0; i < cnt; ++i)
        {
            int start_idx = (start + primes[i] - 1) / primes[i];
            int temp = start_idx < 2 ? 2 : start_idx;
            int j = temp * primes[i] - start;
            for (; j < S; j += primes[i]) bloc[j] = true;
        }
        if (k == 0) bloc[0] = bloc[1] = true;
        for (int i = 0; i < S && start + i <= n; ++i) if (!(bloc[i])) ++result;
    }
    start_time = clock() - start_time;
    printf("%d %.4lfs", result, (double)start_time / CLOCKS_PER_SEC);
    return 0;
}
Ассемблерный вариант:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#include<stdlib.h>
#include<stdio.h>
#include<math.h>
#include <time.h>
#include <memory.h>
#include <stdbool.h>
#pragma function(memset)
const int max_sqrt_n = 100000;
const int S = 100000;
bool nprime[max_sqrt_n];
bool bloc[S];
int primes[max_sqrt_n], cnt;
const char fmt[] = "%d";
const char fmt1[] = " %.4lfs";
 
int main()
{
    long long int c = CLOCKS_PER_SEC;
    int n, nsqrt, k, maxk;
    int result = 0;
    clock_t start_time;
 
    _asm
    {
        lea eax, n
            push    eax
            push    OFFSET fmt
            call    DWORD PTR scanf
            add esp, 8
 
            call  dword ptr clock
            mov start_time, eax
 
            finit
            fild n
            fsqrt
            fist nsqrt
            mov edx, nsqrt
            mov ecx, 2
            cmp edx, ecx
            jl  LN57
            mov edi, cnt
 
        LL22 : cmp  nprime[ecx], 0
               jne  LN21
 
               mov  eax, ecx
               mov  primes[edi * 4], ecx
               imul eax, ecx
               inc  edi
               cmp  eax, edx
               jg   LN21
 
 
               mov  eax, ecx
               imul eax, ecx
               cmp  eax, edx
               jg   LN21
 
           LL17 :
 
        mov nprime[eax], 1
            add eax, ecx
            cmp eax, edx
            jle LL17
 
        LN21 :
 
        inc ecx
            cmp ecx, edx
            jle LL22
 
 
            mov cnt, edi
            mov edi, n
 
        LN57 :
        mov eax, 351843721
            xor esi, esi
            imul    edi
            mov ebx, esi
            mov result, esi
            sar edx, 13
            mov eax, edx
            mov k, ebx
            shr eax, 31
            add eax, edx
            mov maxk, eax
            js  LN12
 
        LL14 :
        push    100000
            push    0
            push offset bloc
            call memset
            xor edi, edi
            imul    ebx, ebx, 100000
            add esp, 12
            cmp cnt, edi
            jle LN9
            mov esi, cnt
 
        LL11 :
 
        mov ecx, primes[edi * 4]
            lea eax, [ebx - 1]
            add eax, ecx
            cdq
            idiv    ecx
 
            mov edx, 2
            cmp eax, 2
            cmovl   eax, edx
 
            imul    eax, ecx
            sub eax, ebx
            cmp eax, 100000
            jge LN10
 
        LL8 :
        mov bloc[eax], 1
            add eax, ecx
            cmp eax, 100000
            jl  LL8
        LN10 :
        inc edi
            cmp edi, esi
            jl  LL11
            mov esi, result
 
        LN9 :
        cmp k, 0
            jne LN5
            mov bloc, 257
        LN5 :
            mov edx, n
            mov edi, ebx
            xor ecx, ecx
            sub edi, OFFSET bloc
 
        LL4 :
        lea eax, bloc[ecx]
            add eax, edi
            cmp eax, edx
            jg  LN13
            cmp bloc[ecx], 0
            jne LN3
            inc esi
        LN3 :
        lea eax, [ebx + 1]
            add eax, ecx
            cmp eax, edx
            jg  LN13
            cmp bloc[ecx + 1], 0
            jne LN43
            inc esi
        LN43 :
        lea eax, [ebx + 2]
            add eax, ecx
            cmp eax, edx
            jg LN13
            cmp bloc[ecx + 2], 0
            jne LN45
            inc esi
        LN45 :
        lea eax, [ebx + 3]
            add eax, ecx
            cmp eax, edx
            jg  LN13
            cmp bloc[ecx + 3], 0
            jne LN47
            inc esi
        LN47 :
        lea eax, [ebx + 4]
            add eax, ecx
            cmp eax, edx
            jg LN13
            cmp bloc[ecx + 4], 0
            jne LN49
            inc esi
        LN49 :
        add ecx, 5
            cmp ecx, 100000
            jl  LL4
 
        LN13 :
        mov ebx, k
            inc ebx
            mov result, esi
            mov k, ebx
            cmp ebx, maxk
            jle LL14
 
        LN12 :
 
        call    DWORD PTR clock
            mov ebx, start_time
            sub eax, ebx
            sub esp, 8
            mov start_time, eax
            fild    dword ptr start_time
            fdiv    dword ptr c
            fstp    QWORD PTR[esp]
            push     offset fmt
            call    DWORD PTR printf
            sub esi, 1
            push    esi
            push    OFFSET fmt1
            call    DWORD PTR printf
            add esp, 16
    }
 
    return 0;
}
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
01.06.2015, 00:05
Я подобрал для вас темы с готовыми решениями и ответами на вопрос Ассемблерная вставка на Си, ошибка Run-Time check failure #0 (Assembler):

Run-time check failure #0 - Assembler
Добрый день. Как понимаю, ошибка возникла вследствие неверного esp. Ниже используемый код. После выхода из фции asm вылетает Run-Time Check...

ассемблерная вставка - Assembler
Здравствуйте, в задании нужно было написать код на С и сделать ассемблерную вставку. &quot;Дана длина отрезка в метрах,...

Ассемблерная вставка на Си - Assembler
Задана следующая задача : дана длина отрезка в метрах, сантиметрах,миллиметрах. Перевести в футы и дюймы. 1 дюйм = 2,54 см. 1 фут =...

Ассемблерная вставка, С++ - Assembler
Доброе утро. Достался &quot;по наследству&quot; код __declspec(dllexport) int __stdcall NtUserPostMes(HWND hWnd, unsigned int Msg, WPARAM...

ассемблерная вставка - Assembler
здравствуйте, подскажите пожалуйста алгоритм для моей задачи: нужно написать в порядке возрастания числа, в двоичном представлении которых...

Ассемблерная вставка в C++ - Assembler
Приветствую,нужен направляющий пинок по решению задачки) задача в следующем Разделить массив на две части, поместив в первую эле-менты,...

22
Ethereal
3703 / 1342 / 178
Регистрация: 17.02.2013
Сообщений: 2,253
03.06.2015, 07:39 #2
Не компилируется твоя программа Borland C++, поэтому на глаз :
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
            call    DWORD PTR clock      ;Параметров не имеет, результат в eax
            mov ebx, start_time
            sub eax, ebx                     ;eax <- clock()-start_time в целых числах
            sub esp, 8                        ;Место в стеке для переменной типа double
            mov start_time, eax           ;start_time <- clock()-start_time
            fild    dword ptr start_time
            fdiv    dword ptr c
            fstp    QWORD PTR[esp]      ;Результат типа double в стеке. <--- После PTR пробел же !!!
            push     offset fmt              ;<--------- Неправильно !!! Должно быть offset fmt1
            call    DWORD PTR printf
            sub esi, 1                          ;<--------- Это еще зачем ??? Тут по смыслу должно быть add esp,12
            push    esi
            push    OFFSET fmt1           ;<--------- Неправильно !!! Должно быть offset fmt
            call    DWORD PTR printf
            add esp, 16                       ;<--------- Что-то не то !!! Тут по смыслу должно быть add esp,8
В принципе
add esp, 12
и
add esp, 8
можно объединить в один
add esp, 20
напоследок. Но 20 !!!, но не 16-же, как у тебя.

Добавлено через 3 минуты
И еще. В сишном коде было
Цитата Сообщение от Saraharas Посмотреть сообщение
printf("%d %.4lfs", result, (double)start_time / CLOCKS_PER_SEC);
а в ассемблерном коде станет
printf(" %.4lfs%d", (double)start_time / CLOCKS_PER_SEC, result);
после моих исправлений.
Так-что там еще в форматных строках пробел перекинуть.

А у тебя было (double) в паре с "%d" и result-1 (почему-то минус один, хотя в Си-шном коде этого
нет) в паре с " %.4lfs" Т.е. фрагменты как-то переставлены были. Бессмысленно переставлены.
1
Charles Kludge
Клюг
7641 / 3156 / 366
Регистрация: 03.05.2011
Сообщений: 8,382
03.06.2015, 07:49 #3
Цитата Сообщение от Ethereal Посмотреть сообщение
Не компилируется твоя программа Borland C++
На Watcom'e компилируется, но ошибки уже в первых строчках:
Assembler
1
2
3
4
5
6
7
8
    _asm
    {
            push    OFFSET fmt
        lea eax, n
            push    eax
//            push    OFFSET fmt
            call    scanf
            add esp, 8
— перепутаны параметры, dword ptr не нужен, компайлер сам сгенерит правильный вызов. Дальше ковырять лень.
0
Ethereal
3703 / 1342 / 178
Регистрация: 17.02.2013
Сообщений: 2,253
03.06.2015, 09:30 #4
Насчет того, что DWORD PTR при call не нужен, тут я согласен, т.к. call на функцию по идее прямой, но никак не косвенный. А вот насчет перепутанности параметров тут меня сомнения взяли. По идее в Си параметры пихаются в стек в порядке справа налево. Поэтому указатель на форматную строку должена пихаться в стек последним.
Вот смотри. Borland C++ компилирует вызов scanf("%d",&i) ; так :
Assembler
1
2
3
4
5
6
7
8
9
   ;    scanf("%d",&i) ;
   ;    
    push      offset _i
    push      offset s@
    call      _scanf
    add       esp,8
...
s@  label   byte
    db  "%d",0
А Watcom C , что , делает наоборот ?
1
Charles Kludge
Клюг
7641 / 3156 / 366
Регистрация: 03.05.2011
Сообщений: 8,382
03.06.2015, 10:45 #5
Цитата Сообщение от Ethereal Посмотреть сообщение
А Watcom C , что , делает наоборот ?
Да нет:
Assembler
1
2
3
4
5
6
7
;     scanf(fmt,&n);
    lea         eax,-18H[ebp] 
    push        eax 
    mov         ebx,dword ptr FLAT:fmt 
    push        ebx 
    call        near ptr FLAT:scanf 
    add         esp,8
Но сомнение закралось после того, как я переставил параметры и Генерал Фаулт при вызове scanf'a исчез.
0
maxillion
273 / 183 / 38
Регистрация: 25.12.2012
Сообщений: 616
03.06.2015, 12:52 #6
Тут де же проверять не надо, ошибки банальны
1. Нет контроля фрейма стэка. Если делаете вставку на асме то выделяйте для неё отдельную функцию.
2. Нужно восстанавливать регистры ESI, EDI, EBX, EBP.
0
Ethereal
3703 / 1342 / 178
Регистрация: 17.02.2013
Сообщений: 2,253
03.06.2015, 13:18 #7
Цитата Сообщение от maxillion Посмотреть сообщение
1. Нет контроля фрейма стэка.
А это что за контроль такой ? Как этот фрейм контролируется ? И какой именно фрейм ?
1
Mikl___
Автор FAQ
11208 / 5866 / 523
Регистрация: 11.11.2010
Сообщений: 10,893
03.06.2015, 13:23 #8
Ethereal,
  • с каким значением ESP ассемблерная вставка зашла в тело СИ-программы с таким и должна выйти
  • и, желательно, сохранить регистр флагов и перед выходом восстановить его...
0
Ethereal
3703 / 1342 / 178
Регистрация: 17.02.2013
Сообщений: 2,253
03.06.2015, 13:53 #9
Цитата Сообщение от Mikl___ Посмотреть сообщение
с каким значением ESP ассемблерная вставка зашла в тело СИ-программы с таким и должна выйти
Это я знаю. Только как именно что-то тут контролировать ?
Если все положенные на стек значения извлекаются (а если код не содержит ошибок, то это так и есть), то никакой отдельный контроль не нужен. А если не все значения извлекаются, то программа содержит ошибки работы со стеком и контроль опять не нужен, поскольку крах программы тогда все равно неизбежен и какая разница каким именно он будет ?

Добавлено через 20 минут
Цитата Сообщение от maxillion Посмотреть сообщение
2. Нужно восстанавливать регистры ESI, EDI, EBX, EBP.
Это кстати вопрос, нужно ли это, если ассемблерная вставка делается прямо в main()
Вернее, является ли это необходимым.
1
maxillion
273 / 183 / 38
Регистрация: 25.12.2012
Сообщений: 616
03.06.2015, 13:59 #10
Цитата Сообщение от Ethereal Посмотреть сообщение
Это кстати вопрос, нужно ли это, если ассемблерная вставка делается прямо в main()
Main это не точка входа в программу, если вы про это.
Как вы думаете такой код будет работать правильно ?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void __stdcall pr(int a, int b)
{
    printf("a - %d; b - %d ",a,b);
}
int _tmain(int argc, _TCHAR* argv[])
{   
    int a = 5;
    int b = 6;
    __asm
    {
        push a
        push b
        call pr     
    }
    return 0;
}
0
Ethereal
3703 / 1342 / 178
Регистрация: 17.02.2013
Сообщений: 2,253
03.06.2015, 13:59 #11
Не знаю что такое _tmain
Если main , то никакого криминала в коде кроме перепутанных a и b нет.
1
maxillion
273 / 183 / 38
Регистрация: 25.12.2012
Сообщений: 616
03.06.2015, 14:06 #12
Цитата Сообщение от Ethereal Посмотреть сообщение
sdadsa
Это как понять ?

Добавлено через 4 минуты
Цитата Сообщение от Ethereal Посмотреть сообщение
Если main , то никакого криминала в коде кроме перепутанных a и b нет.
А если компилятор толкает в стек локальные переменные используя регистр ESP ?
0
Ethereal
3703 / 1342 / 178
Регистрация: 17.02.2013
Сообщений: 2,253
03.06.2015, 14:12 #13
Цитата Сообщение от maxillion Посмотреть сообщение
Main это не точка входа в программу, если вы про это.
Я в курсе. Только вот кому в том коде, что ее вызывает нужны ebx esi edi.
Особенно, если сразу после ассемблерной вставки стоит return 0 из main

Я про то, что ТС нужен был ответ почему у него не работает, а не почему у него может не работать.
Если в коде есть некорректность, которая не приводит к ошибке исполнения, то это не некорректность, а трюк.

Добавлено через 4 минуты
Цитата Сообщение от maxillion Посмотреть сообщение
А если компилятор толкает в стек локальные переменные используя регистр ESP ?
Так он в твоем примере именно так и делает. Только там нигде не похабится ebp.

А в примере от ТС ebp не трогается, а даже если бы и трогался после ассемблерной вставки все равно будет восстановлен, потому-что там уже return 0, а значит и leave или pop ebp. Лишь бы не был сдвинут стек.
1
maxillion
273 / 183 / 38
Регистрация: 25.12.2012
Сообщений: 616
03.06.2015, 14:16 #14
Цитата Сообщение от Ethereal Посмотреть сообщение
Я в курсе. Только вот кому в том коде, что ее вызывает нужны ebx esi edi.
То что что выполняется до main написано на Visual c++, а он требует что бы (The compiler generates prolog and epilog code to save and restore the ESI, EDI, EBX, and EBP registers, if they are used in the function.) - из msdn.

Добавлено через 2 минуты
Цитата Сообщение от Ethereal Посмотреть сообщение
А в примере от ТС ebp не трогается, а даже если бы и трогался
Если бы он трогался то код бы вообще бы не работал.
0
Ethereal
3703 / 1342 / 178
Регистрация: 17.02.2013
Сообщений: 2,253
03.06.2015, 14:18 #15
Цитата Сообщение от maxillion Посмотреть сообщение
Это как понять ?
Трюк. Это значит я хотел уточнить мысль в своем сообщении по поводу EBP. Его как раз похабить не стоит. А поскольку данный форум непосредственно редактировать сообщения не позволяет, то тогда добавляешь нввое сообщение типа asdasdas, оно приклеивается к твоему старому сообщению и тогда его можно отредактировать. Но если в этот момент кто-то кидает свое сообщение (блин), то выходит ерунда.

Добавлено через 1 минуту
Цитата Сообщение от maxillion Посмотреть сообщение
Если бы он трогался то код бы вообще бы не работал.
Работал бы. Из-за того, что сразу за ассемблерной вставкой return 0 стоит и локальные переменные после нее не используются. А значит там уже стоит leave или pop ebp и ebp восстанавливается. Если не сдвинут стек.
1
03.06.2015, 14:18
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
03.06.2015, 14:18
Привет! Вот еще темы с ответами:

Ассемблерная вставка в С++ - Assembler
Привет, есть простая функция по обмену двух переменных, механизм обмена реализован через асм. вставку. В самой функции переменные...

Ассемблерная вставка - Assembler
На Си создать с помощью датчика случайных чисел(random) файл F1 с целочисленными матрицами 2х3. Из файла F1 получить файл F2, содержащий...

Ассемблерная вставка на C++ - Assembler
Делаю программу на С++ с использованием ассемблерных вставок. При использовании прерываний выдает ошибку сегментации: asm(&quot;movb $0x10,...

Ассемблерная вставка - Assembler
Приветствую! Есть небольшая проблемка, во вставке должно считаться значение выражения (A * X^3 + B * X^2 + C div X + D div X) div X, все...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
15
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru