Domanda

C'è un modo per conoscere e produrre le dimensioni dello stack necessarie per una funzione al momento della compilazione in C? Ecco cosa vorrei sapere:

Assumiamo alcune funzioni:

void foo(int a) {
    char c[5];
    char * s;
    //do something
    return;
}

Durante la compilazione di questa funzione, vorrei sapere quanto spazio dello stack consumerà quando viene chiamato. Questo potrebbe essere utile per rilevare la dichiarazione in pila di una struttura che nasconde un grosso buffer.

Sto cercando qualcosa che stampi qualcosa del genere:

file foo.c: la funzione dell'uso dello stack foo è n byte

C'è un modo per non guardare l'assembly generato per saperlo? O un limite che può essere impostato per il compilatore?

Aggiornamento: non sto cercando di evitare l'overflow dello stack di runtime per un determinato processo, sto cercando un modo per trovare prima del runtime, se l'utilizzo dello stack di funzioni, come determinato dal compilatore, è disponibile come output della compilation processo.

Mettiamola in un altro modo: è possibile conoscere la dimensione di tutti gli oggetti locali in una funzione? Immagino che l'ottimizzazione del compilatore non sarà mia amica, perché alcune variabili scompariranno ma va bene un limite superiore.

È stato utile?

Soluzione

Il codice del kernel Linux viene eseguito su uno stack 4K su x86. Quindi a loro importa. Quello che usano per verificarlo è uno script perl che hanno scritto, che puoi trovare come script / checkstack.pl in un recente tarball del kernel (2.6.25 ce l'ha). Funziona sull'output di objdump, la documentazione sull'utilizzo è nel commento iniziale.

Penso di averlo già usato per i binari dello spazio utente anni fa, e se conosci un po 'di programmazione perl, è facile risolverlo se è rotto.

Ad ogni modo, ciò che sostanzialmente fa è guardare automaticamente all'output di GCC. E il fatto che gli hacker del kernel abbiano scritto un tale strumento significa che non esiste un modo statico per farlo con GCC (o forse che è stato aggiunto di recente, ma ne dubito).

A proposito, con objdump del progetto mingw e ActivePerl, o con Cygwin, dovresti essere in grado di farlo anche su Windows e anche sui binari ottenuti con altri compilatori.

Altri suggerimenti

StackAnlyser sembra esaminare il codice eseguibile stesso oltre ad alcune informazioni di debug. Quello che sto cercando da questa risposta , è quello che sto cercando, l'analizzatore di stack mi sembra eccessivo.

Qualcosa di simile a ciò che esiste per ADA andrebbe bene. Guarda questa pagina di manuale dal manuale di gnat:

22.2 Analisi dell'utilizzo dello stack statico

Un'unità compilata con -fstack-use genererà un file aggiuntivo che specifica la quantità massima di stack utilizzata, in base alla funzione. Il file ha lo stesso nome di base del file oggetto di destinazione con estensione .su. Ogni riga di questo file è composta da tre campi:

* The name of the function.
* A number of bytes.
* One or more qualifiers: static, dynamic, bounded. 

Il secondo campo corrisponde alla dimensione della parte nota del frame della funzione.

Il qualificatore statico indica che la dimensione del frame della funzione è puramente statica. Di solito significa che tutte le variabili locali hanno una dimensione statica. In questo caso, il secondo campo è una misura affidabile dell'utilizzo dello stack di funzioni.

La dinamica del qualificatore indica che la dimensione del frame della funzione non è statica. Succede principalmente quando alcune variabili locali hanno una dimensione dinamica. Quando questo qualificatore appare da solo, il secondo campo non è una misura affidabile dell'analisi dello stack di funzioni. Quando è qualificato con delimitato, significa che il secondo campo è un massimo affidabile dell'utilizzo dello stack di funzioni.

Non vedo perché un'analisi del codice statico non possa fornire una cifra abbastanza valida per questo.

È banale trovare tutte le variabili locali in una data funzione, e la dimensione per ogni variabile può essere trovata attraverso lo standard C (per tipi incorporati) o calcolandola (per tipi complessi come strutture e unioni).

Certo, non è possibile garantire che la risposta sia accurata al 100%, poiché il compilatore può eseguire vari tipi di ottimizzazioni come il riempimento, l'inserimento di variabili nei registri o la rimozione completa di variabili non necessarie. Ma ogni risposta che dà dovrebbe essere almeno una buona stima.

Ho fatto una rapida ricerca su Google e ho trovato StackAnalyzer ma la mia ipotesi è che altre analisi di codice statico gli strumenti hanno capacità simili.

Se vuoi una cifra accurata al 100%, dovresti guardare l'output dal compilatore o controllarlo durante il runtime (come Ralph ha suggerito in la sua risposta )

Solo il compilatore lo saprebbe davvero, dal momento che è l'uomo che mette insieme tutte le tue cose. Dovresti guardare l'assembly generato e vedere quanto spazio è riservato nel preambolo, ma ciò non tiene conto di cose come alloca che fanno le loro cose in fase di esecuzione.

Supponendo che tu sia su una piattaforma integrata, potresti scoprire che la tua toolchain ci prova. Buoni compilatori embedded commerciali (come, ad esempio, il compilatore Arm / Keil) producono spesso report sull'utilizzo dello stack.

Ovviamente, gli interrupt e la ricorsione sono di solito un po 'al di là di loro, ma ti dà un'idea approssimativa se qualcuno ha commesso un terribile malfunzionamento con un buffer multi-megabyte nello stack da qualche parte.

Non esattamente " tempo di compilazione " ;, ma lo farei come un passo post-build:

  • consenti al linker di creare un file mappa per te
  • per ogni funzione nel file della mappa leggere la parte corrispondente dell'eseguibile e analizzare il prologo della funzione.

Questo è simile a quello che fa StackAnalyzer, ma molto più semplice. Penso che analizzare l'eseguibile o lo smontaggio sia il modo più semplice per ottenere l'output del compilatore. Mentre il compilatore conosce queste cose internamente, temo che non sarai in grado di ottenerlo da esso (potresti chiedere al fornitore del compilatore di implementare la funzionalità o, se usi un compilatore open source, potresti farlo da solo o lasciare che qualcuno lo faccia per te).

Per implementare questo è necessario:

  • essere in grado di analizzare il file della mappa
  • comprende il formato dell'eseguibile
  • sa come può apparire un prologo di funzione ed è in grado di "decodificare" it

Quanto facile o difficile sarebbe dipendere dalla tua piattaforma di destinazione. (Incorporato? Quale architettura CPU? Quale compilatore?)

Tutto ciò può sicuramente essere fatto in x86 / Win32, ma se non hai mai fatto nulla di simile e devi creare tutto questo da zero, potrebbero essere necessari alcuni giorni prima di aver finito e avere qualcosa che funzioni.

Non in generale. Il problema dell'arresto nell'informatica teorica suggerisce che non si può nemmeno prevedere se un programma generale si ferma su un dato input. Il calcolo dello stack utilizzato per un programma eseguito in generale sarebbe ancora più complicato. Quindi: no. Forse in casi speciali.

Supponiamo che tu abbia una funzione ricorsiva il cui livello di ricorsione dipende dall'input che può essere di lunghezza arbitraria e che sei già sfortunato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top