Bytekod

Den aktuella versionen av sidan har ännu inte granskats av erfarna bidragsgivare och kan skilja sig väsentligt från versionen som granskades den 8 augusti 2022; verifiering kräver 1 redigering .

Bytecode ( bytecode ; engelsk  bytecode , även ibland p-code , p-code från portable code ) är en standardmellanrepresentation [ till vilken ett datorprogram kan översättas med automatiska medel. Jämfört med mänskligt läsbar källkod är bytecode en kompakt representation av ett program som redan har analyserats och analyserats . Den kodar uttryckligen typer , omfattningar och andra konstruktioner. Ur teknisk synvinkel är en bytekod en maskinoberoende kod på låg nivå som genereras av en översättare från en källkod.

Många moderna programmeringsspråk , särskilt tolkade , använder bytekod för att underlätta och påskynda tolkens arbete . Översättning till bytekod är en metod som ligger mellan i effektivitet mellan direkt tolkning och kompilering till maskinkod.

I form liknar bytecode maskinkod , men är avsedd att exekveras inte av en riktig processor , utan av en virtuell maskin . Den virtuella maskinen är vanligtvis en tolk av motsvarande programmeringsspråk (ibland kompletterat med en JIT- eller AOT-kompilator ). Specifikationerna för bytekoden och de virtuella maskiner som kör den kan variera mycket från språk till språk: bytecode består ofta av instruktioner för en staplad maskin [1] , men registermaskiner [ 2] [3] kan också användas . De flesta bytekodinstruktioner är dock vanligtvis likvärdiga med en eller flera instruktioner för assemblerspråk .

En bytekod heter så eftersom varje opcode traditionellt är en byte lång . Varje instruktion är vanligtvis en en-byte opkod (0 till 255) som kan följas av olika parametrar, såsom ett registernummer eller en minnesadress .

Prestanda

Ett bytekodsprogram exekveras vanligtvis av en bytekodtolkare . Fördelen med bytecode är större effektivitet och portabilitet , det vill säga att samma bytekod kan köras på olika plattformar och arkitekturer för vilka tolken är implementerad. Direkttolkade språk ger dock samma fördel, eftersom bytekod vanligtvis är mindre abstrakt och mer kompakt än källkod, är bytekodtolkning vanligtvis mer effektiv än ren källkodstolkning eller AST -tolkning . Dessutom är en bytekodtolkare ofta enklare än en källkodstolk och är lättare att överföra (porta) till en annan hårdvaruplattform.

Högpresterande implementeringar av virtuella maskiner kan använda en kombination av en tolk och en JIT-kompilator , som översätter ofta använda bytekodfragment till maskinkod under programkörning, samtidigt som olika optimeringar tillämpas. Istället för JIT-kompilering kan en AOT-kompilator användas , som översätter bytekoden till maskinkod i förväg, innan exekvering.

Samtidigt är det möjligt att skapa processorer för vilka den givna bytekoden är direkt maskinkod (sådana experimentella processorer skapades till exempel för språken Java och Forth ).

Historik

Bland de första systemen som använde bytekod var O-kod för BCPL (1960-talet), Smalltalk (1976) [4] , SIL (System Implementation Language) för Snobol-4 (1967), p-kod ( p-kod , 1970-talet, med bidrag från Niklaus Wirth ) för bärbara kompilatorer av programmeringsspråket Pascal [5] [6] [7] .

Varianter av p-koden har använts i stor utsträckning i olika implementeringar av Pascal-språket, såsom UCSD p-System ( UCSD Pascal ). [åtta]

Applikation

Tolkade språk som använder bytekod inkluderar Perl , PHP (som Zend Engine ), Ruby (sedan version 1.9), Python , Erlang och många fler.

Utbredda plattformar som använder bytekod [9] :

Clipper - kompilatorn skapar en körbar fil som inkluderar bytekoden översatt från programmets källkod och en virtuell maskin som exekverar bytekoden.

Java-program kompileras vanligtvis till klassfiler, som innehåller Java-bytekod . Dessa generiska filer överförs till olika målmaskiner.

Tidiga implementeringar av Visual Basic (före version 6) använde högnivå Microsoft p-kod [9]

P-koder och bytekoder på hög nivå användes i DBMS , vissa implementeringar av BASIC och Pascal .

I Open Firmware- standarden från Sun Microsystems representerar bytekoden Forth- operatörer .

Exempel

Python

Koden:

>>> print ( "Hej, värld!" ) Hej , värld !

Bytekod:

>>> importera dis #importera "dis"-modulen - Disassembler av Python-bytekod till mnemonics. >>> dis . dis ( 'print("Hello, World!")' ) 1 0 LOAD_NAME 0 ( print ) 2 LOAD_CONST 0 ( 'Hej världen!' ) 4 CALL_FUNCTION 1 6 RETURN_VALUE

Java

Koden:

yttre : för ( int i = 2 ; i < 1000 ; i ++ ) { for ( int j = 2 ; j < i ; j ++ ) { if ( i % j == 0 ) fortsätt yttre ; } System . ut . println ( i ); }

Bytekod:

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_21 : iload_1_21 9 : iload_1_21 : iload_1 21 9 : iload_1_21 9 25: iinc 2 , 1 28: goto 11 31: getstatic #84 ; //Fält java/lang/System.out:Ljava/io/PrintStream; 34: iload_1 35: invokevirtual #85 ; //Method java/io/PrintStream.println:(I)V 38: iinc 1 , 1 41: goto 2 44: return

Kritik

Traditionellt är bytekod utformad i stil med staplade virtuella maskiner, vilket förenklar generering från AST , möjliggör enklare och mer kompakt bytekodning, förenklar tolken och minskar mängden maskinkod som krävs för att exekvera en enkel bytekodinstruktion. Å andra sidan innehåller sådana varianter av bytekoden för ett givet program fler instruktioner än bytekoderna för virtuella registermaskiner, på grund av vilka tolken måste göra fler indirekta hopp, för vilka grenprediktion inte fungerar bra [3] . Bytekoden för virtuella registermaskiner har en något större storlek på maskinkoder, men antalet instruktioner jämfört med stackbytekoden är ungefär två gånger mindre och tolken är tiotals procent snabbare [3] . Dessutom är bytekoden för stackmaskiner svårare att optimera (uttryck blir implicita, relaterade instruktioner grupperas inte, uttryck fördelas över flera grundläggande block ) [12] och kräver verifiering av korrektheten av att använda stacken [13] .

Verifieringsfel för staplingsmaskinbytekod ledde till många extremt farliga sårbarheter, särskilt dussintals i den virtuella AVM2-maskinen som användes i Adobe Flash för att köra ActionScript-skript [14] [15] [16] och flera i de tidiga populära Java-runtime-systemen (JVM ) [ 17] [18]

I slutet av 2000-talet och början av 2010-talet ifrågasatte författarna till V8 (för JavaScript, ofta implementerad via bytecode) [19] och Dart [20] kompilatorerna behovet av mellanliggande bytekoder för snabba och effektiva virtuella maskiner. Dessa projekt implementerade direkt JIT-kompilering (kompilering vid körning) från källkoder direkt till maskinkod. [21]

Anteckningar

  1. Terence Parr. Language Implementation Patterns - Pragmatic Bookshelf, december 2009, ISBN 978-1-934356-45-6 "Del 3: Byggnadstolkar. Pattern 27 Stack-Based Bytecode Interpreter” Arkiverad 26 juni 2015 på Wayback Machine
  2. Terence Parr. Language Implementation Patterns - Pragmatic Bookshelf, december 2009, ISBN 978-1-934356-45-6 "Del 3: Byggnadstolkar. Pattern 28 Register-Based Bytecode Interpreter" Arkiverad 26 juni 2015 på Wayback Machine
  3. 1 2 3 Yunhe Shi, David Gregg, Andrew Beatty, M. Anton Ertl. Virtual Machine Showdown: Stack Versus Register  //  VEE '05: Proceedings of the 1st ACM/USENIX internationella konferens om virtuella exekveringsmiljöer. - Chicago, Illinois, USA: ACM, 2005. - S. 153 - 163 . — ISBN 1-59593-047-7 . - doi : 10.1145/1064979.1065001 .
  4. Ge prestanda och skalbarhet till dynamiska språk  (inte tillgänglig länk) // Mario Wolczko, Oracle 2012 bild 7
  5. Ruslan Bogatyrev. Chronicle of Pascal Languages ​​arkiverad 30 maj 2015 på Wayback Machine , PC World, nr 04/2001
  6. Compilers: Principles, Techniques and Tools Arkiverad 4 mars 2016 på Wayback Machine  - Williams, ISBN 9785845901897 , sida 517 "12.2 Pascal Compilers"
  7. UCSD P-SYSTEM MUSEUM Arkiverad 17 februari 2015 på Wayback Machine , 2004
  8. 1 2 Understanding .NET: A Tutorial and Analysis Archived March 6, 2016 at the Wayback Machine , David Chappell, David Wayne Chappell, 2002, ISBN 9780201741629 sida 92
  9. 1 2 C# Versus Java Arkiverad 6 april 2016 på Wayback Machine / Dr. Dobb's Journal februari 2001
  10. http://www.javaworld.com/article/2077233/core-java/bytecode-basics.html Arkiverad 19 maj 2015 på Wayback Machine 1996
  11. Alan Jock. Kompilatorer, tolkar och bytekod . Computerworld Ryssland, nr 06, 2001. Hämtad 18 maj 2015. Arkiverad 28 oktober 2010.
  12. Ando Saabas, Tarmo Uustalu. Typsystem för optimering av stackbaserad kod  // Electronic Notes in Theoretical Computer Science. - 2007. - Utgåva. 190,1 . — s. 103-119. . - doi : 10.1016/j.entcs.2007.02.063 . Arkiverad från originalet den 26 maj 2016. : "VM:s virtuella stack eller virtuella register kan exekveras mer effektivt med hjälp av en tolk. Virtuella registermaskiner kan vara ett attraktivt alternativ till stackarkitekturer eftersom de tillåter att antalet exekverade VM-instruktioner reduceras avsevärt.”
  13. Gerwin Klein och Martin Wildmoser, Verified Bytecode Subroutines Arkiverad 10 augusti 2017 på Wayback Machine // Journal of Automated Reasoning 30.3-4 (2003): 363-398. "Bytekodsverifiering är en statisk kontroll av bytekodsäkerhet. Dess syfte är att säkerställa att JVM endast exekverar säker kod: inga operandstack över- eller underflöden, inga felaktigt utformade instruktioner, inga typfel"
  14. Mark Dowd (X-Force Researcher IBM Internet Security Systems), Utnyttja ActionScript Virtual Machine  (otillgänglig länk) , IBM 2008 "om det fanns ett sätt att exekvera AS3-instruktioner som aldrig hade verifierats, skulle det vara ganska farligt. Overifierade instruktioner skulle kunna manipulera den ursprungliga runtime-stacken ... Attacken fungerar genom att manipulera en datastruktur som används av AVM2-verifieraren så att den inte korrekt verifierar ActionScript-instruktionerna för en given metod"
  15. Haifei Li, Understanding and Exploiting Flash ActionScript Vulnerabilities Arkiverad 26 november 2013. , 2011 "Bytecode -> Verifieringsprocess ... ActionScript-sårbarheter beror på olika programflödesberäkningsfel i verifierings-/genereringsprocessen (verifieringsflödet och exekveringsflödet är inte samma sak)"
  16. Haifei Li (Microsoft), Inside AVM Arkiverad 21 november 2014 på Wayback Machine // REcon 2012, Montreal "De flesta Flash-sårbarheter är ActionScript-relaterade ... Fel vid verifiering orsakar mycket farliga JIT-förvirringssårbarheter. • mycket farlig betyder perfekt utnyttjande: förbi ASLR+DEP, med %100 tillförlitlighet, ingen heapSpray, ingen JITSpray. • Förvirringsbuggar av JIT-typ beror på fel i verifieringen av AVM!”
  17. Det sista steget i deliriumforskningsgruppen, säkerhetssårbarheter i Java och Java Virtual Machine och deras utnyttjandetekniker Arkiverad 12 mars 2016 på Wayback Machine , BlackHat 2002: "Festen härrörde från det faktum att Bytecode Verifier inte utförde bytekodflödesanalysen korrekt "
  18. Verifiering av bytekod i en virtuell maskin Arkiverad 30 april 2013. // International Journal of Advanced Research in Computer Science and Software Engineering Vol. 3 Utgåva 3 mars 2013, ISSN 2277-128X: “Javabyte-kodverifiering har studerats omfattande ur ett korrekthetsperspektiv, och flera sårbarheter har hittats och eliminerats i detta bearbeta"
  19. Dynamisk maskinkodgenerering . Google. Hämtad 18 maj 2015. Arkiverad från originalet 17 september 2013.
  20. Loitsch, Florian Varför inte en bytekod VM? . Google. Hämtad 18 maj 2015. Arkiverad från originalet 12 maj 2013.
  21. Dr. Axel Rauschmayer. JavaScript-myt: JavaScript behöver en  standardbytekod . Hämtad 18 maj 2015. Arkiverad från originalet 19 maj 2015.