È possibile creare un file di intestazione C da una libreria dinamica?

2

Supponiamo di avere una libreria dinamica compilata: .dll , .lib , .so ecc. È (teoricamente) possibile creare automaticamente un file di intestazione C per tale libreria? C'è uno strumento esistente che lo fa?

Intuitivamente mi sembra che dovrebbe essere possibile. Dopo tutto, il linker è in grado di trovare i simboli necessari all'interno della libreria dinamica e risolvere quei simboli in fase di runtime. Ma ancora, alcune informazioni potrebbero mancare. Se sì, quale? Tipi di argomenti? Il tipo di ritorno? So che quando una libreria C ++ viene compilata senza il flag "extern", con le informazioni sui tipi che sono incorporati nel nome. Questo tipo di libreria sarebbe "reverse-engineeringable"?

Aggiornamento. Grazie per tutte le risposte - sembra che ci sia un consenso sul fatto che in genere NON è possibile, a meno che uno non voglia provare veramente (I indovina esaminando l'assembly e vedendo quanti parametri vengono estratti dallo stack) OPPURE la libreria è compilata nella modalità di debug .

Lo scopo di questa domanda non è quello di offuscare la mia biblioteca, né di decompilarne una esistente. Piuttosto, è una domanda teorica: tale azione è possibile per una biblioteca generica? Il motivo della mia curiosità è che sto cercando di capire le implicazioni legali di avere una libreria con licenza GPL mentre i suoi file di intestazione sono concessi in licenza sotto licenza LGPL.

    
posta Pasha 18.10.2017 - 19:56
fonte

3 risposte

6

In generale, non sarà possibile (almeno non con i file ELF su Linux). Poiché le informazioni sul tipo e sulla firma non vengono conservate (ad esempio nei file di simboli ELF). Ma i compilatori C ++ stanno facendo nome mangling per codificare alcune informazioni sul tipo nel loro nome di simbolo ELF. Tuttavia, i compilatori C non lo fanno. E il nome del mangling in C ++ non lo dice abbastanza (ad esempio, direbbe che il primo argomento di qualche funzione è un puntatore Foo* , ma non descriverà i campi all'interno di class Foo ).

Ad esempio, non puoi nemmeno (in modo affidabile) sapere quanti argomenti si aspetta una data funzione (in particolare una C), e ancor più il loro tipo. E alcune funzioni non hanno nomi visibili esternamente (ad esempio static funzioni, ma leggi anche l'attributo visibility su Linux ). Maggiori informazioni su ABI s (ad es. qui per Linux su PC) e chiamate alle convenzioni .

Tuttavia, se il codice è stato compilato (utilizzando -g ) con informazioni di debug in DWARF , potrebbe essere possibile. Leggi anche il comando strip .

E se hai altre informazioni a priori (ad esempio, sapendo che la libreria data è distribuita da Debian) probabilmente dovrebbe essere possibile. Alcuni progetti (forse FOSSology , ma potrei sbagliarmi) hanno semplicemente indovinato le librerie software libere confrontando le loro stringhe letterali costanti con un database precedentemente costruito di loro.

A proposito, quello che stai guardando è più o meno chiamato decompilatore e il processo sarebbe decompilato. Leggi anche su offuscamento .

Con un sacco di sforzi e risorse (ad esempio ciò di cui la NSA sarebbe capace) molte cose potrebbero essere in pratica possibili, ma difficili e costose.

    
risposta data 18.10.2017 - 20:29
fonte
3

Dipende dal sistema di compilazione, e specialmente se solo C o C & Compilatore C ++, poiché questi sono aspetti del sistema di sviluppo che non sono comportamenti standardizzati.

Alcuni sistemi definiscono i punti di ingresso di una DLL, ma non rilevano nemmeno il conteggio dei parametri non corrispondenti, poiché il nome dell'identificatore non elaborato viene esportato senza mangling o decorazione.

Questo evidenzia che esistono inesistenti metadati nel meccanismo di DLL raw per inferire automaticamente un file di intestazione .h.

Altri sistemi di collegamento acquisiscono il conteggio dei parametri ma non i tipi di parametri completi. Ad esempio, su windows __stdcall :

An underscore _ is prefixed to the name. The name is followed by the at sign @ followed by the number of bytes (in decimal) in the argument list. Therefore, the function declared as int func( int a, double b ) is decorated as follows: _func@12.

Si noti che il tipo di ritorno non è codificato in decorazione (la maggior parte delle lingue non supporterà sovraccarichi in cui differisce solo il tipo di ritorno).

Oltre a ciò, anche il nome mangling di C ++ garantisce solo la corrispondenza tra il chiamante e il chiamato della stessa firma. Pertanto, mentre tutti i tipi di parametri sono abbinati per garantire una corrispondenza completa della firma del sovraccarico corretto, in realtà le dichiarazioni di tipo di codice (ad esempio di strutture e simili) che ci si aspetterebbe di vedere in un file di intestazione sono assenti. Quindi, solo il nome di un tipo struct sarà disponibile nel nome manglinato / decorato, ma non i membri di quella struct.

Anche il C ++ non usa sempre il meccanismo di esportazione del nome disponibile nelle DLL, e questo potrebbe andare ai metodi inline e ai metodi privati (e anche ai metodi teoricamente virtuali). Nota qui sto distinguendo tra le esportazioni DLL (post compilation e post static linking che crea la DLL) e i moduli oggetto (post compilation ma prima del collegamento statico).

    
risposta data 18.10.2017 - 20:22
fonte
3

No, questo non è generalmente possibile. Le librerie sono auto-descrittive in quanto elencano tutti i simboli disponibili (funzioni e variabili con collegamento esterno). Ma non contengono informazioni sufficienti sui tipi.

Non si tratta solo di fornire i nomi di tutti i tipi, ad es. che una funzione riceve o restituisce un struct Foo . Per soddisfare correttamente la convenzione di chiamata, è necessario conoscere il layout e in particolare l'allineamento di quel tipo. Quindi avremmo bisogno di incorporare le informazioni complete sul tipo che verrebbero fornite da un'intestazione. (Naturalmente i tipi incompleti possono essere eli- minati.)

I file di intestazione possono essere visti come una lingua di descrizione dell'interfaccia che fornisce tutte queste informazioni.

Ovviamente è possibile progettare un formato di libreria dinamico che contenga tutte le informazioni rilevanti, in particolare i file di classe JVM lo fanno.

Come nota storica: C non impone i tipi di parametro prima della standardizzazione con C89 ("ANSI C"). Una funzione dichiarata senza un prototipo non può essere verificata in alcun modo e il programmatore dovrebbe conoscere i tipi corretti. In questo senso, le funzioni nelle librerie dinamiche si comportano ancora molto come le funzioni di K & R C.

    
risposta data 18.10.2017 - 20:28
fonte

Leggi altre domande sui tag