ABSTRACT Possiamo buttare completamente PWB! Ecco come scrivere una Applicazione completamente Assembler dentro MSVC con tutta la ricchezza dell’IDE. Presentiamo anche l’"anatomia" di un file Obj e qualche tool di "dissezione". (sconsigliato per i deboli di stomaco di derivazione Java/VB) INTRO Abbiamo già visto come sia possibile integrare facilmente codice assembler a 32 bit, scrivere file sorgente esterni, con file "C" e "C++" all’interno di progetto MSVC 6.0 e anche con Ide successivi quando il main file era comunque C. Vediamo se è possibile scrivere completamente in Assembler. BREVE RADIOGRAFIA DI UN ESEGUIBILE Riassumiamo in breve come si genera un "exe". Compilatore Ogni file sorgente viene passato al compilatore che ne legge il contenuto e genera il binario corrispondente. L’IDE di MSVC chiama il suo compilatore interno, che è un compilatore C++, oppure chiama altri compliatori, se si sono personalizzati i settaggi. (vedi articolo precedente). Oltre a generare il puro codice binario nel rispettivo file OBJ, produce una particolare struttura dati descrittiva, contenente i nomi delle funzioni e variabili dichiarati nel file e disponibili per altri moduli (diremo: "simboli esportati"), ma anche contenente i simboli "richiesti" dall’obj per poter funzionare: diciamo i simboli "importati". Tale struttura viene comunemente detta "symbol table". Symbol Table: un esempio Se compiliamo il file sum.asm già citato nel precedente articolo, ; SUM.ASM .386 ; set di istruzioni da utilizzare; anche .586 .MODEL FLAT, C ; SOLO FLAT per win32; C per modello param sullo stack anche se qui li estraiamo "a mano" .CODE ; inizio codice ; .STACK Sum PROC MOV EDX, ESP MOV EAX, [EDX+4] MOV EBX, [EDX+8] ADD EAX,EBX RET Sum ENDP; fine proc Possiamo facilmente vedere il suo contenuto tramite il tool, invocabile solo da linea di comando, dumpbin. Se digitiamo: dumpbin /all E:\lavori_NT\MIXED1\SUM.obj Otterremo: COFF SYMBOL TABLE 000 00000000 DEBUG notype Filename | .file .\SUM.ASM 002 00000000 SECT1 notype Static | .text Section length B, #relocs 0, #linenums 5, checksum 0 004 00000000 SECT2 notype Static | .data Section length 0, #relocs 0, #linenums 0, checksum 0 006 00000000 SECT1 notype () External | _Sum tag index 00000008 size 0000000B lines 0000006F next function 00000000 008 00000000 SECT1 notype BeginFunction | .bf line# 000b end 00000000 00A 00000005 SECT1 notype .bf or.ef | .lf 00B 0000000B SECT1 notype EndFunction | .ef line# 0012 (solo parte del lungo dump). Si noti il nome della f. esportata. Analogamente nel file obj del main: 017 00000000 UNDEF notype () External | _Subtract 018 00000000 UNDEF notype () External | _Sum dove appunto vengono richieste le due f. Subtract e Sum. Linker Il Linker riceve in input TUTTI i file OBJ del progetto, più le Librerie ( per inciso, le librerie NON SONO stdio.h, stdlib.h come dicono alcuni ….) in formato *.lib e tentano di risolvere tutte gli export con tutti gli import. La regola è:
Alla fine, con fatica, il linker sputa fuori un EXE, ma non è finita.. Supporto Run-Time ed entry point Oltre ad unire tutti gli obj ed a risolvere nomi chiamate, il linker aggiunge anche alcune informazioni che serviranno al s.o. per caricare e lanciare il ns programma. Il lancio di un programma richiede, fra le altre innumerevoli cose, un entry point, un punto di ingresso, o per dirla più dura e giusta, un indirizzo a cui fare CALL o JUMP per cominciare il programma. Tale punto deve, ovviamente, essere univoco e viene creato a partire da una label generata automaticamente dal compilatore C, o specificata a mano nel sorgente ASM. Tale label passa quindi nell’obj e poi nell’exe. IL PROGETTO CON SOLO FILE ASM Partiamo al solito da un progetto, ma stavolta vuoto, di tipo Console Application: Creiamo un nuovo file, p.es. hello.asm: occhio: estensione .ASM ! E copiamoci dentro p.es. l’esempio per NT fornito col MASM: .386 .MODEL flat, stdcall STD_OUTPUT_HANDLE EQU -11 GetStdHandle PROTO NEAR32 stdcall, nStdHandle:DWORD WriteFile PROTO NEAR32 stdcall, hFile:DWORD, lpBuffer:NEAR32, nNumberOfBytesToWrite:DWORD, lpNumberOfBytesWritten:NEAR32, lpOverlapped:NEAR32 ExitProcess PROTO NEAR32 stdcall, dwExitCode:DWORD .STACK 4096 .DATA msg DB "Hello, world.", 13, 10 written DD 0 hStdOut DD 0 .CODE _start: INVOKE GetStdHandle, STD_OUTPUT_HANDLE ; Standard output handle mov hStdOut, eax INVOKE WriteFile, hStdOut, ; File handle for screen NEAR32 PTR msg, ; Address of string LENGTHOF msg, ; Length of string NEAR32 PTR written, ; Bytes written 0 ; Overlapped mode INVOKE ExitProcess, 0 ; Result code for parent process PUBLIC _start END NOTA: potremmo anche importare il file .MAK, ma vogliamo vedere bene come impostare i settaggi. Ora parte la fase dei settaggi delle opzioni, non solo di compilazione, ma anche di linkaggio. Come nel precedente articolo, personalizziamo i "Settings" (click col pulsante destro): Custom Build: nell’edit box "Commands" vanno messi tutti quei comandi da command line che producono l’OBJ invocando l’Assemblatore: c:\masm611\bin\ml /c /Cx /coff /Zd $(inputpath) dove appunto c:\masm611\bin\ml è il masm ml.exe con qualche opzione: /c = Assembla SENZA linker (usiamo il linker di Visual Studio) /coff = Common Object file Format: che tipo di OBJ generare /Zd = Aggiungi nomi per debugging a livello sorgente $(inputpath) è una variabile dell’ambiente ed ha lo stesso nome del file Nell’edit box va specificato il nome del file di uscita, e la cosa più comoda è: $(InputName).obj in modo che abbia lo stesso nome del file sorgente ma con estensione OBJ. OSS: se non ci sono particolari esigenze di PATH e/o NOMI, usare $(inputpath) e $(InputName).obj Si dovrebbe avere: La Compilazione Col classico F5 lanciamo la compilazione, e compila, ma il linker dà errori: ------------------Configuration: only_ASM - Win32 Debug----------------- Performing Custom Build Step on .\hello.ASM Microsoft (R) Macro Assembler Version 6.11 Copyright (C) Microsoft Corp 1981-1993. All rights reserved. Assembling: .\hello.ASM Linking... LINK : error LNK2001: unresolved external symbol _mainCRTStartup Debug/only_ASM.exe : fatal error LNK1120: 1 unresolved externals Error executing link.exe. only_ASM.exe - 2 error(s), 0 warning(s) Errore di linker, manca un simbolo, quello usualmente settato per default che punta al codice di inizializzazione standard delle applicazione a console. Personalizziamo. Nella sezione linker rimuoviamo tutte le Lib e DLL non utilizzate, ci basta kernel32.lib: ricompiliamo, ma non basta ancora, non era quello il motivo, manca l’entry point. Alla sezione LINK, category "Output" aggiungiamo l’entry point specificato nel file asm, _start. Compila! E se poniamo un breakpoint nel sorgente, il debugging funziona: CONCLUSIONI E’ possibile utilizzare un sistema 32 bit, a finestre, ben collaudato, per scrivere programmi anche solo costituiti da Assembler PURO. Con qualche trucco ci siamo sbarazzati per sempre del PWB! |