Autor Thema: Lowest Level - Eigener Compiler  (Gelesen 16853 mal)

MagicTom

  • Beiträge: 4
    • Profil anzeigen
Gespeichert
« Antwort #20 am: 17. July 2005, 21:08 »
[MM]: kann es sein dass deine Webseite nicht erreichbar ist? Bei mir funktioniert sie seit Tagen nicht und hat seit dem ich das erste mal dieses Thema gelesen habe, auch noch nie funktioniert  :?:

[MM]

  • Beiträge: 130
    • Profil anzeigen
    • www.mmcoding.eu
Gespeichert
« Antwort #21 am: 17. July 2005, 21:34 »
Ich schätze du hast das Thema dann am Freitag entdeckt?
Das Problem ist mir bekannt, mit Beschwerden wendet euch bitte an das HRZ Kompetenzteam unserer Lehranstalt...  :wink:

Ich schätze da ist am Freitag nachdem alle schon weg waren der Apache kaputt gegangen. Wird aber bestimmt behoben sein, wenn am Montag Mittag die ersten RZ Murkel aus dem Wochenende zurück kommen.

Also einfach morgen nochmal probieren,

MM

matthieuriolo

  • Beiträge: 226
    • Profil anzeigen
Gespeichert
« Antwort #22 am: 18. July 2005, 13:20 »
Hey
Also erst mal ein grosses Lob. Sehr intressante projekte, nur blicke ich nicht durch ;) Denn beim compiler müssste man doch es irgendwie in 0 und 1 umschreiben. Das finde ich nicht! Und genau das würde mich eben intressieren...


Und was ist eine com datei? Dies ist wahrscheinlich eine sehr blöde frage aber es is only windows und darum für mich unbekannt (mac user). Hab schon danach gesucht aber nix gefunden  :oops:


Danke

DarkThing

  • Beiträge: 652
    • Profil anzeigen
Gespeichert
« Antwort #23 am: 18. July 2005, 13:23 »
Zitat von: [MM]
Nicht das ich wüßte. Musst dir halt nur mal überlegen wie die Wertigkeiten sind, und das ganze auf Papier mal durchspielen. Einen Tipp kann ich aber noch geben:
Bei +,- bzw *,/ sollte man auf jeden Fall die gleiche Wertigkeit verwenden, sonst kommt es zu seltsamen Effekten, wie ich feststellen musste...  :wink:

Ansonsten kann ich dir nur raten, dass du dir mal ansiehst, wie mein Compiler die Debugausgabe mit dem Parameter "-stt" macht. Damit sollte man schon die meisten kniffeligen Stellen erkennen können.
MM

Werd ich machen ^^ Im Moment hab ich eh ne "Kreativitäts"-Pause  :wink: Und danach werd ich das dann einbauen. Der Rest (if, schleifen, ...) erscheint mir dann wieder einfacher.

[MM]

  • Beiträge: 130
    • Profil anzeigen
    • www.mmcoding.eu
Gespeichert
« Antwort #24 am: 18. July 2005, 14:02 »
@matthieuriolo:
Erst zum einfachen Teil:
com Dateien sind ausführbare Dateien ohne Header oder feste Formatierung. Das heißt sie werden vom System an die Adresse 0x100 eines Segmentes geladen und dann wird einfach der IP auf 0x100 gesetzt. Also die primitiveste Art ausführbaren Code zu verpacken (siehe command.com bei ms dos).  Oft befinden sich auch Code, Stack und Daten im gleichen Segment, wenn das Programm größer wird als 64K muss der Programmiere das im Program selber regeln.

So, nun zu 0 und 1:
In der Datei code.c ist ein Makro definiert MAKE_ASM:
#define MAKE_ASM(s) c=atocommand((s));if(Optionen.dbc){fprintf(dbc,commandtoalong(c));}add_opcode(c.opcode.hex);
Wenn du dir die Datei ansiehst wirst du feststellen, dass dort zB Zeilen auftauchen, die das heißen:
MAKE_ASM("mov eax,ebx")
Es wird die Funktion atocommand() aufgerufen, die in asm.c steht (glaub ich). Diese Funktion liefert eine Struktur mit dem Bytecode zurück, der dann von dem Makro in den Codebuffer eingetragen wird, welcher später in die obj Datei geschrieben wird.
So, die Datei asm.c ist 1:1 aus meinem Assembler übernommen und es mag etwas umständlich erscheinen, den Umweg über den String zugehen, der dann wieder vom Assembler analysiert wird, aber so hat man gleich den Stribng für Debugausgaben.
Ich gehe mal davon aus, dass du dich dafür interessierst, wie nun aus den ASM-Befehlen der Opcode erzeugt wird, denn wie man auf die ASM-Befehle kommt sollte klar sein.
Was macht also die Funktion atocommand: Es wird analysiert, welcher Befehl mit welchen Parametern benutzt wird und dann wird der Opcode generiert. Als Beispiel hier mal den str Befehl, der mit einer Speicherstelle oder einem Register benutzt werden kann:

    case _STR_:
      if(com->p1.typ==_REG_){ // str reg
        add("000011110000000011001");
        addreg(p1);
        break;
      }
      if(com->p1.typ==_MEM_){ // str mem
        add("0000111100000000");
        addmod(p1);
        add("001");
        addrm(p1);
        addstuff(p1);
        break;
      }
      break;

Die Funktion add fügt ganz konkret die 1/0 an den Opcode an, addreg entsprechend den Code für das beschriebene Register und addrm/addmod analog für die Speicheradressierung.

So, das war nun zwar etwas viel Text, aber ich hoffe ich konnte deine Frage beantworten, oder dir zumindest aufzeigen, wo du die 'Aha'-Stellen finden kannst.

MM

matthieuriolo

  • Beiträge: 226
    • Profil anzeigen
Gespeichert
« Antwort #25 am: 18. July 2005, 14:38 »
Danke vielmal für diese ausführliche erklärung. Ich habe im moment Ferien und werde öfters zum prgrammieren kommen. Darum intressiere ich mich so sehr für deinen Compiler. Aber ich habe noch 2 fragen. So wie ich das verstanden habe machst du aus dem C ein asm Code. Der tust du dann compilieren. Wo bleibt die Optimation? Oder is die irgendwo weiter versteckt? Und wenn man zum beispiel short int = 2; macht. Wie macht man das man nen short int hat, vorallem das es überhaupt als ein int deklariert wird. Und wo wird das asm Compiliert?


Ok, das waren jetzt mehr als 2 Fragen  :D

[MM]

  • Beiträge: 130
    • Profil anzeigen
    • www.mmcoding.eu
Gespeichert
« Antwort #26 am: 18. July 2005, 18:59 »
Nett,ich habe Donnerstag meine letzte Prüfung und dann 2 Monate Vorlesungsfrei, dann werde ich mich mal an einen neuen, besseren Compiler setzen  :D
Also, wenn Jemand schon immer einen C-Compiler haben wollte, der was ganz bestimmtes können sollte....  :wink:

Nun zu deinen Fragen matthieuriolo:
Optimierung findet nur auf Ebene des Token Trees statt bzw in der Umsetzung der Assembleroperationen. Eine Zuweisung von 1 wird zb so dargestellt:
xor eax,eax
inc eax

Und auf der Ebene des Token Trees werden überflüssige Operationen gekürzt, wie zb Addition von Konstanten werden vorberechnet.
Wenn u Optimierungen in der Größenordnung haben willst, wie zB das Weglassen einer Zuweisung, wenn die Variable nie benutzt wird, dann müßte man sehr viel mehr Aufwand treiben, was ich aber nicht gemacht habe.

Und die Sache mit den Variablen ist dann etwas trickreich. Man unterscheidet sinnvoller Weise zwischen globalen, lokalen und Parametervariablen.
Bei einem globalen int werden einfach irgendwo im Datenbereich 4 bytes reserviert, und immer auf diese feste Adresse verwiesen (die Adresse ist erst dem Linker bekannt).
Die anderen beiden Typen sind etwas komplizierter, da sie auf dem Stack liegen.
Beim Eintritt in eine Funktion wird der esp in den ebp kopiert. Alle Aufrufparameter liegen also über ebp. Wenn eine lokale Variable deklariert wird, so wird einfach esp um die erforderliche anzahl an Bytes verringert und der Zugriff erfolgt dann durch ebp-x, wobei x sich nach der Anzahl der schon auf dem Stack befindlichen Variablen richtet.
Um das etwas zu verdeutlichen hier ein Beispiel:

void func(int para){
  var=0x10;
  para=0x20;
}

0004: func()
func_begin:
6689E5           mov ebp,esp
{
 < create  short var (2) >
6681EC02000000   sub esp,02
 < local stack: 2 >
0007: var=0x10;
B81000           mov ax,010
678945FE         mov [ebp-02],ax      <-- var liegt bei ebp-2
0008: para=0x20;
66B820000000     mov eax,020
6667898504000000 mov [ebp+04],eax   <-- para bei ebp+4
6681C402000000   add esp,02
66C3             ret
}
0010: end of func()


Man muss halt nur genau aufpassen, wo was liegt, denn auf dem Stack liegt dazwischen ja noch die Rücksprungadresse, und wenn man die überschreibt kommt man Sonstwo raus.
Klar soweit?

MM

matthieuriolo

  • Beiträge: 226
    • Profil anzeigen
Gespeichert
« Antwort #27 am: 20. July 2005, 20:22 »
Hey
Ich habe nochmal ein frage an dich. Wenn man mit deinem Compiler den Befehl mov compiliert wird aus dem das 000011110010001011. So wie ich das gesehen habe. Sind diese befehle und länge irgendwie eingeschrenkt? Gibt es irgendwo trennungen? Bin mir am überlegen dein Compiler noch eine Disassemblierung einzubauen ;)

[MM]

  • Beiträge: 130
    • Profil anzeigen
    • www.mmcoding.eu
Gespeichert
« Antwort #28 am: 20. July 2005, 21:31 »
Ich fürchte ich verstehe die Frage nicht.
Die Befehle sind natürlich an 8 Bit ausgerichtet und können daher sehr bequem als Hecadezimalzahlen dargestellt werden:
B81000           mov ax,010
678945FE         mov [ebp-02],ax

Wie man sieht ist B81000 "mov ax,0x10" wenn man aber ein Byte weiter hinten anfäng zu disassemblieren, so wird murks draus.
Ein sehr guter 32Bit Debugger ist meiner Meinung nach der DDEB der kann den von meinem Compiler produzierten Code wunderbar lesen (bis auf Jumps).
Und was möchtest du genau machen? Der Compiler produziert ja schon bei Bedarf Ausgabedateien mit dem Opcode und ASM-Befehlen in Textform.

MM

matthieuriolo

  • Beiträge: 226
    • Profil anzeigen
Gespeichert
« Antwort #29 am: 20. July 2005, 22:12 »
Naja es ist ja so. Mov entspricht 000011110010001011. Was ist aber wenn man ein program hat das so aussieht 10100011100101111010001010101000010101111000110101011101000101010101001010100001010100 (KA was das is ... is ja auch wurst). Wie kann ich nun aus dem, ein assambler zurück machen? Da muss es also irgendwie trennungen geben. Wie du gesagt hast sind befehle immer (?) 8 bit lang, wie finde ich nun der Rest heraus?

elfish_rider

  • Beiträge: 293
    • Profil anzeigen
Gespeichert
« Antwort #30 am: 20. July 2005, 22:28 »
Stelle den Code besser hexadezimal anstatt binär dar, sodass du wenigstens einen gewissen Überblick hast.

x86-CPUs haben bestimmte Opcodes für alle Befehle, die sehr unterschiedlich lang und auch sonst ziemlich verschieden sind. Am besten schaust du in der Dokumentation von NASM, dort stehen zu jeder Befehlsvariante die Opcodes.

[MM]

  • Beiträge: 130
    • Profil anzeigen
    • www.mmcoding.eu
Gespeichert
« Antwort #31 am: 20. July 2005, 23:06 »
Genau, mov al,1 ist zb sehr viel kürzer als zb mov eax [ebp+esi*8]
Es kommt also auf den Einsprungpunkt an, wo du anfängst den Code zu disassemblieren. Und wenn dein Disassembler einen Opcode nicht kennt bzw eine Kombination nicht, dann wird ab der Stelle nur noch Müll produziert. Kannst ja mal versuchen mit dem MS-Debugger ein Programm in den Speicher zu schreiben, und dann läßt du es dir wieder disassemblieren nur mit der Startadresse +1:
C:\>debug
-a100
0CD8:0100 mov al,1
0CD8:0102 xor bx,bx
0CD8:0104
-u100 l4
0CD8:0100 B001          MOV     AL,01
0CD8:0102 31DB          XOR     BX,BX
-u101 l4
0CD8:0101 0131          ADD     [BX+DI],SI
0CD8:0103 DB20          ESC     1C,[BX+SI]TBYTE PTR [BX+SI]
-

Und bzgl der Länge:
0CD8:0100 B001          MOV     AL,01
0CD8:0102 B80100        MOV     AX,0001


Und ich würde dir als Doku die von Intel empfhelen, den Link findest du weiter Oben auf Seite 1 in einem meiner Posts.
Und was willst du denn genau machen für den Compiler, oder interessiert es dich nur so?

MM

matthieuriolo

  • Beiträge: 226
    • Profil anzeigen
Gespeichert
« Antwort #32 am: 20. July 2005, 23:19 »
Das is ja einiges komplizierter als ich gedacht habe! Dann lass ich es lieber sein und mache lieber mal das gleiche wie du ^^ "klein" anfangen is hier die Devise. Ich hab zwar gesagt für den Compiler aber das is eigentlich mist. Weil jeder Compiler bringt ungerfähr den gleichen bit hervor (sonst würde es nicht das gleiche machen) also wäre es ja egal mit welchem man es kompiliert hat.

DarkThing

  • Beiträge: 652
    • Profil anzeigen
Gespeichert
« Antwort #33 am: 21. July 2005, 16:59 »
Es ist wirklich egal - solange man alle Opcodes abfragt. Aber nicht jeder Compiler erzeugt den gleichen Code. Die wirklich großen Compiler haben z.B. noch viele Optimierungen drin und die Compiler setzen ifs, usw. manchmal unterschiedlich um.
Auch ein cooles Projekt wäre ein Decompiler sozusagen. Das heißt man macht aus den Opcodes/Assembler wieder C-Code. Das ist sicher höllisch schwer aber sollte bis zu einem gewissen Grad machbar sein.

SSJ7Gohan

  • Beiträge: 398
    • Profil anzeigen
Gespeichert
« Antwort #34 am: 22. July 2005, 12:51 »
Falls du dich an einen neuen Compiler setzt, könnstest du evt. die Möglichkeit einbauen, die Zielsprache leicht zu verändern? Also nicht nur in x86 Assembler Code zu erzeugen, sondern auch in anderen Sprachen? Es würde ja genügen, die Codeerzeugung von dem eigentlichen Compiler (also Lexer und Parser) zu trennen. Das würde mir sehr helfen.

[MM]

  • Beiträge: 130
    • Profil anzeigen
    • www.mmcoding.eu
Gespeichert
« Antwort #35 am: 22. July 2005, 14:26 »
Es wäre möglich asm-Code zu generieren, wobei der Code natürlich aus x86 Befehlen besteht. Würde das reichen, oder brauchst du Code für einen ganz anderen Befehlssatz (ATMEL 8051 oder sowas verrücktes)?

MM

SSJ7Gohan

  • Beiträge: 398
    • Profil anzeigen
Gespeichert
« Antwort #36 am: 22. July 2005, 14:32 »
Ich brauche den Code für eine VM, und der Code sieht ziehmlich anders aus, als der x86, es wäre also gut, wenn du die Codegenerierung von den anderen Teilen des Compiler trennen könntest. =D

[MM]

  • Beiträge: 130
    • Profil anzeigen
    • www.mmcoding.eu
Gespeichert
« Antwort #37 am: 22. July 2005, 15:53 »
Das wird nicht so einfach sein. Aber du könntest die Funktionen überschreiben, die das übernehmen (in der aktuellen Version ist das in code.c token_operant()). Ich hätte jetzt spontan keine Idee, wie man das abnders machen könnte.

MM

[MM]

  • Beiträge: 130
    • Profil anzeigen
    • www.mmcoding.eu
Gespeichert
« Antwort #38 am: 24. July 2005, 23:31 »
Bin grad dabei einen neuen Compiler zu basteln und überlege, ob ich wie bei dem Alten einfach die mehrdimensionalen Felder weglassen sollte.
Damit meine ich nicht die Möglichkeit auf zB einen char ** mit [..][..] zuzugreifen, sondern die Möglichkeit ein Feld zu erstellen, welches mehr als eine Dimension hat, denn [10][20] ist ja das selbe wie [200].
Was meint ihr dazu?
Benutzt eigentlich jemand von euch mehrdimensionale Felder?

MM

DarkThing

  • Beiträge: 652
    • Profil anzeigen
Gespeichert
« Antwort #39 am: 28. July 2005, 13:48 »
Ich benutz mehrdiemensionale Felder nur sehr selten, aber sie sind manchmal seht praktisch und koennen nie schaden.  :wink:

 

Einloggen