|
|
(6 versioni intermedie di uno stesso utente non sono mostrate) |
Riga 1: |
Riga 1: |
− | ===Introduzione===
| + | #REDIRECT [[Come creare una Libreria condivisa (Shared Library) .so]] |
− | In Gambas possono essere richiamate con la funzione ''Extern'' le cosiddette "''Librerie condivise''" (''Shared Library'') con estensione finale .so . Esse sono librerie esterne a Gambas, scritte in C o C++, che offrono potenzialità, funzionalità e risorse non disponibili eventualmente al momento con Gambas.
| |
− | | |
− | Le ''Librerie condivise'' sono delle librerie che vengono caricate dai programmi al loro avvio. Esse introducono il concetto di codice riutilizzabile dinamicamente da altri programmi.
| |
− | | |
− | E' possibile creare da sé una ''Liberia condivisa .so'' ed utilizzarla con uno o più dei nostri programmi dei Gambas, richiamandola, come sappiamo, con la funzione "''Extern''". Per realizzare una ''Liberia condivisa .so'' sarà necessario avere dimistichezza ed adeguata conoscenza con il linguaggio C o con il C++. La realizzazione da sé di proprie ''Librerie condivise .so'', dunque, offre la possibilità e la maniera concreta di implementare il linguaggio C nel codice Gambas e di utilizzarlo così con il nostro programma.
| |
− | | |
− | | |
− | ==Elementi distintivi delle ''Librerie condivise''==
| |
− | Riguardo alle ''Librerie condivise'' si distinguono tre tipi di nomi.
| |
− | | |
− | Il pprimo tipo di nome è chiamato "''soname''", costituito in ordine da:
| |
− | * il prefisso ''lib'';
| |
− | * il nome della libreria;
| |
− | * l'estensione ''.so'';
| |
− | * un punto ed il numero della versione.
| |
− | | |
− | | |
− | Il secondo tipo di nome è chiamato ''nome reale'', il quale non è altro che il file vero e proprio contenente il codice della libreria. Il ''nome reale'' della Liberia è costituito a sua volta in ordine da:
| |
− | * il prefisso <FONT color=#B22222>''lib''</font>;
| |
− | * il <FONT color=#0000FF>nome</font> della libreria;
| |
− | * l'estensione <FONT color=#B22222>''.so''</font>;
| |
− | * un <FONT color=#0000FF>punto</font> ed un <FONT color=#0000FF>numero</font>, poi un <FONT color=#B22222>punto</font> ed un <FONT color=#B22222>numero del rilascio</font>.
| |
− | | |
− | | |
− | Esempio: <FONT color=#B22222>lib</font><FONT color=#0000FF>nomequalsiasi</font><FONT color=#B22222>.so</font><FONT color=#0000FF>.0</font><FONT color=#B22222>.0</font>
| |
− | | |
− | | |
− | Vi è, infine, il nome che il compilatore usa, quando viene richiamata ed utilizzata una ''Liberia condivisa''. Tale nome non è altro il ''soname'' privo di qualsiasi numero di versione. Questo file è semplicemente un collegamento simbolico al ''soname'' più recente della Libreria.
| |
− | <BR>Se, dunque, per esempio il ''soname'' della ''Liberia condivisa'' è ''/usr/lib/libnomequalsiasi.so.1'' , il file con il nome del collegamento simbolico a tale ''soname'' sarà: /usr/lib/libnomequalsiasi.so .
| |
− | | |
− | | |
− | Pertanto riassumendo in ipotesi avremo:
| |
− | * '''''soname''''': libnomequalsiasi.so.1
| |
− | * '''''nome reale''''': libnomequalsiasi.so.1.0
| |
− | * '''''nome del collegamento simbolico''''': libnomequalsiasi.so
| |
− | | |
− | | |
− | ==Dove salvare le ''Librerie condivise .so''==
| |
− | Gli standard GNU raccomandano di insatallare tutte le Librerie nella directory: ''/usr/local/lib''. Invece il ''Filesystem Hierarchy Standard'' (FHS) suggerisce di porre la maggior parte delle Librerie nella directory: ''/usr/lib'', e solo quelle che non appartengono al sistema andrebbero salvate nella directory: ''/usr/local/lib''.
| |
− | | |
− | Ad ogni modo, la ''Liberia condivisa .so'', da noi creata ed utilizzata dal nostro programma Gambas, potrà essere salvata in una qualsiasi directory, e da lì richiamata dal nostro programma.
| |
− | | |
− | | |
− | ==Realizzazione pratica di una ''Libreria condivisa .so''==
| |
− | La creazione di una ''Libreria condivisa .so'' è semplice. Essa si articola sostanzialmente in quattro fasi, che vedremo nello specifico di seguito.
| |
− | | |
− | | |
− | <B><FONT color=#0000FF>1<sup>a</sup> fase</font></b>
| |
− | La prima fase prevede la creazione in C o C++ dei due file che contengono il codice della/le funzione/i che sarà/nno utilizzata/e dal nostro programma Gambas. Il primo file sarà un normale file, che qui a mo' di esempio chiameremo ''primo.c'', ed il secondo file sarà l'header del file precedente, e che chiameremo: ''primo.h'' .
| |
− | | |
− | Poniamo come esempio che il file ''primo.c'' sia semplicemente il seguente:
| |
− | | |
− | int fun(int a)
| |
− | {
| |
− | return (a * 2);
| |
− | }
| |
− | ossia una funzione che riceve (da nostro programma Gambas chiamante) un intero, lo moltiplica per 2 e lo restiuisce alla funzione principale chiamante (cioè alla funzione della nostro programma Gambas).
| |
− | | |
− | Il relativo file header ''primo.h'' sarà dunque questo:
| |
− | | |
− | int fun(int);
| |
− | | |
− | | |
− | <B><FONT color=#0000FF>2<sup>a</sup> fase</font></b>
| |
− | La seconda fase prevede la creazione del (o dei) ''file oggetto'' che sarà inserito nella Libreria.
| |
− | | |
− | Ponendosi nella directory che contiene i due file ''primo.c'' e ''primo.h'', si userà nel terminale la seguente linea di istruzioni:
| |
− | | |
− | gcc -fPIC -Wall -g -c primo.c
| |
− | utilizzando -fPIC per abilitare la generazione di codice non dipendente dalla posizione (Position Indipendent Code).
| |
− | | |
− | Da questa linea di istruzioni si otterrà il file: <FONT color=#B22222>''primo.o''</font> .
| |
− | | |
− | | |
− | <B><FONT color=#0000FF>3<sup>a</sup> fase</font></b>
| |
− | La terza fase prevede la creazione vera e propria della ''Libreria condivisa .so'' . La linea di istruzione nel terminale prevede, tra l'altro, l'indicazione del ''soname'', del ''nome-reale'' e del ''file oggetto'' creato nella 2<sup>a</sup> fase:
| |
− | | |
− | gcc -g -shared -Wl,-soname,libprimo.so.0 -o libprimo.so.0.0 primo.o -lc
| |
− | dalla quale si otterrà il file della nostra ''Libreria condivisa'': <FONT color=#B22222>libprimo.so.0.0</font> (ossia il "''nome-reale''" della Libreria).
| |
− | | |
− | Bisogna avere attenzione a non porre alcuno spazio fra i parametri ''-Wl,-soname,libprimo.so.0'' della precedente riga di comando.
| |
− | | |
− | | |
− | <B><FONT color=#0000FF>4<sup>a</sup> fase</font></b>
| |
− | A questo punto si potrà, volendo, copiare/spostare il file della Libreria da noi appena creata in un'altra directory, dopo di che creeremo il file del nome di collegamento simbolico lanciando sempre da terminale la seguente riga di comando:
| |
− | | |
− | sudo ln -sf libprimo.so.0.0 libprimo.so
| |
− | dalla quale si otterrà il file: <FONT color=#B22222>libprimo.so</font>
| |
− | | |
− | ==Prova della ''Libreria condivisa'' creata nell'esempio==
| |
− | '''1''' - Proviamo, quindi, la nostra Libreria ''libprimo.so.0.0'', da noi appena creata, dichiarandola ed utilizzando la funzione, in essa contenuta, nel nostro applicativo Gambas. Alleghiamo al nostro applicativo Gambas il file della Libreria ''libprimo.so.0.0'' e quello del relativo collegamento simbolico ''libprimo.so'', inserendoli nella cartella "Dati". Poi scriveremo il codice del nostro programma come segue, facendo in modo, per non installare permanentemente quei due file nel nostro sistema operativo, che sia il nostro programma a porre i file all'interno della directory /tmp:
| |
− | | |
− | Rammentiamo che la funzione in linguaggio C, contenuta nella nostra Libreria, è la seguente:
| |
− | int fun(int a)
| |
− | {
| |
− | return (a * 2);
| |
− | }
| |
− | | |
− | | |
− | L'applicazione Gambas, che richiamerà quella funzione, sarà:
| |
− | | |
− | <FONT color=#006400>' ''Dichiariamo la nostra Libreria da usare,''
| |
− | ' ''specificando in questo caso anche il percorso /tmp ove l'abbiamo copiata:''</font>
| |
− | Library "/tmp/libprimo"
| |
− |
| |
− |
| |
− | '''Public''' Sub Form_Open()
| |
− |
| |
− | <FONT color=#006400>' ''Se non sono già stati copiati da un precedente avvio del programma,''
| |
− | ' ''copiamo i due file relativi alla nostra Libreria nella directory /tmp:''</font>
| |
− | If Not Exist("/tmp/libprimo.so.0.0") And Not Exist("/tmp/libprimo.so") Then
| |
− | Copy "libprimo.so" To "/tmp/libprimo.so"
| |
− | Copy "libprimo.so.0.0" To "/tmp/libprimo.so.0.0"
| |
− | Endif
| |
− |
| |
− | '''End'''
| |
− |
| |
− |
| |
− | <FONT color=#006400>' ''Dichiariamo la funzione esterna specifica, contenuta nella "Libreria condivisa" da noi creata:''</font>
| |
− | Private Extern fun(a As Integer) As Integer
| |
− |
| |
− | '''Public''' Sub Button1_Click()
| |
− |
| |
− | Dim a As Integer
| |
− |
| |
− | <FONT color=#006400>' ''Passiamo alla funzione esterna della nostra Libreria il valore intero: 64.''
| |
− | ' ''Essa ci restiuirà il risultato della sua operazione (moltiplicazione del valore passato per 2):''</font>
| |
− | a = fun(64)
| |
− |
| |
− | Print a
| |
− |
| |
− | '''End'''
| |
− | | |
− | In console avremo coerentemente: 128
| |
− | | |
− | | |
− | '''2''' - Facciamo ora l'esempio di una ''Libreria condivisa'' avente una funzione che restituisce semplicemente un carattere. Chiameremo questa Libreria: libsecondo.so.0.0, e la porremo ancora in /tmp.
| |
− | <BR>La Libreria conterrà il presente codice, scritto in linguaggio C:
| |
− | char a;
| |
− |
| |
− | char * funChar (void) {
| |
− |
| |
− | a = 'z';
| |
− |
| |
− | char *ap;
| |
− |
| |
− | ap = &a; /* contiene l'indirizzo di a */
| |
− |
| |
− | return ap;
| |
− | }
| |
− | | |
− | | |
− | L'applicazione Gambas, che richiamerà quella funzione, sarà:
| |
− | | |
− | Library "/tmp/libsecondo"
| |
− |
| |
− | Private Extern funChar() As String
| |
− |
| |
− |
| |
− | '''Public''' Sub Button1_Click()
| |
− |
| |
− | Dim r As String
| |
− |
| |
− | r = funChar()
| |
− |
| |
− | Print r
| |
− |
| |
− | '''End'''
| |
− | | |
− | In console avremo: z
| |
− | | |
− | | |
− | '''3''' - Ora invece faremo l'esempio di una funzione che ci restituirà più caratteri.
| |
− | <BR>La Libreria conterrà il presente codice, scritto in linguaggio C:
| |
− | char a[] = "Gambas e Librerie esterne";
| |
− |
| |
− | char * funChar (void) {
| |
− |
| |
− | return a;
| |
− |
| |
− | }
| |
− | | |
− | | |
− | L'applicazione Gambas, che richiamerà quella funzione, sarà:
| |
− | | |
− | Library "/tmp/libsecondo"
| |
− |
| |
− | Private Extern funChar() As String
| |
− |
| |
− |
| |
− | '''Public''' Sub Button1_Click()
| |
− |
| |
− | Dim r As String
| |
− |
| |
− | r = funChar()
| |
− |
| |
− | Print r
| |
− |
| |
− | '''End'''
| |
− | | |
− | In console avremo: Gambas e Librerie esterne
| |
− | | |
− | | |
− | | |
− | ==Modalità semplificata di creazione di una Libreria condivisa==
| |
− | Oltre alla modalità complessa vista sopra, è possibile creare più velocemente una libreria condivisa .so nel modo seguente:
| |
− | Shell "gcc -fPIC -Wall -o ''percorso_della_libreria'' ''percorso_del_file.c'' ''-eventuale_libreria_da richiamare'' -shared" Wait
| |
− | possiamo anche aggiungere un accorgimento per intercettare eventuali errori:
| |
− | '''Public''' Sub Button1_Click()
| |
− |
| |
− | Dim s As String
| |
− |
| |
− | Shell "gcc -fPIC -Wall -o ''percorso_della_libreria'' ''percorso_del_file.c'' ''-eventuale_libreria_da richiamare'' -shared <FONT color=#B22222>2>&1</font>" Wait To s
| |
− |
| |
− | Print s
| |
− |
| |
− | '''End'''
| |
− | | |
− | | |
− | | |
− | ==Inserire nella "''Libreria condivisa''" anche codice ''Assembly''==
| |
− | Oltre al codice in linguaggio C/C++ è possibile inserire parti in ''Assembly'', al fine di ottenere prestazioni ancor più veloci.
| |
− | | |
− | Nel semplice esempio, che segue, è possibile vedere come - <SPAN style="text-decoration:underline">in via generale</span> - si integra il codice ''Assembly'' all'interno del linguaggio C:
| |
− | | |
− | static unsigned int car <FONT color=#B22222>asm("raxregistro")</font>;
| |
− | static unsigned int cbr <FONT color=#B22222>asm("rbxregistro")</font>;
| |
− | cbr = 15;
| |
− | <FONT color=#B22222>asm("mov rbxregistro,%ax")</font>;
| |
− | <FONT color=#B22222>asm("mov %ax,raxregistro")</font>;
| |
− | printf("%d",car);
| |
− | alla fine, alla riga con ''printf'', la variabile ''car'' avrà valore 15.
| |
− | | |
− | | |
− | ====Problemi di compilazione nei sistemi a 64-bit durante la creazione di una Libreria .so====
| |
− | | |
− | Si è riscontrato nei sistemi a 64-bit che durante la compilazione, qualora nel codice vi siano una o più variabili, viene restituito il seguente avviso di errore:
| |
− | | |
− | relocation R_X86_64_32S against `.bss' can not be used when making a shared object; recompile with -fPIC
| |
− | | |
− | Ciò avviene, ad esempio, nel tentativo di compilazione di un codice di questo tipo:
| |
− | //libprova.c file:
| |
− |
| |
− | static unsigned int car asm("raxregistro");
| |
− | static unsigned int cbr asm("rbxregistro");
| |
− |
| |
− | int prova(int numero) {
| |
− | cbr=numero;
| |
− | asm("mov rbxregistro,%eax");
| |
− | asm("ciclo:");
| |
− | asm("dec %eax");
| |
− | asm("jne ciclo");
| |
− | asm("mov %eax,raxregistro");
| |
− | return car;
| |
− | }
| |
− | Il problema è nel trasferimento del valore contenuto nelle variabili "''raxregistro''" e "''rbxregistro''" alle righe appunto in ''Assembly'':
| |
− | asm ("mov rbxregistro, eax%");
| |
− | asm("mov %eax,raxregistro");
| |
− | e sembra essere legato alle differenze nel codice pic tra i386 e amd64.
| |
− | <BR>Per risolvere il problema è necessario utilizzare uno spostamento relativo di indirizzo; altrimenti si finisce con il codice dipendente di posizione che non funziona per le librerie condivise.
| |
− | <BR>Cosicché, data ad esempio le variabili "''raxregistro''" e "''rbxregistro''" in queste righe:
| |
− | static unsigned int car asm("raxregistro");
| |
− | static unsigned int cbr asm("rbxregistro");
| |
− | per richiamarle bisogna usare l'address-relative, così:
| |
− | asm("mov %eax,raxregistro(%rip)");
| |
− | asm("mov rbxregistro(%rip),%eax");
| |
− | | |
− | Pertanto, l'esempio di codice, sopra mostrato, dovrà - per i sistemi a 64-bit - essere scritto come segue:
| |
− |
| |
− | static unsigned int car asm("raxregistro");
| |
− | static unsigned int cbr asm("rbxregistro");
| |
− |
| |
− | int prova(int numero) {
| |
− | cbr=numero;
| |
− | asm("mov rbxregistro(%rip),%eax");
| |
− | asm("ciclo:");
| |
− | asm("dec %eax");
| |
− | asm("jne ciclo");
| |
− | asm("mov %eax,raxregistro(%rip)");
| |
− | return car;
| |
− | }
| |