2013-03-08 16 views
7

Ho provato a eseguire ricerche su tutorial di assemblaggio intel x64 con esempi o un buon libro ma non l'ho trovato nemmeno nel sito Intel.Esercitazioni di assemblaggio Intel X86-64 o libro

quindi, potresti suggerirmi un buon tutorial o prenotare per quello ?? Sto usando nasm su linux.

Grazie

+2

Non conosco le esercitazioni su nasm, ma è possibile ottenere i [Intel Software Developer Manuals] (http://www.intel.com/content/www/us/en/processors/architectures-software-developer- manuals.html), che dovresti trovare abbastanza utile. –

+0

Un tutorial su intel.com? Sembra una barzelletta. Inizia con [il tutorial di Paul Carter] (http://www.drpaulcarter.com/pcasm/). Sorprendentemente, è basato su NASM. Quando si master assembly a 32 bit, passare a 64. –

+0

vi consiglio Introduzione a 64 bit di programmazione Intel linguaggio Assembler per Linux da Ray Seyfarth. Il libro utilizza YASM al posto di NASM, ma YASM accetta AFAIK quasi tutto il codice NASM e supporta anche il formato di dati di debug di DWARF2 ecc. Il libro ha anche un codice SSE e AVX, e presuppone solo alcuni sottofondi di programmazione e spiega anche numeri binari ed esadecimali ecc. Ho imparato l'assemblaggio di x86 nei giorni DOS, ma il libro funge da utile riferimento su come sono fatte le cose nell'assemblaggio di Linux x86-64. – nrz

risposta

15

Certo si tratta di pregiudizi personali come si preferisce per conoscere la programmazione.

Ma per quanto riguarda i linguaggi di assemblaggio in particolare, ho trovato un approccio che per me è stato più utile della lettura di manuali di riferimento e/o di libri di istruzioni sul linguaggio assembly (dove esistono).

Quello che faccio normalmente per capire come funziona l'assemblaggio per una nuova CPU/una CPU sconosciuta su una piattaforma OS con cui non ho ancora lavorato è sfruttare la toolchain dello sviluppatore. Così:

  • installa te stesso un (cross-) compilatore e disassemblatore per la CPU di destinazione. Oggigiorno, l'ubiquità di GNU gcc/binutils spesso significa che sono gcc e objdump -d.

  • creare un gruppo di piccoli programmi/piccoli pezzi di codice sorgente, come:

extern int funcA(int arg); 
extern int funcB(int arg1, int arg2); 
extern int funcC(int arg1, int arg2, int arg3); 
extern int funcD(int arg1, int arg2, int arg3, int arg4); 
extern int funcE(int arg1, int arg2, int arg3, int arg4); 
extern int funcF(int arg1, int arg2, int arg3, int arg4, int arg5); 
extern int funcG(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6); 
extern int funcH(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, 
       int arg7); 

int main(int argc, char **argv) 
{ 
    printf("sum of all funcs: %d\n", 
     funcA(1) + funcB(2, 3) + funcC(4, 5, 6) + funcD(7, 8, 9, 10) + 
     funcE(11, 12, 13, 14, 15) + funcF(16, 17, 18, 19, 20, 21) + 
     funcG(22, 23, 24, 25, 26, 27, 28) + funcH(29, 30, 31, 32, 33, 34, 35)); 
    return 12345; 
}
  • compilare questi con l'ottimizzazione del compilatore e smontare il codice oggetto generato.
    La struttura del codice è abbastanza semplice da dimostrare come l'ABI si comporta correttamente. alla funzione chiamando, passando argomenti e valori restituiti, gestione dello spazio di registrazione wrt. a quali registri sono conservati/volatili quando si effettuano chiamate di funzione. Ti mostrerà anche codice di assembly di base per inizializzare i dati costanti e "incollare" come l'accesso e la gestione dello stack.

  • estendere questo per semplici costrutti in linguaggio C come loop e dichiarazioni if/else o switch. Conserva sempre alcune chiamate a funzioni esterne indefinite perché così facendo impedirai al compilatore di lanciare tutto il tuo "codice test" e quando usi i test if() di switch(), predicato su argc (o altri argomenti di funzione) perché il compilatore non può prevedere che entrambi (e quindi ottimizzare gli elementi costitutivi del codice "stranamente").

  • estendono questo per usando struct {} e class {} definizioni contenenti sequenze di tipi di dati primitivi, al fine di scoprire come il compilatore organizza questi in memoria, che le istruzioni di montaggio sono utilizzati per accedere byte/parole/ints/longitudine/galleggianti ecc.
    Tutti questi pezzi di codice di test possono variare volutamente (ad esempio, utilizzare operazioni diverse da +) e/o renderli più complessi al fine di ottenere ulteriori informazioni sui pezzi specifici dell'insieme di istruzioni e dell'ABI.

Dopo aver fatto questo, e guardato in uscita, individuare una copia (elettronica e non) del piattaforma ABI. Questo contiene il regolamento per come viene fatto/perché è fatto in questo modo, e ti aiuterà a capire perché queste regole si applicano alla piattaforma specifica. È essenziale avere un'idea di quanto sopra perché quando scrivi il tuo codice assembly, dovrai interfacciarlo con altri non assembly (a meno che non si tratti di demo). È lì che devi giocare secondo le regole, quindi anche se non le conosci a memoria, almeno sai dov'è il regolamento.

Solo dopo vorrei suggerire di rintracciare effettivamente il riferimento del set di istruzioni per la piattaforma specifica.

Ecco perché, se prima hai esaminato quanto sopra, hai già abbastanza esperienza/hai già visto abbastanza per iniziare con un piccolo programma in C, compilarlo in assembly, modificarlo un po ', assemblarlo e collegalo e vedi se la tua modifica fa quello che dovrebbe fare.

Il tentativo di utilizzare alcune istruzioni non comuni/specializzate sarà molto più semplice perché hai già visto come funziona la funzione di chiamata, che tipo di codice di colla è necessario per interfacciare il tuo assieme con altre parti del programma, hai già usato la toolchain, quindi non hai più bisogno di iniziare completamente da zero.

Vale a dire, per riassumere tutto questo, il mio suggerimento è quello di imparare il montaggio dal verso il basso anziché dal basso verso l'alto.

Nota a margine:

Perché sto suggerendo di utilizzare l'ottimizzazione compilatore quando si analizza il codice assembly generato dal compilatore per questi semplici esempi?
Bene, la risposta è perché, contrariamente ad alcuni, il codice assembly generato è molto più semplice se si consente al compilatore di ottimizzare l'inferno delle cose. Senza ottimizzazione, i compilatori creano spesso codice "stupido" che, ad es. mette tutte le variabili nello stack, salva e li ripristina da lì per nessun motivo si può vedere, non registro salva/ripristini/inizializzazione solo per sovrascrivere il REG La molto prossima istruzione e molte altre cose del genere. La quantità di codice emesso è molto più grande a causa di questo. È pepato di cruft e molto più difficile da capire. L'ottimizzazione del compilatore impone di ridurre questo carico all'essenziale, che è ciò che si vuole vedere per capire la piattaforma ABI e l'asseribilmente. Pertanto, utilizzare l'ottimizzazione del compilatore.

+0

Questo è un ottimo idead/risposta. Grazie mille. – gideon

+0

Questa è una delle migliori risposte che abbia mai visto su SO. –