Cod octet

Versiunea actuală a paginii nu a fost încă examinată de colaboratori experimentați și poate diferi semnificativ de versiunea revizuită la 8 august 2022; verificarea necesită 1 editare .

Bytecode ( bytecode ; engleză  bytecode , de asemenea, uneori p-code , p-code din codul portabil ) este o reprezentare intermediară standard în care un program de calculator poate fi tradus prin mijloace automate. În comparație cu codul sursă care poate fi citit de om , bytecode este o reprezentare compactă a unui program care a fost deja analizat și analizat . Codifică în mod explicit tipuri , domenii și alte constructe. Din punct de vedere tehnic, un bytecode este un cod de nivel scăzut, independent de mașină, generat de un traducător dintr-un cod sursă.

Multe limbaje de programare moderne , în special cele interpretate , folosesc bytecode pentru a facilita și accelera munca interpretului . Traducerea în bytecode este o metodă intermediară ca eficiență între interpretarea directă și compilarea în codul mașină.

În formă, bytecode este similar cu codul de mașină , dar este destinat să fie executat nu de un procesor real , ci de o mașină virtuală . Mașina virtuală este de obicei un interpret al limbajului de programare corespunzător (uneori completat de un compilator JIT sau AOT ). Specificațiile bytecode-ului și ale mașinilor virtuale care îl execută pot varia foarte mult de la o limbă la alta: bytecode-ul constă adesea în instrucțiuni pentru o mașină stivuită [ 1] , dar pot fi utilizate și mașinile de înregistrare [2] [3] . Cu toate acestea, majoritatea instrucțiunilor bytecode sunt de obicei echivalente cu una sau mai multe instrucțiuni în limbaj de asamblare .

Un bytecode este numit astfel deoarece fiecare opcode are în mod tradițional un octet . Fiecare instrucțiune este de obicei un cod operațional de un octet (de la 0 la 255) care poate fi urmat de diferiți parametri, cum ar fi un număr de registru sau o adresă de memorie .

Performanță

Un program bytecode este de obicei executat de un interpret de bytecode . Avantajul bytecode este o mai mare eficiență și portabilitate , adică același bytecode poate fi executat pe diferite platforme și arhitecturi pentru care este implementat interpretul. Limbile interpretate direct oferă același avantaj, însă, deoarece bytecode este de obicei mai puțin abstract și mai compact decât codul sursă, interpretarea bytecode este de obicei mai eficientă decât interpretarea pură a codului sursă sau interpretarea AST . În plus, un interpret de cod de octet este adesea mai simplu decât un interpret de cod sursă și este mai ușor de transferat (port) pe o altă platformă hardware.

Implementările de înaltă performanță ale mașinilor virtuale pot folosi o combinație între un interpret și un compilator JIT , care traduce fragmentele de bytecode utilizate frecvent în codul mașinii în timpul execuției programului, aplicând în același timp diverse optimizări. În loc de compilare JIT, poate fi folosit un compilator AOT , care traduce bytecode în cod mașină în prealabil, înainte de execuție.

În același timp, este posibil să se creeze procesoare pentru care bytecode dat este direct cod de mașină (astfel de procesoare experimentale au fost create, de exemplu, pentru limbajele Java și Forth ).

Istorie

Printre primele sisteme care au folosit bytecode au fost O-code pentru BCPL (anii 1960), Smalltalk (1976) [4] , SIL (System Implementation Language) pentru Snobol-4 (1967), p-code ( p-code , anii 1970, cu contribuții de la Niklaus Wirth ) pentru compilatoare portabile ale limbajului de programare Pascal [5] [6] [7] .

Variante ale codului p au fost utilizate pe scară largă în diferite implementări ale limbajului Pascal, cum ar fi sistemul p UCSD ( UCSD Pascal ). [opt]

Aplicație

Limbile interpretate care folosesc bytecode includ Perl , PHP (cum ar fi Zend Engine ), Ruby (din versiunea 1.9), Python , Erlang și multe altele.

Platforme răspândite folosind bytecode [9] :

Compilatorul Clipper creează un fișier executabil care include bytecode tradus din codul sursă al programului și o mașină virtuală care execută bytecode.

Programele Java sunt de obicei compilate în fișiere de clasă, care conține bytecode Java . Aceste fișiere generice sunt transferate pe diferite mașini țintă.

Implementările timpurii ale Visual Basic (înainte de versiunea 6) au folosit cod p Microsoft de nivel înalt [9]

În DBMS au fost utilizate coduri p de nivel înalt și coduri de octeți , unele implementări ale BASIC și Pascal .

În standardul Open Firmware de la Sun Microsystems , bytecode reprezintă operatorii Forth .

Exemple

Python

Codul:

>>> print ( "Bună, lume!" ) Bună , lume !

Cod octet:

>>> import dis #import modulul "dis" - Dezasamblarea codului de octeți Python în mnemonici. >>> dis . dis ( 'print("Bună, lume!")' ) 1 0 LOAD_NAME 0 ( print ) 2 LOAD_CONST 0 ( 'Bună, lume!' ) 4 CALL_FUNCTION 1 6 RETURN_VALUE

Java

Codul:

exterior : for ( int i = 2 ; i < 1000 ; i ++ ) { for ( int j = 2 ; j < i ; j ++ ) { if ( i % j == 0 ) continua exterior ; } Sistem . afară . println ( i ); }

Cod octet:

0: iconst_2 1: istore_1 2: iload_1 3: sipush 1000 6: if_icmpge 44 9: iconst_2 10: istore_2 11: iload_2 12: iload_1 13: if_icmpge 31 16: iload_1 22: 16: iload_1 22 : 19 : iload_1 2 2 3 25: iinc 2 , 1 28: goto 11 31: getstatic #84 ; //Câmp java/lang/System.out:Ljava/io/PrintStream; 34: iload_1 35: invokevirtual #85 ; //Metoda java/io/PrintStream.println:(I)V 38: iinc 1 , 1 41: goto 2 44: return

Critica

În mod tradițional, bytecode este proiectat în stilul mașinilor virtuale stivuite, ceea ce simplifică generarea din AST , permite o codificare bytecode mai simplă și mai compactă, simplifică interpretul și reduce cantitatea de cod de mașină necesară pentru a executa o singură instrucțiune bytecode. Pe de altă parte, astfel de variante ale codului de octet pentru un program dat conțin mai multe instrucțiuni decât codurile de octet ale mașinilor virtuale de registru, din cauza cărora interpretul trebuie să facă mai multe salturi indirecte, pentru care predicția de ramificație nu funcționează bine [3] . Codul de octet pentru mașinile virtuale de înregistrare are o dimensiune puțin mai mare a codurilor de mașină, dar numărul de instrucțiuni în comparație cu codul de octet al stivei este de aproximativ două ori mai mic, iar interpretul este cu zeci de procente mai rapid [3] . De asemenea, bytecode-ul mașinilor stive este mai greu de optimizat (expresiile devin implicite, instrucțiunile aferente nu sunt grupate, expresiile sunt distribuite pe mai multe blocuri de bază ) [12] și necesită verificarea corectitudinii utilizării stivei [13] .

Erorile de verificare a codului octet al mașinii stive au condus la multe vulnerabilități extrem de periculoase, în special zeci în mașina virtuală AVM2 utilizată în Adobe Flash pentru a executa scripturi ActionScript [14] [15] [16] și câteva din primele sisteme populare de rulare Java (JVM) [ 17] [18]

La sfârșitul anilor 2000 și începutul anilor 2010, autorii compilatorilor V8 (pentru JavaScript, implementat adesea prin bytecode) [19] și Dart [20] au pus la îndoială necesitatea unor bytecoduri intermediare pentru mașini virtuale rapide și eficiente. Aceste proiecte au implementat compilarea JIT directă (compilare la timpul rulării) de la codurile sursă direct la codul mașinii. [21]

Note

  1. Terence Parr. Language Implementation Patterns - Pragmatic Bookshelf, decembrie 2009, ISBN 978-1-934356-45-6 „Partea 3: Construirea interpreților. Pattern 27 Stack-Based Bytecode Interpreter” Arhivat 26 iunie 2015 la Wayback Machine
  2. Terence Parr. Language Implementation Patterns - Pragmatic Bookshelf, decembrie 2009, ISBN 978-1-934356-45-6 „Partea 3: Construirea interpreților. Pattern 28 Register-Based Bytecode Interpreter" Arhivat 26 iunie 2015 la Wayback Machine
  3. 1 2 3 Yunhe Shi, David Gregg, Andrew Beatty, M. Anton Ertl. Virtual Machine Showdown: Stack Versus Registers  //  VEE '05: Proceedings of the 1st ACM/USENIX international Conference on Virtual execution environments. - Chicago, Illinois, SUA: ACM, 2005. - P. 153 - 163 . — ISBN 1-59593-047-7 . - doi : 10.1145/1064979.1065001 .
  4. Aducerea performanței și scalabilității în limbaje dinamice  (link indisponibil) // Mario Wolczko, Oracle 2012 slide 7
  5. Ruslan Bogatyrev. Chronicle of Pascal Languages ​​Arhivat la 30 mai 2015 la Wayback Machine , PC World, nr. 04/2001
  6. Compilers: Principles, Techniques, and Tools Arhivat 4 martie 2016 la Wayback Machine  - Williams, ISBN 9785845901897 , pagina 517 „12.2 Pascal Compilers”
  7. THE UCSD P-SYSTEM MUSEUM Arhivat 17 februarie 2015 la Wayback Machine , 2004
  8. 1 2 Understanding .NET: A Tutorial and Analysis Arhivat 6 martie 2016 la Wayback Machine , David Chappell, David Wayne Chappell, 2002, ISBN 9780201741629 pagina 92
  9. 1 2 C# Versus Java Arhivat 6 aprilie 2016 la Wayback Machine / Dr. Dobb's Journal februarie 2001
  10. http://www.javaworld.com/article/2077233/core-java/bytecode-basics.html Arhivat 19 mai 2015 la Wayback Machine 1996
  11. Alan Jock. Compilatoare, interpreți și bytecode . Computerworld Russia, nr. 06, 2001. Consultat la 18 mai 2015. Arhivat la 28 octombrie 2010.
  12. Ando Saabas, Tarmo Uustalu. Sisteme de tip pentru optimizarea codului bazat pe stivă  // Note electronice în informatică teoretică. - 2007. - Emisiune. 190,1 . — p. 103-119. . - doi : 10.1016/j.entcs.2007.02.063 . Arhivat din original pe 26 mai 2016. : „Stiva virtuală sau mașinile virtuale cu registru virtual pot fi executate mai eficient folosind un interpret. Mașinile de registru virtuale pot fi o alternativă atractivă la arhitecturile de stivă, deoarece permit reducerea substanțială a numărului de instrucțiuni VM executate.”
  13. Gerwin Klein și Martin Wildmoser, Verified Bytecode Subroutines Arhivat la 10 august 2017 la Wayback Machine // Journal of Automated Reasoning 30.3-4 (2003): 363-398. „Verificarea bytecode este o verificare statică pentru siguranța bytecode. Scopul său este de a se asigura că JVM-ul execută doar cod sigur: fără supra- sau sub depășire a stivei de operanzi, fără instrucțiuni prost formate, fără erori de tip"
  14. Mark Dowd (X-Force Researcher IBM Internet Security Systems), Leveraging the ActionScript Virtual Machine  (link indisponibil) , IBM 2008 „dacă ar exista o modalitate de a executa instrucțiuni AS3 care nu ar fi fost niciodată verificate, ar fi destul de periculos. Instrucțiunile neverificate ar putea manipula stiva nativă de rulare... Atacul funcționează prin manipularea unei structuri de date utilizate de verificatorul AVM2, astfel încât să nu verifice corect instrucțiunile ActionScript pentru o anumită metodă"
  15. Haifei Li, Understanding and Exploiting Flash ActionScript Vulnerabilities Arhivat 26 noiembrie 2013. , 2011 „Bytecode -> Proces de verificare... Vulnerabilitățile ActionScript se datorează diferitelor erori de calculare a fluxului de program în procesul de verificare/generare (fluxul de verificare și fluxul de execuție nu sunt identice)”
  16. Haifei Li (Microsoft), Inside AVM Arhivat 21 noiembrie 2014 la Wayback Machine // REcon 2012, Montreal „Majoritatea vulnerabilităților Flash sunt legate de ActionScript... Eșecurile de verificare cauzează vulnerabilități de confuzie de tip JIT extrem de periculoase. • foarte periculos înseamnă exploatare perfectă: ocolirea ASLR+DEP, cu fiabilitate %100, fără heapSpray, fără JITSpray. • Erorile de confuzie de tip JIT se datorează unor erori în verificarea AVM!”
  17. Ultima etapă a grupului de cercetare Delirium, vulnerabilitățile de securitate Java și Java Virtual Machine și tehnicile lor de exploatare Arhivat 12 martie 2016 la Wayback Machine , BlackHat 2002: „Defectul a provenit din faptul că Bytecode Verifier nu a efectuat corect analiza fluxului de bytecode. "
  18. Verificarea bytecode într-o mașină virtuală Arhivat 30 aprilie 2013. // International Journal of Advanced Research in Computer Science and Software Engineering Vol.3 Numărul 3 martie 2013, ISSN 2277-128X: „Verificarea codului de octeți Java a fost studiată pe larg din perspectiva corectitudinii și au fost găsite și eliminate mai multe vulnerabilități în acest sens. proces"
  19. Generarea dinamică a codului mașinii . Google. Consultat la 18 mai 2015. Arhivat din original la 17 septembrie 2013.
  20. Loitsch, Florian De ce nu este o VM Bytecode? . Google. Preluat la 18 mai 2015. Arhivat din original la 12 mai 2013.
  21. Dr. Axel Rauschmayer. Mitul JavaScript: JavaScript are nevoie de un  bytecode standard . Preluat la 18 mai 2015. Arhivat din original la 19 mai 2015.