Perché i nomi delle funzioni sono decorati in C?

1

Quando si compila un file sorgente C in un file oggetto, i nomi delle funzioni nel file oggetto verranno decorati. Ogni convenzione di chiamata avrà una decorazione diversa.

Ad esempio, la seguente funzione __stdcall :

void __stdcall stdcallFunction(int i)
{
    int j = 12345;
}

Saranno decorati in questo modo nel file oggetto:

_stdcallFunction@4

E la seguente funzione __cdecl :

void __cdecl cdeclFunction(int i)
{
    int j = 12345;
}

Saranno decorati in questo modo nel file oggetto:

_cdeclFunction

Ora la mia domanda è: perché viene usata la decorazione del nome? Voglio dire, perché la funzione stdcallFunction non viene salvata nel file oggetto semplicemente come stdcallFunction e non come _stdcallFunction@4 ?

Penso che la ragione sia la seguente:

Dire che ho creato una libreria (una libreria .lib e non una libreria .c ) che contiene le due funzioni precedenti.

Ora voglio chiamare la funzione stdcallFunction in questa libreria dal mio file sorgente C, farei quanto segue:

void __stdcall stdcallFunction(int i);

stdcallFunction(123);

Questo compilerà bene. Ma se ho fatto quanto segue (ha cambiato la convenzione di chiamata per la dichiarazione di funzione):

void __cdecl stdcallFunction(int i);

stdcallFunction(123);

Quindi questo produrrà un errore di compilazione.

Quindi il motivo per usare la decorazione del nome è per il compilatore assicurarsi che io stia usando la convenzione di chiamata corretta quando chiami una funzione che esiste in una libreria (la decorazione del nome è semplicemente un'indicazione di quale sia la convenzione di chiamata di un funzione in una libreria).

Sono corretto?

    
posta user8437463 10.08.2017 - 20:49
fonte

3 risposte

4

Penso che il tuo ragionamento sia corretto. Se dovessi chiamare una funzione con la convenzione di chiamata sbagliata, possono accadere cose molto brutte in fase di esecuzione, e in questo modo, tali errori vengono rilevati durante la costruzione del programma (cioè il collegamento) piuttosto che in fase di esecuzione.

C ++ è noto per il nome mangling, motivato in parte dal suo supporto per funzioni sovraccariche - un insieme di funzioni tutte con lo stesso nome anche se diverse per numero o tipi di parametri - quindi nel caso C ++, non si tratta solo di catturare errori nella compilazione separata ma anche sul supporto degli overload con l'oggetto e i formati di file eseguibili comunemente usati che preesistevano in C ++.

C non offre un sovraccarico delle funzioni, quindi non è una motivazione per C. Tuttavia, molti compilatori C sono anche compilatori C ++, quindi condividi alcuni dettagli di implementazione sottostanti e amp; funzionalità.

Tuttavia, non dovremmo attribuire questi comportamenti che state osservando al linguaggio C, ma piuttosto compilatori specifici.

Il linguaggio C non specifica come i nomi che dovrebbero apparire nei file oggetto; non ha __stdcall e __cdecl - queste sono estensioni (non standard) da alcune implementazioni.

Al meglio lo C Standard richiede un'implementazione a supporto separato compilazione (5.1.1.1) ma i dettagli dei file oggetto e del collegamento non sono semplicemente nello standard. Un'implementazione è libera di utilizzare un formato di file oggetto arbitrario e / o un formato di file eseguibile, compresi il mangling o la decorazione di identificatori.

Lo standard C non fornisce alcun concetto di riflessione o dinamicamente (in fase di esecuzione) cercando una funzione per nome in un Dynamic- Link Library (DLL) , quindi non c'è nulla da violare se un'implementazione decora i nomi.

Ancora una volta, nel migliore dei casi, lo standard definisce una nozione di collegamento esterno (6.2.2).

In the set of translation units and libraries that constitutes an entire program, each declaration of a particular identifier with external linkage denotes the same object or function.

Quindi, fondamentalmente finché la stessa dichiarazione in una unità di compilazione è correttamente equiparata alla stessa dichiarazione in un'altra unità di compilazione, l'implementazione è legale.

    
risposta data 10.08.2017 - 22:28
fonte
2

La decorazione dei nomi in questo modo viene eseguita per prevenire alcuni errori potenzialmente spettacolari che potrebbero verificarsi con errori di collegamento diversi.

I tuoi esempi non dovrebbero produrre errori del compilatore. Dovrebbero compilare bene. Dovrebbero produrre errori linker , quando tenta di trovare una funzione cdecl chiamata stdcallFunction . Può produrre quell'errore perché il compilatore decora i nomi per cdecl e stdcall in modo diverso. Se non li decorassero in modo diverso, il linker non avrebbe alcuna informazione con cui capire che le convenzioni di chiamata sono diverse.

Gli errori nelle convenzioni di chiamata sono davvero cattive notizie . In cdecl , il chiamante dovrebbe pulire lo stack, rimuovendo tutti gli argomenti che ha spinto. In stdcall , il callee ripulisce lo stack, rimuovendo gli argomenti prima che ritorni. Se il chiamante e il chiamato non concordano sullo stato della pila quando la funzione ritorna, possono comparire tutti i tipi di comportamenti non definiti. Questo è il tipo di errore che può cancellare completamente le opportunità di debug, perché anche i debugger dipendono dal fatto di avere un'idea di come sia lo stack.

Questo, ovviamente, spiega perché cdecl richiede solo un semplice manomissione dei nomi (una sotto barra), ma stdcall deve aggiungere il numero di byte che ha fatto scoppiare. Il chiamante deve sapere esattamente quale sarà lo stato dello stack al momento del ritorno del destinatario.

    
risposta data 10.08.2017 - 23:16
fonte
-1

La decorazione dei nomi consente più di una funzione con lo stesso nome, ad es. con diversi prototipi (argomenti). Senza il nome decorato, saresti limitato a disporre di una sola funzione per simbolo o dovrai trovare il punto di ingresso per ordinale anziché per nome.

    
risposta data 10.08.2017 - 22:26
fonte

Leggi altre domande sui tag