570 likes | 721 Views
Introduzione al CLR/MSIL. Alfredo Paolillo e Marco Servetto. Vocabolario. IL: Intermediate Language, Standard ECMA del 1997 MSIL: Microsoft IL, Implementazione Microsoft di IL. Introduzione. Perché .NET Ambiente di esecuzione Common Language Runtime. Perché .NET.
E N D
Introduzione alCLR/MSIL Alfredo Paolillo e Marco Servetto
Vocabolario • IL: • Intermediate Language, Standard ECMA del 1997 • MSIL: • Microsoft IL, Implementazione Microsoft di IL
Introduzione • Perché .NET • Ambiente di esecuzione • Common Language Runtime
Perché .NET • Difficile effettuare sviluppo omogeneo • Molto tempo viene dedicato a far comunicare i vari “strati” • Serve un salto qualitativo per semplificare lo scenario
Codici • Evoluzione • Codice nativo • Codice interpretato • Codice MSIL
Codice nativo Sorgenti Compilatore Codicenativo(.EXE) Output
Codice interpretato Sorgenti Interprete Output
Codice MSIL Sorgenti Compilatore.NET CodiceMSIL (Assembly) .EXE/.DLL Codicenativo Output Compilatore JIT
Codice MSIL Sorgenti Compilatore.NET CodiceMSIL (Assembly) .EXE/.DLL Codice + metadati Codicenativo Output Compilatore JIT
Codice MSIL Ambiente di esecuzione .NET Runtime Sorgenti Compilatore.NET CodiceMSIL (Assembly) .EXE/.DLL Codicenativo Output Compilatore JIT
Motori JIT • Inizialmente previsti 4 motori:
JIT – Just in Time Compiler • In teoria, come con Java, è possibile compilare MSIL ed eseguirlo (interpretato) in qualsiasi ambiente che supporti l’esecuzione • La compilazione di un’applicazione da un tipo di codice assembly quale MSIL verso un codice eseguibile sulla macchina nativa dovrebbe appesantire le prestazioni dell’applicazione • È quello che succede?
JIT – Just in Time Compiler • Il codice non viene caricato tutto in memoria • il compilatore JIT compila solo il codice necessario, quindi memorizza nella cache il codice nativo compilato per riutilizzarlo • L’overhead è una lieve differenza che, nella maggior parte dei casi, non verrà rilevata
JIT – Just in Time Compiler • Quando viene caricata una classe, il caricatore aggiunge uno stub a ogni metodo della classe • La prima volta che viene chiamato il metodo, il codice stub cede il controllo al compilatore JIT, che compila MSIL nel codice nativo. • Lo stub viene quindi modificato per puntare al codice nativo appena creato, affinché le chiamate successive passino direttamente al codice nativo
Indipendenza dalla piattaforma • .NET è un’implementazione di CLI • Common Language Infrastructure • CLI è uno standard ECMA • ECMA-334, ECMA-335 • Esistono già altre implementazioni di CLI: • SSCLI (Microsoft, per Windows, FreeBSD e Macintosh) • Mono (per Linux) • DotGNU • Intel OCL (Open CLI Library) • …
Codice IL Tutto questo assomiglia a qualcosa di già visto? Forse Java? • Ci sono delle differenze • Un compilatore Java crea bytecode, che in fase di esecuzione viene interpretato tramite JVM • .NET crea un codice nativo
Codice IL • Un vantaggio rilevante offerto da .NET Framework rispetto a Java e JVM è la scelta del linguaggio di programmazione • JVM solo Java • .NET Multilinguaggio (VB.net, C#, J# etc…) • Vediamo un esempio di IL
Modulo(file PE) Codice IL Metadati Manifest Assembly Assembly
Metadati • Concetto chiave in .NET • Informazioni sui tipi di un assembly • Generati automaticamente dai compilatori • Estendibili da terze parti • Formato binario rappresentabile con XML: • XML Schema (XSD) • Serializzazione e deserializzazione oggetti a runtime in XML
Metadati • Descrizione di un assembly • Identità: nome, versione, cultura [, pubblic key] • Tipi esportati • Assembly da cui dipende • Descrizione dei tipi • Nome, visibilità, classe base, interfacce implementate • Attributi custom • Definiti dall’utente • Definiti dal compilatore
Codice IL • Proviamo adesso a scrivere e compilare dei semplici programmi in C# e proviamo ad analizzarli
Codice IL • Esempio 1 namespace testUno { public class esempioUno { public esempioUno() { } static void Main(string[] args) { int primaVariabile = 0x1234; int secondaVariabile = 0xabcdef; } } }
Codice IL • Il file eseguibile è costituito da due parti: • la prima è il codice MSIL, utilizzato per generare il codice nativo • la seconda è rappresentata dai metadati • Con un tool in dotazione con l’SDK possiamo Diassemblare il file ottenuto dalla compilazione • Otterremo il seguente output • Tralasceremo comunque alcuni dettagli come il codice del costruttore di classe
Codice IL .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 13 (0xd) .maxstack 1 .locals init (int32 V_0, int32 V_1) IL_0000: ldc.i4 0x1234 IL_0005: stloc.0 IL_0006: ldc.i4 0xabcdef IL_000b: stloc.1 IL_000c: ret } // end of method esempioUno::Main
Codice IL – istruzioni principali • .entrypoint • Specifies that this method is the entry point to the application (only one such method is allowed). • .maxstack • int32 specifies the maximum number of elements on the evaluation stack during the execution of the method • .locals [init] • Defines a set of local variables for this method. • ldc.i4: • Description Push num of type int32 onto the stack as int32. • stloc.0: • Description: Pop value from stack into local variable 0. • ret: • Description: return from method, possibly returning a value
Codice IL – Metainformazioni ScopeName : testUno.exe MVID : {F01C8E38-E942-43D9-9D71-95D37789D357} =========================================================== Global functions ------------------------------------------------------- Global fields ------------------------------------------------------- Global MemberRefs ------------------------------------------------------- TypeDef #1 ------------------------------------------------------- TypDefName: testUno.esempioUno (02000002) Flags : [Public] [AutoLayout] [Class] [AnsiClass] (00100001) Extends : 01000001 [TypeRef] System.Object Method #1 ------------------------------------------------------- MethodName: .ctor (06000001) Flags : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor] (00001886) RVA : 0x00002050 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments. Method #2 [ENTRYPOINT] ------------------------------------------------------- MethodName: Main (06000002) Flags : [Private] [Static] [HideBySig] [ReuseSlot] (00000091) RVA : 0x00002064 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] ReturnType: Void 1 Arguments Argument #1: SZArray String 1 Parameters (1) ParamToken : (08000001) Name : args flags: [none] (00000000)
Codice IL – Metainformazioni TypeRef #1 (01000001) ------------------------------------------------------- Token: 0x01000001 ResolutionScope: 0x23000001 TypeRefName: System.Object MemberRef #1 ------------------------------------------------------- Member: (0a000002) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments. TypeRef #2 (01000002) ------------------------------------------------------- Token: 0x01000002 ResolutionScope: 0x23000001 TypeRefName: System.Diagnostics.DebuggableAttribute MemberRef #1 ------------------------------------------------------- Member: (0a000001) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 2 Arguments Argument #1: Boolean Argument #2: Boolean
Codice IL – Metainformazioni Signature #1 (0x11000001) ------------------------------------------------------- CallCnvntn: [LOCALSIG] 2 Arguments Argument #1: I4 Argument #2: I4 Assembly ------------------------------------------------------- Token: 0x20000001 Name : testUno Public Key : Hash Algorithm : 0x00008004 Major Version: 0x00000000 Minor Version: 0x00000000 Build Number: 0x00000000 Revision Number: 0x00000000 Locale: <null> Flags : [SideBySideCompatible] (00000000) CustomAttribute #1 (0c000001) ------------------------------------------------------- CustomAttribute Type: 0a000001 CustomAttributeName: System.Diagnostics.DebuggableAttribute :: instance void .ctor(bool,bool) Length: 6 Value : 01 00 00 01 00 00 > < ctor args: ( <can not decode> )
Codice IL – Metainformazioni AssemblyRef #1 ------------------------------------------------------- Token: 0x23000001 Public Key or Token: b7 7a 5c 56 19 34 e0 89 Name: mscorlib Major Version: 0x00000001 Minor Version: 0x00000000 Build Number: 0x00001388 Revision Number: 0x00000000 Locale: <null> HashValue Blob: Flags: [none] (00000000)
Codice IL I metadati vengono organizzati in tabelle, in cui fondamentalmente viene descritto ciò che il codice definisce e a cui fa riferimento Prestiamo attenzione a questa parte di codice: CallCnvntn: [LOCALSIG] 2 Arguments Argument #1: I4 Argument #2: I4
Codice C# • Proviamo adesso a compilare il seguente codice FILE:esempioDueB namespace testDue { public class esempioDueB { static void Main(string[] args) { esempioDueA variabile = new esempioDueA(); variabile.printString(); } } }
Codice C# FILE: esempioDueA using System; namespace testDue { public class esempioDueA { public esempioDueA() { } public void printString() { string s = "Hello!!!!"; Console.Write(s); } } }
Codice IL • Disassembliamo: A differenza di prima dovremo analizzare due codici
Codice IL .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 13 (0xd) .maxstack 1 .locals init (class testDue.esempioDueA V_0) IL_0000: newobj instance void testDue.esempioDueA::.ctor() IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: callvirt instance void testDue.esempioDueA::printString() IL_000c: ret } // end of method esempioDueB::Main
Codice IL .method public hidebysig instance void printString() cil managed { // Code size 13 (0xd) .maxstack 1 .locals init (string V_0) IL_0000: ldstr "Hello!!!!" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: call void [mscorlib]System.Console::Write(string) IL_000c: ret } // end of method esempioDueA::printString
Codice IL Principali differenze rispetto al codice precedente: • Newobj: • Assembli format: newobjctor • Description: allocate an uninitialized object or value type and call ctor • Call: • Assembli format: call method • Description: Call method described by method • Callvirt: • Assembli format: callvirt method • Description: Call a method associated with obj
Codice IL • Andiamo nuovamente a riesaminare le meta-informazioni: Signature #2 (0x11000002) (EsempioDueB) ------------------------------------------------------- CallCnvntn: [LOCALSIG] 1 Arguments Argument #1: Class testDue.esempioDueA Signature #1 (0x11000001) (EsempioDueA) ------------------------------------------------------- CallCnvntn: [LOCALSIG] 1 Arguments Argument #1: String
Codice IL – Metainformazioni Method #2 (definizione del metodo invocato dalla call) ------------------------------------------------------- MethodName: printString (06000002) Flags : [Public] [HideBySig] [ReuseSlot] (00000086) RVA : 0x00002064 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments. .................. User Strings (costante) ------------------------------------------------------- 70000001 : ( 9) L"Hello!!!!”
Codice C# • Passaggio di parametri: namespace testTre { public class esempioTreA { static void Main(string[] args) { string s = ("HELLO!!!!!!!!!!!!!!!!!!!!!!!!!!"); esempioTreB variabile = new esempioTreB(); variabile.printString(s); } } }
Esempio C# Using system; public class esempioTreB { public esempioTreB() { } public void printString(string s) { Console.Write(s); } }
Codice IL .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 20 (0x14) .maxstack 2 .locals init (string V_0, class testTre.esempioTreB V_1) IL_0000: ldstr "HELLO!!!!!!!!!!!!!!!!!!!!!!!!!!" IL_0005: stloc.0 IL_0006: newobj instance void testTre.esempioTreB::.ctor() IL_000b: stloc.1 IL_000c: ldloc.1 IL_000d: ldloc.0 IL_000e: callvirt instance void testTre.esempioTreB::printString(string) IL_0013: ret } // end of method esempioTreA::Main
Codice IL .method public hidebysig instance void printString(string s) cil managed { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.1 IL_0001: call void [mscorlib]System.Console::Write(string) IL_0006: ret } // end of method esempioTreB::printString
Codice IL • ldarg.1 • Assembli format: ldarg.1 • Description: Load argument 1 onto stack Esistono anche delle varianti, ad esempio: • ldarg num • Assembli format: ldarg num • Description: Load argument numbered num onto stack.
Codice IL – Metainformazioni TypeDef #1 ------------------------------------------------------- TypDefName: testTre.esempioTreB (02000002) Flags : [Public] [AutoLayout] [Class] [AnsiClass] (00100001) Extends : 01000001 [TypeRef] System.Object Method #1 ------------------------------------------------------- MethodName: .ctor (06000001) Flags : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor] (00001886) RVA : 0x00002050 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments. Method #2 ------------------------------------------------------- MethodName: printString (06000002) Flags : [Public] [HideBySig] [ReuseSlot] (00000086) RVA : 0x00002064 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: String 1 Parameters (1) ParamToken : (08000001) Name : s flags: [none] (00000000)
Codice IL – Metainformazioni Signature #1 (0x11000001) ------------------------------------------------------- CallCnvntn: [LOCALSIG] 2 Arguments Argument #1: String Argument #2: Class testTre.esempioTreB Assenza di Signature #2 La classe su cui viene invocato il metodo printString non ha dichiarazioni locali
Garbage Collector • Gli oggetti vengono distrutti automaticamente quando non sono più referenziati • Algoritmo Mark-and-Compact
Garbage Collector - fase 1: Mark NextObjPtr Root set Oggetti “vivi” Oggetti non raggiungibili Spazio libero
Garbage Collector - fase 2: Compact Spazio recuperato NextObjPtr Root set Oggetti “vivi” Spazio libero
GC e distruzione deterministica • In alcuni casi serve un comportamento di finalizzazione deterministica: • Riferimenti a oggetti non gestiti • Utilizzo di risorse che devono essere rilasciate appena termina il loro utilizzo • Non si possono usare i finalizzatori, che non sono richiamabili direttamente • Implementare l’interfaccia IDisposable
Common Type System • Tutto è un oggetto • Due categorie di tipi: • Tipi reference (riferimento) • Allocati su heap gestito • Tipi value (valore) • Allocati su stack o in oggetti gestiti (reference) • Tutti i tipi value possono essere visti come tipi reference • Boxing