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


Capitolo 564.   Interazione con il sistema operativo

È possibile gestire un certo grado di comunicazione tra il programma in linguaggio assemblatore e il sistema GNU/Linux. In particolare si possono ottenere i parametri della chiamata del programma (gli argomenti della riga di comando) ed è possibile chiamare delle funzioni di sistema attraverso delle «interruzioni».

564.1   Parametri di chiamata del programma

All'avvio del programma, questo riceve una pila contenente il numero degli argomenti della riga di comando (nome del programma incluso), il nome del programma che è stato avviato e quindi gli argomenti stessi. Si può realizzare un sorgente molto semplice per l'indagine con GDB:

.section .text
.globl _start
_start:
    mov   %esp, %ebp
bp1:
    mov   $0, %ebx    # Restituisce zero.
    mov   $1, %eax    #
    int   $0x80       #
section .text
global _start
_start:
    mov   ebp, esp
bp1:
    mov   ebx, 0    ; Restituisce zero.
    mov   eax, 1    ;
    int   0x80      ;

Supponendo che il programma compilato si chiami argomenti, lo si avvia sotto il controllo di GDB nello stesso modo di sempre:

gdb argomenti[Invio]

Gli argomenti del programma vanno passati necessariamente attraverso un comando di GDB:

(gdb) set args 1 2 3 4 "ciao amore"[Invio]

Si fissa il punto di sospensione del programma e quindi si avvia:

(gdb) break bp1[Invio]

(gdb) run[Invio]

Il programma viene sospeso in corrispondenza dell'etichetta bp1 e si può consultare la pila, o più precisamente lo stack frame della pila:

(gdb) backtrace[Invio]

#0  bp1 () at argomenti.s:6
#1  0x00000006 in ?? ()
#2  0xbfeb1a89 in ?? ()
#3  0xbfeb1a98 in ?? ()
#4  0xbfeb1a9a in ?? ()
#5  0xbfeb1a9c in ?? ()
#6  0xbfeb1a9e in ?? ()
#7  0xbfeb1aa0 in ?? ()
#8  0x00000000 in ?? ()

In questa situazione non sono presenti variabili locali; quindi, nella pila, dopo l'indirizzo corrispondente all'etichetta bp1 (che si trova lì solo perché si sta usando GDB ed è stato sospeso il corso del programma), appare la quantità di argomenti (sei); gli elementi successivi contengono dei puntatori alle stringhe che rappresentano i vari argomenti ricevuti (si osservi che gli argomenti sono rappresentati tutti in forma di stringa), stringhe che sono tutte terminate con un byte a zero. Per leggere gli argomenti con GDB si devono fare dei tentativi; qui vengono indicate le dimensioni esatte, ma se si usano dimensioni maggiori si possono vedere delle porzioni degli argomenti successivi:

(gdb) print (char[33])*0xbfeb1a89[Invio]

$1 = "/tmp/argomenti\0001\0002\0003\0004\000ciao amore"

In questo caso si legge il primo argomento, ma usando una dimensione eccessiva, si vedono di seguito anche gli altri, separati dai vari byte a zero, rappresentati con la sequenza \000. Il primo argomento da solo sarebbe:

(gdb) print (char[14])*0xbfeb1a89[Invio]

$2 = "/tmp/argomenti"

Gli altri argomenti:

(gdb) print (char[1])*0xbfeb1a98[Invio]

$3 = "1"

(gdb) print (char[1])*0xbfeb1a9a[Invio]

$4 = "2"

(gdb) print (char[1])*0xbfeb1a9c[Invio]

$5 = "3"

(gdb) print (char[1])*0xbfeb1a9e[Invio]

$6 = "4"

(gdb) print (char[9])*0xbfeb1aa0[Invio]

$7 = "ciao amore"

(gdb) quit[Invio]

564.2   Funzioni del sistema operativo

Si accede alle funzioni offerte dal sistema operativo attraverso quella che è nota come «interruzione software» (interrupt) e si genera con l'istruzione INT. Per la precisione, in un sistema GNU/Linux occorre l'interruzione 8016 (12810), come è stato mostrato in tutti gli esempi apparsi fino a questo punto. Per selezionare il tipo di funzione e per passarle degli argomenti si usano i registri in questo modo:

Registro Utilizzo
EAX Contiene il numero che rappresenta la funzione di sistema.
EBX Primo parametro della funzione.
ECX Secondo parametro della funzione.
EDX Terzo parametro della funzione.
ESI Quarto parametro della funzione.
EDI Quinto parametro della funzione.

Se la funzione deve restituire un valore, questo viene ottenuto attraverso il registro EAX.

Per una mappa completa delle chiamate di sistema si può consultare <http://web.archive.org/web/2005/www.lxhp.in-berlin.de/lhpsysc0.html>, come annotato nei riferimenti alla fine del capitolo. Qui vengono mostrate delle tabelle riepilogative di alcune funzioni importanti.

Pagina di manuale Descrizione EAX EBX ECX EDX
exit(2) Conclude il funzionamento del programma restituendo un valore. 1 Valore da restituire: numero intero compreso tra zero e 255.
read(2) Legge da un descrittore di file e mette i dati letti in una memoria tampone. Attraverso EAX restituisce la quantità di byte letta effettivamente e aggiorna il puntatore del file per la lettura successiva. 3 Descrittore del file da leggere. Indirizzo iniziale della memoria tampone. Dimensione in byte della memoria tampone.
write(2) Scrive il contenuto di una memoria tampone in un descrittore di file. Attraverso EAX restituisce la quantità di byte scritta effettivamente. 4 Descrittore del file da scrivere. Indirizzo iniziale della memoria tampone. Dimensione in byte della memoria tampone.

564.3   Esempi di lettura e scrittura con i flussi standard

Di solito, il primo programma che si scrive è quello che visualizza un messaggio e termina, ma in questo caso, un'operazione così semplice sul piano teorico, in pratica è già abbastanza complicata. Quello che segue è un programma che, avvalendosi di una chiamata di sistema, visualizza un messaggio attraverso lo standard output. Come si può osservare, si utilizza anche una tecnica per far calcolare al compilatore la lunghezza della stringa da visualizzare.

#
.equ SYS_EXIT,  1               # exit(2)
.equ SYS_WRITE, 4               # write(2)
.equ STDOUT,    1               # Descrittore di standard output.
#
.section .data                  # Qui si dichiara la stringa da
msg:  .ascii "Ciao a tutti!\n"  # visualizzare, calcolandone la
size = . - msg                  # dimensione.
#
.section .text
.globl _start
_start:
    mov   $SYS_WRITE, %eax      # Scrive nello standard output.
    mov   $STDOUT,    %ebx      #
    mov   $msg,       %ecx      #
    mov   $size,      %edx      #
    int   $0x80
exit:
    mov   $SYS_EXIT,  %eax      # Conclude il funzionamento.
    mov   $0,         %ebx      #
    int   $0x80
;
SYS_EXIT   equ     1            ; exit(2)
SYS_WRITE  equ     4            ; write(2)
STDOUT     equ     1            ; Descrittore di standard output.
;
section .data                   ; Qui si dichiara la stringa da
msg:  db  "Ciao a tutti!", 0x0A ; visualizzare, calcolandone la
size  equ $ - msg               ; dimensione.
;
section text
global _start
_start:
    mov   eax, SYS_WRITE        ; Scrive nello standard output.
    mov   ebx, STDOUT           ;
    mov   ecx, msg              ;
    mov   edx, size             ;
    int   0x80
exit:
    mov   eax, SYS_EXIT         ; Conclude il funzionamento.
    mov   ebx, 0                ;
    int   0x80

Segue un esempio di programma che legge dallo standard input e scrive ciò che ha letto attraverso lo standard output. Come già nell'esempio precedente, vengono dichiarate inizialmente delle costanti per semplificare la lettura del codice; inoltre vengono usate aree di memoria non inizializzate e delle funzioni banali senza parametri, per le quali non si utilizzano variabili locali. Si mostrano due listati, uno adatto per GNU AS e l'altro per NASM.

#
.equ SYS_EXIT,  1               # exit(2)
.equ SYS_READ,  3               # read(2)
.equ SYS_WRITE, 4               # write(2)
.equ STDIN,     0               # Descrittore di standard input.
.equ STDOUT,    1               # Descrittore di standard output.
.equ STDERR,    2               # Descrittore di standard error.
.equ MAX_SIZE,  1000            # Dimensione massima dei dati da leggere.
#
.section .data                  # Non ci sono variabili già inizializzate.
#
.section .bss
.lcomm record, MAX_SIZE         # Memoria tampone per la lettura dei dati.
.lcomm size, 4                  # Quantità di byte letti effettivamente.
#
.section .text
.globl _start
_start:
read_write_begin:
    call  read                  # Legge dallo standard input.
    cmp   $0, %eax              # Se sono stati letti zero byte,
    jz    read_write_end        # il ciclo termina.
    call  write                 # Scrive i byte letti nello standard output.
    jmp   read_write_begin      # Ripete il ciclo.
read_write_end:
    jmp   exit
read:
    mov   $SYS_READ,  %eax      # Legge dallo standard input.
    mov   $STDIN,     %ebx      #
    mov   $record,    %ecx      #
    mov   $MAX_SIZE,  %edx      #
    int   $0x80
    mov   %eax,       size      # Salva la dimensione letta effettivamente.
    ret
write:
    mov   $SYS_WRITE, %eax      # Scrive nello standard output.
    mov   $STDOUT,    %ebx      #
    mov   $record,    %ecx      #
    mov   size,       %edx      #
    int   $0x80
    ret
exit:
    mov   $SYS_EXIT,  %eax      # Conclude il funzionamento.
    mov   $0,         %ebx      #
    int   $0x80
;
SYS_EXIT   equ     1            ; exit(2)
SYS_READ   equ     3            ; read(2)
SYS_WRITE  equ     4            ; write(2)
STDIN      equ     0            ; Descrittore di standard input.
STDOUT     equ     1            ; Descrittore di standard output.
STDERR     equ     2            ; Descrittore di standard error.
MAX_SIZE   equ  1000            ; Dimensione massima dei dati da leggere.
;
section .data                   ; Non ci sono variabili già inizializzate.
;
section .bss
record resb MAX_SIZE            ; Memoria tampone per la lettura dei dati.
size   resd 1                   ; Quantità di byte letti effettivamente.
;
section .text
global _start
_start:
read_write_begin:
    call  read                  ; Legge dallo standard input.
    cmp   eax, 0                ; Se sono stati letti zero byte,
    jz    read_write_end        ; il ciclo termina.
    call  write                 ; Scrive i byte letti nello standard output.
    jmp   read_write_begin      ; Ripete il ciclo.
read_write_end:
    jmp   exit
read:
    mov   eax, SYS_READ         ; Legge dallo standard input.
    mov   ebx, STDIN            ;
    mov   ecx, record           ;
    mov   edx, MAX_SIZE         ;
    int   0x80
    mov   [size], eax           ; Salva la dimensione letta effettivamente.
    ret
write:
    mov   eax, SYS_WRITE        ; Scrive nello standard output.
    mov   ebx, STDOUT           ;
    mov   ecx, record           ;
    mov   edx, [size]           ;
    int   0x80
    ret
exit:
    mov   eax, SYS_EXIT         ; Conclude il funzionamento.
    mov   ebx, 0                ;
    int   0x80

564.4   Riferimenti


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 interazione_con_il_sistema_operativo.htm

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

Valid ISO-HTML!

CSS validator!

Gjlg Metamotore e Web Directory