[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [home] [volume] [parte]


Capitolo 565.   Compilazione di programmi composti da più file sorgenti

Per poter compilare un programma distribuito tra più file sorgenti, all'interno di questi file occorre dichiarare quali simboli (riferiti a variabili e funzioni) devono essere pubblici e come tali accessibili anche dagli altri; inoltre, nei file in cui si fa riferimento a simboli esterni, occorre dichiarare questa dipendenza.

565.1   Inclusione di file

Prima di affrontare il problema del collegamento di più file oggetto in un file eseguibile singolo, conviene considerare l'inclusione automatica del contenuto di un file. In altri termini, si può ottenere una funzione simile al «copia-incolla», dichiarando in un file che, in un certo punto, va incluso il contenuto di un altro. Per esempio, per incorporare in un certo punto, il contenuto del file funzioni.s, occorre scrivere la direttiva seguente:

...
; GNU AS
.include "funzioni.s"
...
...
; NASM
%include "funzioni.s"
...

Naturalmente, il file funzioni.s contiene qualcosa che si può copiare e incollare, tale e quale, in quel certo punto del sorgente che vi fa riferimento.

Il file in questione viene cercato nella directory corrente, ma nella riga di comando di GNU AS è possibile aggiungere l'opzione -I per indicare altri percorsi di ricerca; nello stesso modo, con NASM si può usare l'opzione -i:

as -I directory ...[Invio]

nasm -i directory ...[Invio]

565.2   Due file sorgenti da collegare assieme

Per dimostrare come si gestiscono più file sorgenti assieme, viene mostrato un esempio molto semplice, composto da due soli file: nel primo si trova la funzione _start; nel secondo si trova la funzione f_rs che esegue una ricerca sequenziale all'interno di un array (nella sezione 562.1 si vede lo stesso programma, tutto intero, in un file unico). I due listati seguenti sono realizzati per GNU AS:

# rs-main.s
#
.section .data
lista:  .int    1, 4, 3, 7, 9, 10, 22, 44, 11, 23   # Interi senza segno.
a:      .int    0                                   # Indice minimo.
z:      .int    9                                   # Indice massimo.
#
.section .text
.globl _start
.extern f_rs
#
_start:
    push  z                # f_rs ($lista, $7, a, z) ==> EAX
    push  a                # Si cerca il valore 7 nell'array
    push  $7               # «lista», tra gli indici «a» e «z».
    push  $lista           # Viene restituito l'indice dell'elemento
    call  f_rs             # trovato, oppure -1 se non è presente.
    add   $16, %esp        #
bp1:
    mov   %eax, %ebx       # Restituisce l'indice trovato,
    mov   $1, %eax         # ammesso che sia abbastanza piccolo
    int   $0x80            # da poter essere rappresentato come
                           # valore di uscita.
# rs-f.s
#
.section .data
#
.section .text
.globl f_rs
#
# Ricerca sequenziale all'interno di una lista di valori.
# f_rs (lista, x, a, z) ==> EAX
# Al termine EAX contiene l'indice del valore trovato,
# oppure -1 se questo non c'è.
#
f_rs:
    enter $4, $0
    pusha
    .equ  rs_i,     -4           # Gli si associa EAX.
    .equ  rs_lista,  8           # Gli si associa ESI.
    .equ  rs_x,     12           # Gli si associa EDX.
    .equ  rs_a,     16
    .equ  rs_z,     20
    #
    mov   rs_lista(%ebp), %esi   # ESI contiene l'indirizzo dell'array.
    mov   rs_x(%ebp),     %edx   # EDX contiene il valore cercato.
    #
    mov   rs_a(%ebp),     %eax   # EAX viene usato come indice di scansione.
f_rs_loop:
    cmp   rs_z(%ebp),     %eax   # Se EAX è maggiore dell'indice massimo,
    ja    f_rs_non_trovato       # l'elemento cercato non c'è.
    #
    cmp   (%esi,%eax,4),  %edx   # Se il valore cercato corrisponde a quello
    je    f_rs_trovato           # dell'indice corrente, termina la scansione.
    #
    inc   %eax                   # Incrementa l'indice di scansione e
    jmp   f_rs_loop              # salta all'inizio del ciclo.
    #
f_rs_non_trovato:
    popa                   # Conclude la funzione con EAX = -1.
    mov $-1, %eax          #
    leave                  #
    ret                    #
f_rs_trovato:
    mov %eax, rs_i(%ebp)   # Salva EAX nella variabile locale prevista.
    popa                   # Conclude la funzione con EAX pari
    mov rs_i(%ebp), %eax   # al valore salvato nella variabile
    leave                  # locale.
    ret                    #

Nel primo dei due listati, corrispondente al file rs-main.s, si deve osservare la dichiarazione esterna del simbolo f_rs, corrispondente al nome della funzione contenuta nel file rs-f.s.

...
.section .text
.globl _start
.extern f_rs
...

Dal momento che i dati necessari all'elaborazione vengono passati alla funzione attraverso i parametri della chiamata, a parte _start, non ci sono altre dichiarazioni di simboli pubblici nel file f-main.s. Nel secondo listato, corrispondente al file rs-f.s, il simbolo f_rs viene reso pubblico, per consentire al file rs-main.s di farvi riferimento.

...
.section .text
.globl f_rs
...

Seguono gli stessi due listati, nella versione adatta a NASM:

; rs-main.s
;
section .data
lista:  dd    1, 4, 3, 7, 9, 10, 22, 44, 11, 23       ; Interi senza segno.
a:      dd    0                                       ; Indice minimo.
z:      dd    9                                       ; Indice massimo.
;
section .text
global _start
extern f_rs
;
_start:
    push  long [z]         ; f_rs ($lista, $7, a, z) ==> EAX
    push  long [a]         ; Si cerca il valore 7 nell'array
    push  long 7           ; «lista», tra gli indici «a» e «z».
    push  lista            ; Viene restituito l'indice dell'elemento
    call  f_rs             ; trovato, oppure -1 se non è presente.
    add   esp, 16          ;
bp1:
    mov   ebx, eax         ; Restituisce l'indice trovato,
    mov   eax, 1           ; ammesso che sia abbastanza piccolo
    int   0x80             ; da poter essere rappresentato come
                           ; valore di uscita.
; rs-f.s
;
section .data
;
section .text
global f_rs
;
; Ricerca sequenziale all'interno di una lista di valori.
; f_rs (lista, x, a, z) ==> EAX
; Al termine EAX contiene l'indice del valore trovato,
; oppure -1 se questo non c'è.
;
f_rs:
    enter 4, 0
    pusha
    rs_i     equ -4              ; Gli si associa EAX.
    rs_lista equ  8              ; Gli si associa ESI.
    rs_x     equ 12              ; Gli si associa EDX.
    rs_a     equ 16
    rs_z     equ 20
    ;
    mov   esi, [rs_lista+ebp]    ; ESI contiene l'indirizzo dell'array.
    mov   edx, [rs_x+ebp]        ; EDX contiene il valore cercato.
    ;
    mov   eax, [rs_a+ebp]        ; EAX viene usato come indice di scansione.
f_rs_loop:
    cmp   eax, [rs_z+ebp]        ; Se EAX è maggiore dell'indice massimo,
    ja    f_rs_non_trovato       ; l'elemento cercato non c'è.
    ;
    cmp   edx, [esi+eax*4]       ; Se il valore cercato corrisponde a quello
    je    f_rs_trovato           ; dell'indice corrente, termina la scansione.
    ;
    inc   eax                    ; Incrementa l'indice di scansione e
    jmp   f_rs_loop              ; salta all'inizio del ciclo.
    ;
f_rs_non_trovato:
    popa                   ; Conclude la funzione con EAX = -1.
    mov eax, -1            ;
    leave                  ;
    ret                    ;
f_rs_trovato:
    mov [rs_i+ebp], eax    ; Salva EAX nella variabile locale prevista.
    popa                   ; Conclude la funzione con EAX pari
    mov eax, [rs_i+ebp]    ; al valore salvato nella variabile
    leave                  ; locale.
    ret                    ;

In questo caso, le direttive salienti sono, rispettivamente:

...
section .text
global _start
extern f_rs
...
...
section .text
global f_rs
...

Per compilare il tutto in un solo file eseguibile, occorre procedere secondo i comandi seguenti. Nel caso di GNU AS:

as --gstabs -o rs-main.o rs-main.s[Invio]

as --gstabs -o rs-f.o rs-f.s[Invio]

ld -o rs rs-main.o rs-f.o[Invio]

Nel caso di NASM:

nasm -g -f elf -o rs-main.o rs-main.s[Invio]

nasm -g -f elf -o rs-f.o rs-f.s[Invio]

ld -o rs rs-main.o rs-f.o[Invio]

Questo programma restituisce l'indice dell'elemento cercato e trovato nell'array. In questo caso, si tratta del quarto elemento che corrisponde all'indice 3:

./rs ; echo $?[Invio]

3

565.3   Incorporazione di codice in linguaggio C

Per collegare assieme sorgenti scritti in linguaggi differenti, si agisce in modo analogo a quanto già mostrato per il solo linguaggio assemblatore. C'è però da considerare che ogni compilatore ha le proprie caratteristiche, sia per ciò che riguarda le convenzioni di chiamata delle funzioni, sia per il modo di nominare i simboli associati alle funzioni stesse. Nel caso di GNU CC, valgono le convenzioni di chiamata comuni e i nomi delle funzioni non vengono modificati.

Qui si mostra un listato, in linguaggio C, da usare in sostituzione del file rs-f.s descritto nella sezione precedente:

/* f_rs (<lista>, <x>, <ele-inf>, <ele-sup>) */

int f_rs (int lista[], int x, int a, int z)
{
    int i;

    /* Scandisce l'array alla ricerca dell'elemento. */

    for (i = a; i <= z; i++)
      {
        if (x == lista[i])
          {
            return i;
          }
      }

    /* La corrispondenza non è stata trovata. */

    return -1;
}

Per compilare questo file e generare un file oggetto, ammesso che il sorgente si chiami rs-f.c, si procede con il comando seguente:

cc -c -o rs-f.o rs-f.c[Invio]

Il collegamento con il file rs-main.o avviene nel modo già visto:

ld -o rs rs-main.o rs-f.o[Invio]


Appunti di informatica libera 2008 --- Copyright © 2000-2008 Daniele Giacomini -- <appunti2 (ad) gmail·com>


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome compilazione_di_programmi_composti_da_piu_file_sorgenti.htm

[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [home]

Valid ISO-HTML!

CSS validator!

Gjlg Metamotore e Web Directory