Was ist die tatsächliche Beziehung zwischen Baugruppe, Maschinencode, Bytecode und Opcode?
Ich habe die meisten SO-Fragen zum Assembly- und Maschinencode gelesen, zB dies , aber sie sind zu hoch und zeigen keine Beispiele von tatsächlichem Assemblercode, der in Maschinencode umgewandelt wird. Daher verstehe ich immer noch nicht, wie es auf einer tieferen Ebene funktioniert.
Die ideale Antwort auf diese Frage würde ein spezifisches Beispiel für einen Assembler-Code zeigen, wie zum Beispiel das unten stehende Snippet, und wie jeder Assembly-Befehl dem Maschinencode, Bytecode und / oder Opcode zugeordnet wird. Eine solche Antwort wäre für zukünftige Menschen, die Montage lernen, sehr hilfreich, denn bisher habe ich in den letzten Tagen des Grabens keine klare Zusammenfassung gefunden.
Hauptsache ich suche:
Hinweis: Ich habe keinen Informatik-Hintergrund, also bin ich in den letzten Jahren langsam tiefer gegangen und habe jetzt den Punkt erreicht, dass ich Assembler- und Maschinencode verstehen möchte.
Beziehung zwischen Baugruppe und Maschinencode
Mein derzeitiges Verständnis ist, dass ein "Assembler" (wie NASM) Assembler-Code nimmt und daraus Maschinencode erstellt.
Wenn Sie also eine Assembly wie dieses example.asm
:
(kompilieren Sie es mit nasm -f macho64 -o example.o example.asm
). Es gibt diese example.o
Objektdatei aus:
(das ist der gesamte Inhalt von example.o
). Wenn Sie dann das mit ld -o example example.o
"verknüpfen", erhalten Sie mehr Maschinencode:
Aber wie ist es von den Montageanweisungen zu diesen Zahlen gegangen? Gibt es eine Art Standardreferenz, die all diese Zahlen auflistet, und was sie bedeuten, für welche Architektur Sie auch immer sind (ich benutze x86-64 über NASM unter OSX) und wie jeder Zahlensatz zu jeder Assembleranweisung gehört ?
Ich verstehe, dass der Maschinencode für jede Maschine anders ist, und es gibt dutzende, wenn nicht Hunderte von verschiedenen Arten von Maschinen. Daher suche ich derzeit nicht danach, wie die Assemblierung in alle transformiert wird (das wäre kompliziert). Ich bin nur an einem Beispiel interessiert, das veranschaulicht, wie die Transformation funktioniert, und jede Architektur kann als Beispiel dienen. Und von diesem Punkt an könnte ich nach der spezifischen Architektur suchen, an der ich interessiert bin, und die Zuordnung finden.
Beziehung zwischen Assembly und Bytecode (oder heißt es "Opcode"?)
Nach meiner bisherigen Lektüre wird die Baugruppe in Maschinencode umgewandelt, wie oben gezeigt.
Aber jetzt bin ich verwirrt. Ich sehe Leute über Bytecode sprechen, wie zum Beispiel in dieser SO-Antwort , die Zeug wie folgt zeigt:
%Vor%Die Assembly für diese Funktion würde folgendermaßen aussehen:
%Vor%
Also werde ich verwirrt. Wenn ich etwas grabe, kann ich nicht sagen, ob jede dieser 2-stelligen hexadezimalen Zahlen wie 13 82 6a
jeweils einzeln "Opcodes" genannt werden, und der ganze Satz davon wird "Bytecode" als Sammelbegriff genannt. Darüber hinaus kann ich keine Tabelle finden, die alle diese 2-stelligen Hex-Zahlen auflistet, und ihre Beziehung zu Maschinencode oder Assembly.
Zusammenfassend freue ich mich sehr auf ein Beispiel, das zeigt, wie Assembleranweisungen dem Maschinencode zugeordnet werden, und es ist eine Beziehung zu Bytecode und / oder Opcode. (Ich suche nicht, wie ein Compiler das macht, wie das allgemeine Mapping funktioniert). Ich denke, das würde es nicht nur für mich selbst erklären, sondern für viele Menschen auf der Straße, die mehr über den Bare Metal erfahren wollen.
Ein weiterer Grund, warum dies wertvoll wäre, ist, dass man verstehen kann, wie der LLVM-Compiler Maschinencode erzeugt. Haben sie eine Art "vollständige Liste" von 2-stelligen Opcodes oder 4-stelligen Maschinencode-Sequenzen und wissen genau, wie diese auf eine architekturspezifische Baugruppe abgebildet werden? Woher haben sie diese Informationen? Eine Antwort auf diese allgemeine Frage würde deutlich machen, wie LLVM seine Code-Generierung implementiert hat.
Aktualisieren
Aktualisierung von @ HansPassants Kommentar. Es interessiert mich eigentlich nicht, was die tatsächlichen Unterscheidungen zwischen den Wörtern sind, tut mir leid, wenn das nicht klar war.Ich möchte das nur wissen: Wie stellt Assembly den Code der Maschine dar (und wo sind die Orte, an denen man nach den Referenzen suchen muss, die diese Informationen im Web enthalten), und werden irgendwo in diesem Prozess Opcodes oder Bytecode verwendet? Und wenn ja wie?
Ja, jede Architektur hat eine Befehlssatzreferenz, die angibt, wie Befehle codiert werden. Für x86 ist es die Entwicklerhandbuch für Intel® 64 und IA-32 Architekturen Band 2 (2A, 2B & amp; 2C): Befehlssatz-Referenz, AZ
Die meisten Assembler, einschließlich nasm
, können eine Listendatei für Sie erstellen. Wenn Sie Ihren Beispielcode an nasm -l
übergeben, erhalten Sie:
Sie können den generierten Maschinencode in der dritten Spalte sehen (die erste ist die Zeilennummer, die zweite Adresse).
Beachten Sie, dass die Ausgabe des Assemblers eine Objektdatei ist und die Ausgabe des Linkers eine ausführbare Datei ist. Beide haben eine komplexe Struktur und enthalten mehr als nur den Maschinencode. Aus diesem Grund unterscheidet sich Ihr hexdump von der obigen Auflistung.
Opcode wird im Allgemeinen als Teil des Maschinencode-Befehls betrachtet, der den auszuführenden Vorgang angibt. Zum Beispiel haben Sie im obigen Code B804000002 mov rax, 0x2000004
. Dort ist B8
der Opcode, 04000002
ist der unmittelbare Operand.
Bytecode wird normalerweise nicht im Assembly-Kontext verwendet, sondern könnte als Maschinencode für eine virtuelle Maschine betrachtet werden.
Für eine Komplettlösung ist x86 eine sehr komplizierte Architektur. Aber Ihr Beispielcode hat eine einfache Anweisung, die syscall
. Sehen wir uns an, wie wir das in Maschinencode umwandeln können. Öffnen Sie das oben genannte Referenz-PDF und gehen Sie in Kapitel 4 zum Abschnitt über syscall
. Sie sehen es sofort als Opcode 0F 05
aufgelistet. Da es keine Operanden benötigt, sind wir fertig, diese 2 Bytes sind der Maschinencode. Wie kehren wir es zurück? Gehe zu Appendix A: Opcode map
. Abschnitt A.1
sagt uns: For 2-byte opcodes beginning with 0FH (Table A-3), skip any instruction prefixes, the 0FH byte (0FH may be preceded by 66H, F2H, or F3H) and use the upper and lower 4-bit values of the next opcode byte to index table rows and columns.
. Okay, wir überspringen die 0F
und teilen die 05
in 0
und 5
und suchen das in der Tabelle A-3
in Zeile # 0, Spalte # 5 nach. Wir finden es eine syscall
Anweisung.
Gibt es eine Art Standardreferenz, die all diese Zahlen auflistet, und was sie bedeuten, für welche Architektur Sie sich auch interessieren und wie jede Zahlengruppe zu jeder Assembleranweisung gehört?
Ja, obwohl sie sehr komplex sein können. Außerdem sind sie aufgrund der Verbreitung von Assemblern und Compilern auch schwer zu finden, weil sie von niemandem verwendet werden.
Beziehung zwischen Assembly und Bytecode
13
den Prozessor an, eine Zeichenfolge auf den Stapel zu schieben. 13
. PushString
der Maschinenanweisung 13
zugeordnet. Ich sollte beachten, dass die Bytecode-Anweisungen, die in diesem Post und in meinem anderen Beitrag, den Sie verlinkt haben, verwendet werden, vereinfachte Auszüge aus einem proprietären Byte-Code sind, mit dem ich bei meiner Firma arbeite. Wir haben eine proprietäre Programmiersprache, die zu diesem Bytecode kompiliert, der von unserem Produkt interpretiert wird, und einige der Werte, die ich erwähnte, sind echte Bytecodes, die wir tatsächlich verwenden. 13
ist eigentlich pushAnything
mit komplexen Parametern, aber ich behalte die Dinge für die Antwort einfach.
Sie haben eindeutig eigene Hausaufgaben gemacht, und ich sage gute Sachen (und habe Ihnen eins gewählt).
Wie Sie erfahren, je mehr Sie lesen, desto mehr sagen Sie "huh?"
Okay, zuerst, wenn Sie auf das Wort "Bytecode" stoßen, schließen Sie einfach das Fenster und hören Sie auf zu lesen, weil Sie sich auf dem falschen Weg befinden; wahrscheinlich eine Tangente im besten Fall und im schlimmsten Fall könnte man jemanden lesen, der versucht klüger zu klingen, als er wirklich ist, indem er technisch klingende Schlagworte in sein Schreiben wirft.
Was nun das Wort "Opcode" betrifft, so gibt es diese wirklich, aber verstehen Sie, dass diese Zahlen tatsächlich symbolisch sind, damit die Menschen sie begrifflich erfassen können. Im wirklichen Leben sind sie super-ultra-winzige Schalter.Wenn Sie Geschichte und Technologie vor dem Internet (oder Farbfernsehen) wirklich mögen, schlagen Sie Sätze wie Schmetterlingsschalter, Vakuumröhren, Schmetterlingsmädchen nach und ich vergesse die anderen Wörter. Dies war zurück, bevor Transistoren existierten. Die ursprünglichen riesigen Computer verwendeten tatsächlich Vakuumröhren und erzeugten genügend Wärme, um einen gesamten Boden (oder zwei oder drei) eines Bürogebäudes im tiefsten Winter zu erwärmen. Die Stromaufnahme war erstaunlich.
Die Sache, die Sie dabei beachten sollten, ist, dass diese Computer "programmiert" wurden, indem man einzeln Schmetterlingsschalter umlegte ("Fledermausgriffe" waren ein anderer Begriff), die einzelne Leitungen von einzelnen Röhren verbanden und trennten was noch.
Die Fakten waren: Sie programmierten einen Computer, indem Sie die Fledermausgriffe umdrehten, die mit den Leitungen verbunden waren, die mit verschiedenen Röhren verbunden waren.
Wenn du einen Opcode von 90h schreibst (ich glaube, das ist ein NOP in x86, jemand korrigiert mich und ich werde es reparieren), machst du (mit dem heutigen Hallo-Tech-Wowe-Zowee) das Gleiche wie die Schmetterlings-Girls schon in der Steinzeit der Computer.
Genauer gesagt "werfen" Sie diese "Schmetterlingsschalter" ...
Hier ist der große Unterschied (und Teil des heutigen Hi-Tech Wowee-Zowee) ...
Sie mussten genau diese Schalter an genau einer Stelle auf den Boden werfen. Du wirst sie überall spiegeln, wo du willst. Drei andere Programme werden zusammenarbeiten und diese Entscheidungen für Sie treffen.
Diese drei Programme sind - Der Monteur - Der Linker - Der Lader
Ich hoffe also, dass dies die Grundlage dafür ist, dass Sie verstehen, dass der OPCODE eine mentale Repräsentation einer Menge kleiner Schalter ist, die "geöffnet" oder "geschlossen" werden. .
(Eigentlich hat der High-Tech-Wowe-Zowee es noch einen Schritt weiter gebracht, aber es ist der gleiche Effekt wie die Butterfly-Schalter früherer Generationen.)
Wie auch immer, es funktioniert so.
Menschen entschieden, dass es eine Anweisung geben würde, nichts zu tun; genannt ein NOP
Also, Sie tippen die Buchstaben NOP
in Ihrem Texteditor so ein
Sie speichern dann die Datei.
Dann bitten Sie den Assembler, diese Datei zu assemblieren
Wenn der Assembler NOP
sieht, erstellt er die 90
(in hex) in der Objekt -Datei, die er für den Linker erstellt.
Der Linker verwendet die Objektdatei und erstellt eine ausführbare Datei
Der Loader platziert diese ausführbare Datei wo immer sie will. (Anmerkung: In früheren Zeiten von Mikrocomputern musste der Softwareschreiber entscheiden, wohin er die ausführbare Datei legte; das war ein Konflikt-Köder, wie Sie es nicht glauben würden.)
Wie auch immer, das NOP
wurde 90
an irgendeiner Stelle in der EXE
Datei und der Loader hat es in einem guten Bereich für Sie gespeichert, basierend auf 179 Regeln, über die Sie sich keine Sorgen mehr machen müssen.
Der Lader wird dann aus dem Bild und lässt Ihr Programm die CPU haben.
Die CPU holt Ihre erste Anweisung und beginnt zu gehorchen.
Wenn die CPU zu dem Byte kommt, das 90
enthält, wird es dasselbe sein, wie der Schmetterling von Generationen nachher schaltet.
Während der Strom keine langen Kabel auf dem Boden bewegt, wird er sehr ähnliche (und funktional äquivalente) Dinge innerhalb des ASICs machen.
Nun, mit allem, was geschrieben wurde (danke, wenn du noch liest), kannst du diese einseitige Erklärung verstehen, was ein Opcode eigentlich ist ...
Nun zu Ihrer zweiten Frage, was der Maschinencode ist.
Wenn etwas unklar ist, fragen Sie im Kommentarbereich und ich werde versuchen, diese Antwort zu bearbeiten.
Kurz gesagt:
"Assembly" ist das, was Sie durch einen "Assembler" führen. Ein Assembler ist ein Programm, das mehrere Decks von Lochkarten einliest und sie zu einem einzigen Programm zusammenfügt.
Oder zumindest war das früher so. Jetzt werden die Karten durch Disketten ersetzt. Aber die Daten auf den "Karten" sind eine "Maschinensprache", die die numerischen Werte für die Maschinenanweisungen sind.
Aber moderne Assembler sind SAPs - Symbolic Assembler Programs - so können Sie die numerischen Werte durch Symbole ersetzen - sagen wir "LOD" für eine Ladeanweisung, "R1" für Register 1 und "label5" für die Befehlsadresse 26734.
"Maschinensprache" ist die Art, wie individuelle Anweisungen (oder "Befehle", wenn Sie ein Brite sind) für die CPU dargestellt werden. Für einen symbolischen Assembler könnten Sie "LOD R1, LOOPCOUNT" haben, um die Anweisung darzustellen, den Wert des Wortes LOOPCOUNT in Register 1 zu laden. "LOD" ist übrigens der "Opcode" - die (symbolische Version von der numerische Wert, der dem Computer sagt, was als nächstes zu tun ist. (Und beachten Sie, dass jedes unterschiedliche Computerdesign eine andere Maschinensprache verwendet, möglicherweise mit anderen Symbolen für die Opcodes. Das meiste, was Sie im Internet finden, ist die eine oder andere Version der Intel Maschinensprache, aber Sie würden sagen, die IBM 370 zu radikal anders sein.)
"Bytecode" ist eine andere Art von "Maschinensprache", die auf einer "virtuellen Maschine" statt auf echter Hardware arbeitet. Der bekannteste Fall ist die Java Virtual Machine. "Bytecode" ist eine Notation, die der normalen "Maschinensprache" ähnelt, aber zu einem gewissen Grad idealisiert ist, da das Ausführen auf einer virtuellen Maschine es von einigen Realitäten einer realen Hardwareumgebung befreit.
Die Beziehung ist:
%Vor% Die Assembler Anweisung ist menschlich lesbarer Code, wie zum Beispiel: mov rax, 0x2000004
Der Opcode ist der Teil des Maschinencodes, der sich auf die Anweisung bezieht, aber aus der Sicht der CPU (es ist also nicht nur MOV, sondern MOV, um sich zu registrieren). Zum Beispiel finden Sie hier für i386 MOV-Opcodes:
MOV reg32, immediate value
wird als B8
+ Registercode codiert (AX ist der erste, also ist es 0), 04 00 00 02
Byte-Code entspricht dem Maschinencode, aber für virtuelle Maschinen wie den JVM. Der Begriff Bytecode-Codes von den ersten Umgebungen, die diese Technologie verwendeten (p-Code aus der UCSD Pascal-Compiler), der ein Byte zum Codieren der virtuellen Anweisung verwendete. Hier finden Sie zum Beispiel die kleine P-Code-Sammlung und die neueren und umfangreicher JVM-Bytecode hier
Zu beachten: LLVM verwendet ein Zwischenformat (IF), das in einem komprimierten Formular
Montage: Vom Menschen lesbare Instruktoren für den Assembler + Datenbytes + Operatoren
Maschinencode: Die tatsächlichen Bitfolgen, die die CPU versteht.
Es enthält:
Bytecode: Dies ist der Code, der von einem Interpreter gelesen wird (Die meisten Java-Implementierungen sind eigentlich ein Interpreter Dieser liest Bytecode und verwendet diesen Bytecode zur Auswahl eine Sequenz von Maschinencode, um die CPU tatsächlich ausführen zu lassen). Bytecode wird oft verwendet, um den gleichen Quellcode für mehrere verschiedene zu verwenden CPUs.
Opcode: Das erste (oder zwei) Byte des Maschinencodes. Es verhält sich wie ein Selektor um der CPU mitzuteilen, welche Mikrocodesequenz die CPU ausführen soll (so etwas wie eine switch-Anweisung in C)
Mikrocode:
Die festverdrahteten Befehlssequenzen innerhalb der CPU, die verwendet werden
führe den Maschinencode aus.
Es gibt viele Mikrocode-Sequenzen,
mindestens eine Sequenz für jeden Opcode.
Im Allgemeinen ist der Rest des Maschinencodes nur Parameter
zu der Mikrocode-Sequenz, die durch den Opcode ausgewählt wird
Jede Mikrocodesequenz enthält Anweisungen zu
Öffnen / Schließen von Toren, Uhrendaten, Weiterleitung von Informationen zum / vom Akkumulator usw.
Tags und Links c assembly llvm compiler-construction bytecode