in esecuzione alloca
in realtà richiede l'assistenza del compilatore. Alcune persone dicono che è facile come:
sub esp, <size>
che purtroppo è solo metà dell'immagine. Sì, "allocerebbe spazio nello stack", ma ci sono un paio di trucchi.
se il compilatore aveva emesso il codice che fa riferimento altre variabili rispetto al esp
invece di ebp
(tipica se si compila senza puntatore telaio). Quindi è necessario modificare i riferimenti . Anche con i puntatori di frame, i compilatori lo fanno a volte.
ancora più importante, per definizione, lo spazio allocato con alloca
deve essere "liberato" quando la funzione termina.
Il più grande è il punto 2. Poiché è necessario il compilare il codice per aggiungere simmetricamente <size>
a esp
in ogni punto di uscita della funzione.
Il caso più probabile è che il compilatore offra alcuni elementi intrinseci che consentono agli scrittori di librerie di chiedere al compilatore l'aiuto necessario.
EDIT:
Infatti, in glibc (implementazione GNU di libc). L'implementazione di alloca
è semplicemente questo:
#ifdef __GNUC__
# define __alloca(size) __builtin_alloca (size)
#endif /* GCC. */
EDIT:
dopo averci pensato, il minimo credo sarebbe necessario sarebbe per il compilatore a sempre utilizzare una forma di cornice in qualsiasi funzioni che utilizzano alloca
, indipendentemente dalle impostazioni di ottimizzazione. Ciò consentirebbe a tutti i locali di essere referenziati attraverso ebp
in modo sicuro e la pulizia del frame verrebbe gestita ripristinando il puntatore del frame su esp
.
EDIT:
così ho fatto alcuni esperimenti con cose come questa:
#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();
}
che purtroppo non funziona correttamente. Dopo aver analizzato l'output dell'assembly da gcc. Sembra che le ottimizzazioni si intromettano. Il problema sembra essere che, poiché l'ottimizzatore del compilatore è completamente inconsapevole del mio assembly inline, ha l'abitudine di fare le cose in un ordine inaspettato e ancora riferimento a cose via esp
.
Ecco l'ASM risultante:
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 <[email protected]>
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 <[email protected]>
8048496: mov eax,DWORD PTR [ebp-0x4]
8048499: mov DWORD PTR [esp],eax ; <- whoops! compiler still referencing via esp
804849c: call 8048368 <[email protected]>
80484a1: leave
80484a2: ret
Come si può vedere, non è così semplice. Sfortunatamente, sostengo la mia affermazione iniziale secondo cui hai bisogno di assistenza per il compilatore.
Penso che tu stia bene lì; gli accessi ESP stanno scrivendo argomenti prima delle chiamate di funzione, e il relativo ESP è corretto. Si potrebbe provare '-fno-accumulate-outgoing-args' o qualunque cosa e argomenti correlati per ottenere gcc per usare semplicemente PUSH invece di usare MOV per modificare la parte inferiore dello stack. –
Ma in realtà, cercando di implementare l'alloca dietro al compilatore è un'idea terribile, come si fa notare nella prima parte di questa eccellente risposta. Tanti modi per andare male e nessuna ragione per farlo. Se la gente vuole scrivere asm e fare la propria allocazione di stack, basta scrivere in puro asm invece di abusare di inline-asm in C++. –
@PeterCordes true che la maggior parte dei riferimenti ESP sono argomenti di funzione, ma poiché ha provato a pre-allocare lo spazio ** prima ** di "alloca", quelle mosse calpesteranno lo "spazio allocato" dell'utente. Che è rotto se intendo usare quello spazio. Cambiarle a spinte adeguate risolverebbe la maggior parte di ciò. Anche l'ultimo riferimento esp sta memorizzando un risultato in una variabile locale e ancora una volta calpesterà "l'array". Va abbastanza velocemente. –