2013-07-02 27 views
6

Quindi sto imparando l'assembly x86 Linux con la sintassi NASM (Oh dio, non di nuovo questo, state tutti pensando). Sto provando a creare una subroutine che stamperà semplicemente il valore in EAX sullo stdout. Il codice viene eseguito ed esce senza errori, ma non viene stampato nulla. Non riesco a capire perché. Prima di tutto, qui è il file su cui sto lavorando in:Linux x86 NASM - Subroutine: stampa un dword da EAX

segment .bss 
    to_print: resd 1 

segment .text 
    global print_eax_val 

print_eax_val:     ;  (top) 
    push dword ebx   ;Stack: edx 
    push dword ecx   ;  ecx 
    push dword edx   ;  ebx 
           ;  (bot) 

    mov  ecx,eax    ;ecx = eax 

    mov  [to_print],ecx  ;to_print = ecx 

    mov  eax, 4    ;sys_write 
    mov  ebx, 1    ;to stdout 
    add  ecx, 47    ;add 47 for ASCII numbers 
    mov  edx, 2    ;double word = 2 bytes 
    int  0x80 

    mov  eax, [to_print]  ;eax = original val 
    pop  edx     ;pop the registers back from the stack 
    pop  ecx 
    pop  ebx     ;Stack: empty 

    ret 

Questo è chiamato dal mio file principale, che assomiglia a questo (questo è probabilmente irrilevante, a meno che non mi manca qualcosa di drastico).

segment .data 
     hello db  "Hello world!", 0 
     newline db  0xA 
     len  equ $ - hello 
     len2 equ $ - newline 

segment .text 
     extern print_nl 
     extern print_eax_val 
     global main 

main: 
     enter 0,0 

     call print_nl 

     mov  eax, 1 

     call print_eax_val 

     mov  ebx, 0   ;exit code = 0 (normal) 
     mov  eax, 1   ;exit command 
     int  0x80   ;ask kernel to quit 

print_nl è solo un'altra subroutine che definisce e stampa un ritorno a capo. Questo funziona correttamente e stampa una nuova riga come previsto.

Il problema ha a che fare con il parametro di lunghezza per la mia chiamata sys_write? Sto dando 2, che è la dimensione di un dword, che è la dimensione sia del registro EAX sia della mia etichetta to_print, che ho riservato con resd 1. Ho provato a cambiare la lunghezza a 1, 4, 8, 16 e 32 per disperazione ... Niente ha funzionato.

EDIT: Per chi sta chiedendo, ecco come ho corretto il codice: (Io porrò asterischi su linee che ho cambiato):

segment .bss 
    to_print: resd 1 

segment .text 
     global print_eax_val 

print_eax_val:      ;  (top) 
     push dword ebx   ;Stack: edx 
     push dword ecx   ;  ecx 
     push dword edx   ;  ebx 
            ;  (bot) 

     mov  ecx,eax    ;ecx = eax 

     mov  [to_print],ecx  ;to_print = ecx 

**** add  dword [to_print], 48 

     mov  eax, 4    ;sys_write 
     mov  ebx, 1    ;to stdout 
**** mov  ecx, to_print 
     mov  edx, 2 
     int  0x80 

**** sub  dword [to_print], 48 
     mov  eax, [to_print]  ;eax = original val 
     pop  edx     ;pop the registers back from the stack 
     pop  ecx 
     pop  ebx     ;Stack: empty 

     ret 

In sostanza, ecx deve contenere l'indirizzo del blocco che si desidera stampare, NON il valore stesso. Come indicato nella risposta selezionata, questo sarà solo funziona se eax è compreso nell'intervallo 0-9.

EDIT 2: Quindi ero un po 'confuso riguardo il 2 ° parametro per sys_write (quello memorizzato in edx). Penso che si riferisca solo a un numero di byte. Quindi per uno dword, come stavo usando, sarebbe corretto usare 4 lì, perché una doppia parola è di 4 byte o 32 bit. Immagino che abbia funzionato perché x86 è little-endian. Così nella memoria, il valore esadecimale di to_print sarebbe simile a questa:

90 00 00 00

E con una lunghezza fornito di due, sys_write ottiene:

90 00

Quindi il valore non lo fa per fortuna essere corrotto

poi ho cambiato il codice per memorizzare to_print come un byte, invece, utilizzando resb 1 e accedere utilizzando byte invece di dword ... Un byte è bene qui, perché so che non ho intenzione di dare to_print un valore superiore 9.

risposta

4

Sono molto arrugginito all'assembler, ma sembra che ECX contenga il valore 48, quando dovrebbe contenere l'indirizzo del buffer da scrivere.

presumo quello che intendeva in print_eax_val è quello di prendere il valore binario di EAX, aggiungere l'offset cifre 0 (che avrebbe dovuto essere 48, non 47) e quindi stampare quel singolo carattere ASCII.Per fare ciò, aggiungi 48 prima di memorizzare il valore in to_print, inserisci l'indirizzo di to_print in ECX e imposta la lunghezza (EDX) su 1, perché stai scrivendo solo un carattere.

Ora ricorda che questo funzionerà solo per i valori EAX tra 0x0000 e 0x0009. Quando andrai oltre 9 otterrai altri caratteri ASCII.

Spiegare come prendere un valore binario arbitrario di EAX e convertirlo in una stringa decimale che può essere stampata va ben oltre lo scopo di SO.

+0

Grazie mille! La tua risposta è stata molto utile. Sì, mi rendo conto che funzionerà solo da 0 a 9, sto solo cercando di ottenere alcune delle nozioni di base. Forse inizierò con qualche business array-pointer int-to-string alla fine. Modificherò il mio post con il codice aggiornato, per chiunque altro voglia sapere. –

+3

Ti applaudo per aver affrontato il linguaggio dell'assemblaggio e ti auguro buona fortuna. A proposito, la tua domanda è una domanda prototipo di SO buono. Hai fornito esattamente le informazioni necessarie, hai mostrato ciò che avevi provato e hai fatto una domanda specifica. +1 –

+1

Grazie ancora, e classificherei sicuramente la tua risposta come una risposta "prototipo buono SO". Ho deciso di imparare alcuni assembly x86 dopo un gigantesco tracollo di segfault/pointer C++ in un progetto su cui sto lavorando. Prima di tutto, è un bel diversivo, in secondo luogo, voglio sapere cosa sto * effettivamente * facendo scrivendo il codice. –

3

La chiamata di sistema write richiede un buffer di caratteri da stampare, indicato da %ecx con una lunghezza fornita da %edx. Un registro come %eax contiene invece un numero intero a 32 bit.

Quindi se si desidera stampare veramente %eax, è necessario prima convertire tale numero intero in una sequenza di caratteri. Puoi copiarlo in decimale ASCII, esadecimale o qualsiasi altra base che ti piace, ma devi trasformarlo in caratteri.

+0

Grazie per il tuo aiuto! La tua risposta è stata del tutto d'aiuto (internet è in realtà sorprendentemente scarso in q & a linguaggio assembly, o forse non so dove guardare). Prima ho avuto l'altra risposta, ma se potessi selezionare due risposte corrette, lo farei. –

+0

Nit molto minore: la dimensione di un 'dword' è 4, non 2 ... –

+0

Hah, nessun errore è" minore "nel montaggio (da quanto ho imparato finora). Ero confuso su cosa avrei dovuto inserire in quel parametro di lunghezza. Vedi Modifica 2 nel mio post per ulteriori spiegazioni. Ma grazie! –