реализация alloca
на самом деле требует помощи компилятора . Несколько человек здесь говорят, что это так же просто, как:
sub esp, <size>
, что, к сожалению, только половина картины. Да, это "выделит место в стеке", но есть несколько ошибок.
, если компилятор выпустил код
который ссылается на другие переменные
относительно esp
вместо ebp
(типично, если вы компилируете без
указатель кадра). Тогда те
ссылки должны быть скорректированы. Даже с указателями фреймов компиляторы делают это иногда.
что более важно, по определению, пространство, выделенное с помощью alloca
, должно быть
"освобожден" при выходе из функции.
Самое важное - это точка № 2. Потому что вам нужен компилятор для генерации кода для симметричного добавления <size>
к esp
в каждой точке выхода функции.
Наиболее вероятным случаем является то, что компилятор предлагает некоторые встроенные функции, которые позволяют авторам библиотеки обращаться к компилятору за необходимой помощью.
EDIT:
Фактически, в glibc (реализация libc в GNU). Реализация alloca
просто так:
#ifdef __GNUC__
# define __alloca(size) __builtin_alloca (size)
#endif /* GCC. */
EDIT:
, подумав об этом, я думаю, что минимум, который, как я полагаю, потребуется для компилятора, всегда использует указатель фрейма в любых функциях, которые используют alloca
, независимо от настроек оптимизации. Это позволило бы безопасно обращаться ко всем местным жителям через ebp
, а очистка кадра будет выполняться путем восстановления указателя кадра в esp
.
EDIT:
Итак, я немного поэкспериментировал с такими вещами:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define __alloca(p, N) \
do { \
__asm__ __volatile__( \
"sub %1, %%esp \n" \
"mov %%esp, %0 \n" \
: "=m"(p) \
: "i"(N) \
: "esp"); \
} while(0)
int func() {
char *p;
__alloca(p, 100);
memset(p, 0, 100);
strcpy(p, "hello world\n");
printf("%s\n", p);
}
int main() {
func();
}
который, к сожалению не работает правильно. Проанализировав вывод сборки по gcc. Похоже, что оптимизация мешает. Кажется, проблема в том, что, поскольку оптимизатор компилятора полностью не знает о моей встроенной сборке, он имеет привычку делать вещи в неожиданном порядке и все еще ссылаться на вещи через esp
.
Вот результирующий ASM:
8048454: push ebp
8048455: mov ebp,esp
8048457: sub esp,0x28
804845a: sub esp,0x64 ; <- this and the line below are our "alloc"
804845d: mov DWORD PTR [ebp-0x4],esp
8048460: mov eax,DWORD PTR [ebp-0x4]
8048463: mov DWORD PTR [esp+0x8],0x64 ; <- whoops! compiler still referencing via esp
804846b: mov DWORD PTR [esp+0x4],0x0 ; <- whoops! compiler still referencing via esp
8048473: mov DWORD PTR [esp],eax ; <- whoops! compiler still referencing via esp
8048476: call 8048338 <memset@plt>
804847b: mov eax,DWORD PTR [ebp-0x4]
804847e: mov DWORD PTR [esp+0x8],0xd ; <- whoops! compiler still referencing via esp
8048486: mov DWORD PTR [esp+0x4],0x80485a8 ; <- whoops! compiler still referencing via esp
804848e: mov DWORD PTR [esp],eax ; <- whoops! compiler still referencing via esp
8048491: call 8048358 <memcpy@plt>
8048496: mov eax,DWORD PTR [ebp-0x4]
8048499: mov DWORD PTR [esp],eax ; <- whoops! compiler still referencing via esp
804849c: call 8048368 <puts@plt>
80484a1: leave
80484a2: ret
Как видите, не все так просто. К сожалению, я придерживаюсь своего первоначального утверждения, что вам нужна помощь компилятора.