Frage zu Assembler und der Syntax
-
Ah noch ne frage wie kann man den eine Hochsprache in Binäre umwandeln also was ich meine ist wie wandelt der compiler den C++ code in binären code um?
Un wie ist das bei Assemblersprache da ja asm ja fast 1:1 ist?
-
Compilerbau ist eine ziemlich komplizierte Angelegenheit. Einen sehr groben Einblick gibt dir vielleicht Wikipedia.
Ein Assembler ist dagegen relativ simpel gestrickt. Wenn man von Zusaetzen wie Meta- und Strukturbefehlen absieht, laeuft es im Prinzip darauf hinaus, dass sich der Assembler in einem ersten Durchgang Zeile fuer Zeile hernimmt, die Zeichenfolge bis zu den ersten Leerzeichen mit einer Liste zulaessiger Mnemonics abgleicht und wenn er was gefunden hat schaut, ob die dahinterstehenden Parameter zulaessig sind. Mit den Mnemonics sind dann die entsprechenden Maschinencodes und Vorschriften zu deren Modifizierung in Abhaengigkeit von der Parametern verknueft. So kann jeder Zeile die Bytefolge eines maschinenlesbaren Befehls zugeordnet werden. Stoesst der Assembler auf Befehle, die Speicher adressieren, werden statt der Adressen zunaechst ausreichend grosse Platzhalter eingefuegt und diese Stellen mit den adressierten Variablen oder Labels gespeichert.
Stoesst der Assembler auf Labels oder "Variablen", werden ihre Adressen soweit schon feststellbar ebenfalls gespeichert.
Am Ende dieses Vorgangs liegt also eine Rohfassung des kompletten Programms inklusive Daten, Labels/Variablen und deren Adressen im Speicher vor.
In einem zweiten Schritt werden dann die Platzhalter fuer Adressen durch die gespeicherten Adressen der Labels ersetzt.
Zur Optimierung kann dann in weiteren schritten die Adressweite von Befehlen angepasst werden. zB. kann man fuer nahegelegene relative Sprungziele auch 8-Bit statt der sicherheitshalber zuvor reservierten maximalen Adressweite verwenden...
-
Damit ist aber nun erst die Hälfte der Arbeit zu einem ausführbaren Programm geschaffen. Jetzt kommt nämlich das Betriebssystem ins Spiel. Nehmen wir ein ganz simpel gestricktes, dann bleibt immer noch das Problem, dass man nicht weiß, wo das Programm im Speicher landen wird. Jetzt gibt es im Programm aber nicht nur relative Sprünge, oder relative Datenadressen - sondern bei weiten Distanzen eben auch absolute Adressen - und die können noch nicht bekannt sein. Daher werden solche Stellen praktisch offen gelassen, und im Anhang des Programms eine Art Liste dieser Stellen drangeklebt. Das Ganze wird nun - sagen wir mal als .exe - abgespeichert. Wenn Du dem Kommandointerpreter nun die Anweisung test.exe eintippst, so hüpft Dein Programm nicht einfach in den Speicher und legt los, sondern dieser Kommandointerpreter bringt es an eine ihm passende Stelle, bereitet es vor, indem er nun jene Liste auswertet und die fehlenden Adressen ergänzt und dann ruft er es wie eine Art Unterprogramm auf. Soweit zum einfachsten Fall! Es gibt nun also durchaus unterschiedliche Dateiformate für ausführbare Programme, und zusammen mit anderen Notwendigkeiten, kann es Dir eben passieren, dass ein DOS-Programm nicht mehr unter Win laufen will, oder grob gesagt, obwohl das Programm mit dem gleichen Assembler auf dem gleichen Prozessor erstellt wurde, spielt eben das Betriebssystem noch eine entscheidende Rolle.
Daher meine Anregung - zum Grundverständnis eben auch gleich noch in dieses Gebiet einlesen.
-
Ich frag mich warum gerade Assembler schneller seien soll als c++ wenn doch beide in Machinensprache übersetzt werden?
Das sind meistens Kleinigkeiten (Dinge die man normalerweise braucht aber in diesem Spezialfall überflüssig sind)
Heutzutage ist der Unterschied durch extrem intelligente Compiler wie den gcc sehr gering und da überwiegt meistens der Voteil der besseren Les- und Wartbarkeit von C(++)
-
fließkommafreak schrieb:
und sollen wir dich jetzt immer abc. w nennen, weil es sich einfach besser liest?
Nein, das ist was anderes. Das ist mein Benutzername in diesem Forum, kurz, anonym und neutral.
Fehlende Leerzeichen stören den Lesefluss, weiss nicht, wie es die anderen sehen. Klar, dem Assembler ist es egal, ob da ein Leerzeichen fehlt oder ob da 20 Leerzeichen stehen. Das ist aber kein Grund, den Quelltext für den Assembler zu schreiben. Vielleicht hat der eine oder andere Mensch Lust, den Quelltext zu lesen.PS: Ich durfte mal Vorlesungen bei einem Informatik-Professor besuchen, er hatte in seinem Quelltext so wenig wie möglich Leerzeichen benutzt, z.B. so ähnlich:
for(i=0;i<N;i=i+2){a[i]=b[i]-1;c[i?0:1]=a[i];}
Vielleicht hat er dabei an den Compiler gedacht, dass er zu viele Zeichen parsen soll und dann hatte er vielleicht Mitleid mit dem Compiler deswegen
Wie dem auch sei, seinen Quelltext hat er sicherlich für den Compiler geschrieben und nicht für seine Studenten...
-
Bitsy schrieb:
offtopic: Gibt's da bereits irgendwelche brauchbare Sourcen, die einen 386 in C emulieren? Mit so manchen Commands tue ich mich da durchaus etwas schwer...
hast du dir schon
http://www.dosbox.com/ angesehen?@abc.w
asm code nervt doch am meisten, wenn er unkommentiert oder schwachkommentiert ist, oder grundlagen voraussetzt, die nirgendwo im buch oder internet stehen, dann folgt die spezialsynthax(oft überflüssig) eines assemblers, von zuvielen speziellen hochsprachenelementen darin und ganz zu schweigen und von verwirrenden weihnachtsdisplätzchen-bezeichnungen(tolle assoziation zu den nummern an den weihnachskalendertürchen, was?, huch bin ich genial).
und wenn ich bei einem schon lange im gebrauch seienden system immer noch überflüssigerweise movl oder movb schreiben oder lesen muss oder soll, oder mainstreamfremde datentypenbezeichnungen, dann fühl ich mich vergessen. aber dies nur nebenbei. über asmsyntax kann man natürlich immer mal diskutieren, ist aber eher ein nebenschauplatz, wenn hier jemand im forum nach grundlagenerklärungen fragt und auf aufklärende antworten hofft. das ist in etwa so, als wenn ein kleines kind auf dich zukommt, und fragt:"spielst du mit mir pennis?" und du antwortest zeigefingerfuchtelnd:"hey, das sagt aber aber nicht, mein kind" "und in der nase bohren solltest du auch nicht". und c-code und c++code: habe ich nur selten lesbarer empfunden, als asm-code. zu den ausnahmen gehört vor allem der c/c++-code einiger wirklicher(!) code-künstler hier in forum oder der c-code für unbekannte microprozessoren.
der benutzername abc.w wirkt auf mich nicht neutral: erster eindruck: codierschützling, weiblich.
wie wäre es mit den alternativen aq, ay, as, a+b² oder gar +a ?@c/c++ vs asm: für windows-anwendungs programmierung weitgehend irrelevant, denn hier ist für den meisten sachen visual basic vorzuziehen oder für mathe ein matheprogramm, welches auch gleich plotten kann oder sinnvollere datentypen hat und genauer rechnen kann usw.
-
hast du dir schon
http://www.dosbox.com/ angesehen?Jo, da hat mir gerade die heavydebug-Version sehr geholfen!
Hat aber auch ein paar Mankos. Wenn's in die Interrupts im Real-mode geht, kann er keine 16-Bit disassemblieren - und der Debugger nimmt immer ein paar Schritte auf einmal und landet auch hinter Breakpoints. Ich habe die fertige Binary benutzt, das war nur Version 0.72. Möglicherweise ist das mittlerweile ausgemerzt.
-
for(i=0;i<N;i=i+2){a[i]=b[i]-1;c[i?0:1]=a[i];}
Ich denk das problem sind hier nicht die (fehlenden) Leerzeichen sondern die fehlenden Zeilenumbrüche
Aber wie heißt es schon im Linux kernel coding styleDon't put multiple statements on a single line unless you have
something to hide:
-
fließkommafreak schrieb:
...und wenn ich bei einem schon lange im gebrauch seienden system immer noch überflüssigerweise movl oder movb schreiben oder lesen muss oder soll, oder mainstreamfremde datentypenbezeichnungen, dann fühl ich mich vergessen.
Wenn du damit die AT&T Syntax für die x86er meinst - die ist meiner Meinung nach die am besten durchdachte Syntax auf dieser Welt
Aber es ist wahr, dass die "movl" z.B. unnötig oft an den Stellen eingesetzt werden, wo sie überflüssig sind. Ich mache diesen Fehler auch und würde mich freuen, wenn ich hier im Forum eine Funktion in AT&T Syntax poste, jemand darauf reagieren und mich gleich korrigieren würde, genauso wie in deinem Beispiel mit dem Kind.
linux_c89 schrieb:
Aber wie heißt es schon im Linux kernel coding style
Wenn du damit die Regeln in Documents/coding_style oder coding_rules (weiss jetzt nicht genau) meinst, da steht es glaube ich noch mehr drin, dass u.a. Kernighan und Ritchie Recht haben und dass Kernighan und Ritchie noch mal Recht haben
Und wenn du zufällig deren Buch "Programmieren in C" zur Hand hast - ich habe zufällig so eins ins Deutsche übersetzte aus dem Jahre 1983 - und in dem Buch mal blätterst, dann wirst du sehen: Alle Quelltexte sauber eingerückt und alle Leerzeichen da, wo sie auch hingehören. Vielleicht einer der Gründe für die Erfolgsgeschichte von C
-
Jaja die unglaublich überzeugende Begründung
but all right-thinking people know that (a) K&R are _right_ and (b) K&R are right
Ich meinte eigentlich dass es hier wichtiger ist die Zeilenumbrüche zu machen als Leerzeichen. Mit Leerzeichen isses natürlich noch besser zu lesen
Und ich glaube nicht dass C so erfolgreich ist weil es so leicht lesbar und sauber formatiert ist^^ (ich sag nurfor(;P("\n"),R--;P("|"))for(e=C;e--;P("_"+(*u++/8)%2))P("| "+(*u/4)%2);
aus Humorseite)
-
die AT&T Syntax für die x86er ... - die ist meiner Meinung nach die am besten durchdachte Syntax auf dieser Welt
Kannst Du das begründen?
-
Erhard Henkes schrieb:
Kannst Du das begründen?
Ja... aber ich denke, eher subjektiv.
x86er Assembler-Programmierung betreibe ich als Hobby oder einfach aus Spass und als ich damit angefangen habe, konnte ich irgendwie wenig Info zu den Addressierungsarten finden. Alles war irgendwie verwirrend, z.B. [eax + ebx + 1] könnte genauso gut [1 + ebx + eax] sein oder [eax + ebx] könne man auch als [ebx + eax] schreiben. Ich wusste nicht so genau, was ist Basisregister, was ist Indexregister. Oder wann muss man z.B. "word ptr" oder "byte ptr" benutzen. Was ist überhaupt mit "ptr" gemeint, auf der Assembler-Ebene gibt es ja nur Adressen und Daten und der Begriff "ptr" passt irgendwie nicht dazu.
Ausserdem behandelten die meisten Assembler-Bücher oder Tutorials, die ich damals kannte, die Adressierungsarten irgendwie nur am Rande. Es fehlte an so was wie "das ist die und die Adressierungsart und das ist die Syntax dazu in Assembler". Ich habe mal eins gefunden, in dem es für jede Adressierungsart ein kleines Kapitel gibt und sogar mit Beispielen. Aber es gab da auch Unklarheiten wie z.B. Kapitel "Based Indexed" ... Codierbeispiel: MOV EAX, [EDI][EBX]. Toll, was ist denn nun Basisregister und was ist Indexregister
Mit der AT&T Syntax ist bekanntlich alles anders mit dem Vorteil der eineindeutigen Zuordnung der Register. Manchmal denke ich, wie weitsichtig die Entwickler der AT&T Syntax doch damals waren... denn mit dem simplen displacement(base, index, scale) und der "Kombinatorik" davon hat man alle Addressierungsarten abgedeckt, ohne Unklarheiten oder sonstige Zweifel.
Ich denke, dass ist der wichtigste Punkt, warum ich der Meinung bin, die Syntax ist am besten durchdacht. Ich könnte jetzt noch auf andere Kleinigkeiten eingehen, dass z.B. $-Zeichen gut ist, um zwischen Konstanten und Displacements zu unterscheiden - aber hab dazu keine Zeit mehr
-
Danke für die Argumente.
Außerdem behandelten die meisten Assembler-Bücher oder Tutorials, die ich damals kannte, die Adressierungsarten irgendwie nur am Rande. Es fehlte an so was wie "das ist die und die Adressierungsart und das ist die Syntax dazu in Assembler". Ich habe mal eins gefunden, in dem es für jede Adressierungsart ein kleines Kapitel gibt und sogar mit Beispielen.
Kannst Du die Adressierungsarten "Direkte Adressierung", "Indirekte Adressierung" und den Zusammenhang zwischen LEA und MOV,OFFSET bitte an dem Beispiel erklären:
0040131B sub esp,0x10 0040131E mov DWORD PTR [ebp-0x4],0x0 <--- local variable 1 [ebp - 4] int value = 0; 00401325 nop 00401326 mov eax,DWORD PTR [ebp+0xc] <--- move argument 2 to EAX 00401329 mov edx,DWORD PTR [ebp+0x8] <--- move argument 1 to EDX 0040132C lea eax,[edx+eax*1] <--- addition of argument 1 and argument 2 (LEA = Load Effective Address) 0040132F add eax,DWORD PTR [ebp+0x10] <--- add argument 3 to EAX 00401332 mov DWORD PTR [ebp-0x4],eax <--- move EAX to local variable 1 00401335 nop 00401336 mov eax,DWORD PTR [ebp-0x4] <--- move local variable 1 to EAX (return value of the function)
Hätte man dort auch MOV, OFFSET einsetzen können?
-
OK, ich versuch es, aber ich werde dabei die AT&T Syntax benutzen aus den o.g. Gründen...
In dem obigen Beispiel gibt es keinen einzigen Befehl mit der direkten Adressingsart. Bei der direkten Adressierungsart kennt man nur das Displacement. Dabei gibt es zwei Möglichkeit.
Entweder man kennt das Displacement auswendig oder der Linker vergibt das Displacement "automatisch":movl $42, 1000 # Schreibe die 32-Bit Konstante 42 an die bekannten Adresse 1000
.section .bss number: .int 0 .section .text movl $42, number # Nach dem Linken ist das Displacement von number bekannt und wird hier eingesetzt
Wegen der indirekten Adressierung: Alle Befehle mit DWORD PTR (in Deinem Beispiel) verwenden die indirekte Adressierungsart. Dabei ist das Register ebp Basisregister und die Konstante, die zu ebp addiert/subtrahiert wird, das Displacement. Diese Kombination nennt man, glaube ich, auch einfach "Based".
Die folgende Zeile0040132C lea eax,[edx+eax*1]
hat folgende Bedeutung: eax = edx + eax. Oder wenn man es genau nimmt: eax = edx + (eax * 1) + 0. Das war's. Der Befehl lea wird also zur Addition missbraucht, warum auch immer. Deswegen kann man hier kein MOV,OFFSET einsetzen.
Im Prinzip hätte man das obige Beispiel wie folgt machen können (wenn ich es richtig sehe):movl 0x4(%esp), %eax addl 0x8(%esp), %eax addl 0xC(%esp), %eax ret
D.h. ohne ebp und ohne die lokale Variable, einfach alles vom Stack aufaddieren und das war's...
-
Wenn man mit -O3 optimiert wird das auch nur mit add ausgeführt.
Ansonsten:
int doSomething(int val1, int val2, int val3) { int value = 0; asm("nop"); value = val1+val2+val3; asm("nop"); return value; } int main() { doSomething(42,-12,-14); return 0; }
objdump mit AT&T Syntax
00000000 <_doSomething>: int doSomething(int val1, int val2, int val3) { 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 10 sub $0x10,%esp int value = 0; 6: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp) asm("nop"); d: 90 nop value = val1+val2+val3; e: 8b 45 0c mov 0xc(%ebp),%eax 11: 8b 55 08 mov 0x8(%ebp),%edx 14: 8d 04 02 lea (%edx,%eax,1),%eax 17: 03 45 10 add 0x10(%ebp),%eax 1a: 89 45 fc mov %eax,-0x4(%ebp) asm("nop"); 1d: 90 nop return value; 1e: 8b 45 fc mov -0x4(%ebp),%eax } 21: c9 leave 22: c3 ret 00000023 <_main>: int main() { 23: 55 push %ebp 24: 89 e5 mov %esp,%ebp 26: 83 e4 f0 and $0xfffffff0,%esp 29: 83 ec 10 sub $0x10,%esp 2c: e8 00 00 00 00 call 31 <_main+0xe> doSomething(42,-12,-14); 31: c7 44 24 08 f2 ff ff movl $0xfffffff2,0x8(%esp) 38: ff 39: c7 44 24 04 f4 ff ff movl $0xfffffff4,0x4(%esp) 40: ff 41: c7 04 24 2a 00 00 00 movl $0x2a,(%esp) 48: e8 b3 ff ff ff call 0 <_doSomething> return 0; 4d: b8 00 00 00 00 mov $0x0,%eax } 52: c9 leave 53: c3 ret }
-
Ich hab's mal mit dem mingw 3.4.5 und Optimierung -O3 ausprobiert. doSomething wird gar nicht aufgerufen
Ich mache es nicht mit objdump sondern mit der Compiler-Option -S in Verbindung mit -fverbose-asm (man bekommt zusätzlich Kommentare, was was ist):
mingw32-gcc -S tmp.c -g0 -fverbose-asm -O3 -fomit-frame-pointer
Ergebnis ist tmp.s und sieht so aus:
.file "tmp.c" # GNU C version 3.4.5 (mingw-vista special r3) (mingw32) # compiled by GNU C version 3.4.5 (mingw-vista special r3). # GGC heuristics: --param ggc-min-expand=99 --param ggc-min-heapsize=130941 # options passed: -iprefix -auxbase -g0 -O3 -fverbose-asm # -fomit-frame-pointer # options enabled: -feliminate-unused-debug-types -fdefer-pop # -fomit-frame-pointer -foptimize-sibling-calls -funit-at-a-time # -fcse-follow-jumps -fcse-skip-blocks -fexpensive-optimizations # -fthread-jumps -fstrength-reduce -funswitch-loops -fpeephole -fforce-mem # -ffunction-cse -fkeep-static-consts -fcaller-saves -freg-struct-return # -fweb -fgcse -fgcse-lm -fgcse-sm -fgcse-las -floop-optimize # -fcrossjumping -fif-conversion -fif-conversion2 -frerun-cse-after-loop # -frerun-loop-opt -fdelete-null-pointer-checks -fschedule-insns2 # -fsched-interblock -fsched-spec -fsched-stalled-insns # -fsched-stalled-insns-dep -fbranch-count-reg -freorder-blocks # -freorder-functions -frename-registers -fcprop-registers -fcommon # -fverbose-asm -fregmove -foptimize-register-move -fargument-alias # -fstrict-aliasing -fmerge-constants -fzero-initialized-in-bss -fident # -fpeephole2 -fguess-branch-probability -fmath-errno -ftrapping-math # -m80387 -mhard-float -mno-soft-float -malign-double -mieee-fp # -mfp-ret-in-387 -mstack-arg-probe -maccumulate-outgoing-args # -mno-red-zone -mtune=pentiumpro -march=i386 .text .p2align 4,,15 .globl _doSomething .def _doSomething; .scl 2; .type 32; .endef _doSomething: /APP nop /NO_APP movl 8(%esp), %eax # val2, val2 movl 4(%esp), %ecx # val1, movl 12(%esp), %edx # val3, addl %ecx, %eax #, tmp63 addl %edx, %eax #, value /APP nop /NO_APP ret .def ___main; .scl 2; .type 32; .endef .p2align 4,,15 .globl _main .def _main; .scl 2; .type 32; .endef _main: pushl %ebp # movl $16, %eax #, tmp66 movl %esp, %ebp #, subl $8, %esp #, andl $-16, %esp #, call __alloca call ___main # /APP nop nop /NO_APP leave xorl %eax, %eax # <result> ret
Man sieht, der Compiler hat die beiden nops eingefügt, aber es gibt keinen Funktionsaufruf. Also wegoptimiert.
Erhard, bist Du jetzt AT&T Syntax Fan? Wenn ja, könnte man vielleicht Marc++us an AT&T Syntax Highlighting in diesem Forum noch mal erinnern
Siehe hier: http://www.c-plusplus.net/forum/viewtopic-var-t-is-242135-and-highlight-is-.html
-
Erhard, bist Du jetzt AT&T Syntax Fan?
Ehrlich gesagt finde ich beides, sowohl Intel als auch AT&T Syntax, nicht eindeutig und eingängig genug. Daher bleibe ich einfach zwischen beiden unentschieden und versuche beides zu verstehen und zu nutzen. Mir geht es momentan darum, den Übergang von C nach Assembler und das Wechselspiel zwischen beiden wirklich zu verstehen. Denn dies ist für eigene Betriebssystementwicklung incl. Bootloader - ob man will oder nicht - notwendig.
Ich habe deinen Tipp aufgenommen und ausprobiert, allerdings mit version 4.4.1 (in code::blocks 10.05), sehr interessant:
.file "main.c" # GNU C (TDM-2 mingw32) version 4.4.1 (mingw32) # compiled by GNU C version 4.4.1, GMP version 4.3.0, MPFR version 2.4.1. # GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 # options passed: -iprefix # c:\programme\codeblocks\mingw\bin\../lib/gcc/mingw32/4.4.1/ # C:\temp\step1\main.c -mtune=i386 -auxbase-strip obj\Debug\main.o -g -g0 # -O3 -Wall -fverbose-asm -fomit-frame-pointer # options enabled: -falign-loops -fargument-alias -fauto-inc-dec # -fbranch-count-reg -fcaller-saves -fcommon -fcprop-registers # -fcrossjumping -fcse-follow-jumps -fdefer-pop # -fdelete-null-pointer-checks -fearly-inlining # -feliminate-unused-debug-types -fexpensive-optimizations # -fforward-propagate -ffunction-cse -fgcse -fgcse-after-reload -fgcse-lm # -fguess-branch-probability -fident -fif-conversion -fif-conversion2 # -findirect-inlining -finline -finline-functions # -finline-functions-called-once -finline-small-functions -fipa-cp # -fipa-cp-clone -fipa-pure-const -fipa-reference -fira-share-save-slots # -fira-share-spill-slots -fivopts -fkeep-static-consts # -fleading-underscore -fmath-errno -fmerge-constants # -fmerge-debug-strings -fmove-loop-invariants -fomit-frame-pointer # -foptimize-register-move -foptimize-sibling-calls -fpeephole -fpeephole2 # -fpredictive-commoning -freg-struct-return -fregmove -freorder-blocks # -freorder-functions -frerun-cse-after-loop -fsched-interblock # -fsched-spec -fsched-stalled-insns-dep -fsigned-zeros # -fsplit-ivs-in-unroller -fsplit-wide-types -fstrict-aliasing # -fstrict-overflow -fthread-jumps -ftoplevel-reorder -ftrapping-math # -ftree-builtin-call-dce -ftree-ccp -ftree-ch -ftree-copy-prop # -ftree-copyrename -ftree-cselim -ftree-dce -ftree-dominator-opts # -ftree-dse -ftree-fre -ftree-loop-im -ftree-loop-ivcanon # -ftree-loop-optimize -ftree-parallelize-loops= -ftree-pre -ftree-reassoc # -ftree-scev-cprop -ftree-sink -ftree-sra -ftree-switch-conversion # -ftree-ter -ftree-vect-loop-version -ftree-vectorize -ftree-vrp # -funit-at-a-time -funswitch-loops -fvect-cost-model -fverbose-asm # -fzero-initialized-in-bss -m32 -m80387 -m96bit-long-double # -maccumulate-outgoing-args -malign-double -malign-stringops # -mfancy-math-387 -mfp-ret-in-387 -mfused-madd -mieee-fp -mno-red-zone # -mno-sse4 -mpush-args -msahf -mstack-arg-probe # Compiler executable checksum: cf55188e7d2f640bec71afc62bf8d59d .text .p2align 2,,3 .globl _doSomething .def _doSomething; .scl 2; .type 32; .endef _doSomething: /APP # 4 "C:\temp\step1\main.c" 1 nop # 0 "" 2 # 6 "C:\temp\step1\main.c" 1 nop # 0 "" 2 /NO_APP movl 4(%esp), %eax # val1, val1 addl 8(%esp), %eax # val2, tmp63 addl 12(%esp), %eax # val3, tmp62 ret .def ___main; .scl 2; .type 32; .endef .p2align 2,,3 .globl _main .def _main; .scl 2; .type 32; .endef _main: pushl %ebp # movl %esp, %ebp #, andl $-16, %esp #, call ___main # /APP # 4 "C:\temp\step1\main.c" 1 nop # 0 "" 2 # 6 "C:\temp\step1\main.c" 1 nop # 0 "" 2 /NO_APP xorl %eax, %eax # leave ret
Hier ohne -O3: mingw32-gcc -S tmp.c -g0 -fverbose-asm -fomit-frame-pointer
.file "main.c" # GNU C (TDM-2 mingw32) version 4.4.1 (mingw32) # compiled by GNU C version 4.4.1, GMP version 4.3.0, MPFR version 2.4.1. # GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 # options passed: -iprefix # c:\programme\codeblocks\mingw\bin\../lib/gcc/mingw32/4.4.1/ # C:\temp\step1\main.c -mtune=i386 -auxbase-strip obj\Debug\main.o -g -g0 # -Wall -fverbose-asm -fomit-frame-pointer # options enabled: -falign-loops -fargument-alias -fauto-inc-dec # -fbranch-count-reg -fcommon -fearly-inlining # -feliminate-unused-debug-types -ffunction-cse -fgcse-lm -fident # -finline-functions-called-once -fira-share-save-slots # -fira-share-spill-slots -fivopts -fkeep-static-consts # -fleading-underscore -fmath-errno -fmerge-debug-strings # -fmove-loop-invariants -fomit-frame-pointer -fpeephole # -freg-struct-return -fsched-interblock -fsched-spec # -fsched-stalled-insns-dep -fsigned-zeros -fsplit-ivs-in-unroller # -ftrapping-math -ftree-cselim -ftree-loop-im -ftree-loop-ivcanon # -ftree-loop-optimize -ftree-parallelize-loops= -ftree-reassoc # -ftree-scev-cprop -ftree-switch-conversion -ftree-vect-loop-version # -funit-at-a-time -fvect-cost-model -fverbose-asm # -fzero-initialized-in-bss -m32 -m80387 -m96bit-long-double # -maccumulate-outgoing-args -malign-double -malign-stringops # -mfancy-math-387 -mfp-ret-in-387 -mfused-madd -mieee-fp -mno-red-zone # -mno-sse4 -mpush-args -msahf -mstack-arg-probe # Compiler executable checksum: cf55188e7d2f640bec71afc62bf8d59d .text .globl _doSomething .def _doSomething; .scl 2; .type 32; .endef _doSomething: subl $16, %esp #, movl $0, 12(%esp) #, value /APP # 4 "C:\temp\step1\main.c" 1 nop # 0 "" 2 /NO_APP movl 24(%esp), %eax # val2, tmp61 movl 20(%esp), %edx # val1, tmp62 leal (%edx,%eax), %eax #, D.1240 addl 28(%esp), %eax # val3, tmp63 movl %eax, 12(%esp) # tmp63, value /APP # 6 "C:\temp\step1\main.c" 1 nop # 0 "" 2 /NO_APP movl 12(%esp), %eax # value, D.1241 addl $16, %esp #, ret .def ___main; .scl 2; .type 32; .endef .globl _main .def _main; .scl 2; .type 32; .endef _main: pushl %ebp # movl %esp, %ebp #, andl $-16, %esp #, subl $16, %esp #, call ___main # movl $-14, 8(%esp) #, movl $-12, 4(%esp) #, movl $42, (%esp) #, call _doSomething # movl $0, %eax #, D.1244 leave ret
Kannst Du die Version ohne Aufruf der Funktion bitte erläutern? Wo werden dort die Parameter addiert?
-
Die Funktion ist ja bereits vom Compiler kommentiert
Kommentare beginnen mit dem #-Zeichen:
.text .p2align 2,,3 .globl _doSomething .def _doSomething; .scl 2; .type 32; .endef _doSomething: /APP # 4 "C:\temp\step1\main.c" 1 nop # 0 "" 2 # 6 "C:\temp\step1\main.c" 1 nop # 0 "" 2 /NO_APP movl 4(%esp), %eax # val1, val1 addl 8(%esp), %eax # val2, tmp63 addl 12(%esp), %eax # val3, tmp62 ret
Ausschnitt der Stelle, wo die Argumente aufaddiert werden:
movl 4(%esp), %eax # val1, val1 addl 8(%esp), %eax # val2, tmp63 addl 12(%esp), %eax # val3, tmp62
movl steht für "move long", long ist 32-Bit
addl steht für "add long"
val1, val2 und val3 (in den Kommentaren) stammen direkt aus der main.c:int doSomething(int val1, int val2, int val3)
tmp63 und tmp62 sind Zwischenergebnisse, vom Compiler automatisch generiert:
tmp63 = val1 + val2
tmp62 = tmp63 + val3Erhard Henkes schrieb:
Ehrlich gesagt finde ich beides, sowohl Intel als auch AT&T Syntax, nicht eindeutig und eingängig genug. Daher bleibe ich einfach zwischen beiden unentschieden und versuche beides zu verstehen und zu nutzen.
Ich denke, die Überzeugung und Zuneigung zur AT&T Syntax und GNU Assembler kommt noch
Ich denke, wenn man einen Assembler als Funktion betrachtet, die eingegebene Befehle in die Maschinenbefehle übersetzt/transformiert/abbildet/was auch immer, dann gilt folgendes:
GNU Assembler ist bijektiv
Rest ist surjektiv, hm, tja, bleibt nichts anderes übrig:
-
doSomething wird gar nicht aufgerufen
Meine Frage war: wie wird denn addiert, wenn doSomething bei dir nirgends aufgerufen wird? Da stimmt doch was nicht.
-
Es wurde wegoptimiert, weil das Ergebnis der Funktion niergends im Programm verwendet wird... ausser der NOPs, weil man diese mit asm("") eingefügt hat und Code in asm("") wird vom Compiler ohne weiteres übernommen, weil es wird optimistisch davon ausgegangen, dass der Programmierer weiss, was er da mit asm("") macht