Frage zu Assembler und der Syntax
-
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
-
abc.w schrieb:
...
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"
...Mir kommt das Grausen, die Syntax (links Displacement und Quelle, rechts Ziel) brrr...
Ich bleib bei der Intel-Syntax: mov Ziel, Quelle
mov eax, [esp+4] ; eax ist 32-Bit also braucht man nicht ; mov eax, dword ptr [esp+4] schreiben. add eax, [esp+8] add eax, [esp+12]
-
Birne schrieb:
Mir kommt das Grausen, die Syntax (links Displacement und Quelle, rechts Ziel) brrr...
Ich frage mich, warum
Man lernt seit dem ersten Schuljahr, von links nach rechts zu lesen und plötzlich kommt einem das Grausen, weil es richtig da steht und man doch von recht nach links lesen möchte
-
Die Reihenfolge opcode source, destination ist sicher intuitiver als der umgekehrte Weg. Das spricht für AT&T. Mich stören die unverständlichen % bzw. %% und auch so etwas: 4(%esp)
Wir können es aber nicht ändern, also sollten wir uns an beides gewöhnen. NASM verwendet standardisiert Intel syntax, und GCC liebt AT&T. Also bleibt nur volle Akzeptanz beider Methoden.
-
Mit dem %-Zeichen wollte man wahrscheinlich zwischen den Registern und Variablen unterscheiden. Man kann z.B. eine Variable eax anlegen und das Register %eax in der Variable eax speichern:
.section .bss eax: .int 0 # Variable eax .section .text movl %eax, eax # Register %eax speichern in der Variable eax
Sprich, Register sind was besonderes und verdienen das %-Zeichen
Die Kombination %% muss man nur im Inline-Assembler von gcc verwenden, warum auch immer. Weiss es jemand, warum?
Vielleicht, weil die Inline-Assembler Strings so was wie printf Formatstrings sind... Bei printf muss man ja auch %d, %u, %X usw. verwenden und speziell bei % muss man %% eingeben:printf("abc.w hat 100%% Recht!\n");
-
abc.w schrieb:
Man lernt seit dem ersten Schuljahr, von links nach rechts zu lesen ...
Genau!
eax = 5 -> mov eax, 5 ebp = esp -> mov ebp, esp eax = eax - ebx -> sub eax, ebx eax = ebx * 1234 -> imul eax, ebx, 1234
-
@merker: das hat auch was
Also wie gesagt, schluss mit dem nutzlosen intel vs. at&t.
-
Möchte nur zum Schluss eine Anmerkung machen, dass es diese Art der Syntax (mit Zuweisungen usw.) in der Praxis wirklich gibt. Bei Blackfin DSPs. Siehe z.B. hier: http://docs.blackfin.uclinux.org/doku.php?id=simple_hello_world_application_example_asm