.top
mov di,num1+digits-1
mov si,num2+digits-1
mov cx, digts
mov bp, num2
dec dword [term]
jz .done
mov di,num2+digits-1
mov si,num1+digits-1
mov cx,digits
call AddNumberBefehle wie mov, jz oder call werden vom Prozessor verarbeitet.
Hochsprache -> Algorithmen optimieren -> Compiler-Optimierungen -> Assembler anpassen
Unterscheidet in verschiedene Assembler Arten/Syntaxe unter x86 z.B.: AT&T und Intel Asm
#include <stdio.h>
#define MSG "Hello World"
int main(void)
{
//Hello world
printf(MSG);
printf(" and bye\n");
return 0;
}Normales kompilieren mit gcc: gcc -Wall example.c -o example
Anzeige der Temporären Dateien: gcc -Wall -save-temps example.c -o example
Die .i Datei zeigt die Schritte des Preprozessing. Preprozessing Datei
Also ersetzt Macros und entfernt Kommentare. Einfügen von Libarys.
Die .s Datei zeigt den generierten Assembler Code an. Assembler Datei
Die .o Datei zeigt Binäre Maschinensprache an. Danach muss dieser noch gelinkt werden. Maschinensprache Datei
Es entsteht ein fertiges Programm. Programm
Nur Assembler Output: gcc -S example.c -o example.s
Objekt File erstellen: gcc -S -c example.s -o example.o
Programm linken: gcc example.o -o example
+--------------------------------------------------------------------+
| |
| +------------------------------------------------------------+ |
| | Control Unit | |
| +----+--------------------------------------------------+----+ |
| ^ +---------+ +----------+ | |
| | | | | Clock | | |
| | | ALU | +----------+ | |
| | | | | |
| | +-----+---+ | |
| | ^ | |
| | v | |
| | +-------+------+ | |
| | | | | |
| | | Register | | |
| | | | | |
| | +-------+------+ | |
| | ^ | |
| | | | |
| | v v |
| +----+-------+ +------+--------+ +------+----+ |
| | | | | | | |
| | Input +---->+ Speicher +------------>+ Output | |
| | | | | | | |
| +------------+ +---------------+ +-----------+ |
| |
+--------------------------------------------------------------------+Ein Programm, was sich selbst beendet:
movl $0, %ebx
movl $1, %eax
int 0x80ebx und eax sind Register. Dort werden Variabeln reingepackt.
Das int $0x80 sagt dem Linux Kernel, es soll den Syscall der in %eax steht ausführen. In diesem Fall der erste, welcher für Schließen steht.
+---------------+
0x00 | |
+---------------+
... | |
+---------------+
0x10 | |
+-------+-------+
0x11 | Dword | Dword |
+-------+-------+
| LWord |
+---------------+
| |
+---------------+mov (%ebx), %eax
1 Zeile: lädt Inhalt aus der Adresse (%ebx) in Register %eax.
esi = für String Operationen
movb $0, %eax
movb -> move 8 bit
movvw -> move 16 bit
movl -> move 32 bit
movq -> move 64 bit
Speicher in der CPU, dient als Zwischenspeicher. Ist zwischen Ram und Registern zu sehen.
esp -> Stak Pointer
Verschiebt eax auf den Stack:
movq $0, %eax
pushq %eaxStack Pointer wächst, zeigt auf die letzte Zahl im Stack.
Lesen vom Stack und in %eax reinschreiben:
popq %eaxMain Prozedur:
main:
...
call myporc
implicit:
...
myporc:
...
reteip -> Instruktion Pointer
+---------------+
eip +---->+ .... |
+---------------+
| call myproc |
+---------------+
| ... |
+---------------+
| myproc |
+---------------+
| ret |
+---------------+
| |
+---------------+
| |
+---------------+
esp +---->+ |
+---------------+Instrution Pointer geht nach unten. Der Stack Pointer geht nach oben. Der Instruktion Pointer soll die Funktion myproc aufrufen. Bevor sie das tut, wird die Adresse+1 in den Stack geschrieben (implicit). Um nachher wieder, zurück zu kehren.
+---------------+
| .... |
+---------------+
| call myproc |
+---------------+
| ... |
+---------------+
eip +---->+ myproc |
+---------------+
| ret |
+---------------+
| |
+---------------+
| |
+---------------+
esp +---->+ implicit |
+---------------+Am Ende der Prozedur trifft der Instruktion Pointer (eip) auf ret, das heißt, er soll da weiter machen, wo er zuvor aufgehört hat. Wert implicit wird in vom Stack gepopt. Und in eip geschrieben.
Damit geht er wieder nach oben.
+---------------+
| .... |
+---------------+
| call myproc |
+---------------+
eip +---->+ ... |
+---------------+
| myproc |
+---------------+
| ret |
+---------------+
| |
+---------------+
| |
+---------------+
esp +---->+ |
+---------------+Wichtig ist, dass hier jetzt richtig terminiert wird.
.text
.data
.global main
main:
movl $4, %eax
movl $1, %ebx
movl $msg, %ecx
movl $len, %edx
int $0x80
movl $0, %ebx
movl $1, %eax
int $0x80
msg:
.ascii "Hello World."
len = . - msg.text, .data und .global main sind Sections.
Wichtig erstmal ist .global main dieses sagt, das wir ab main starten wollen.
Mit int $0x80 wird ein Syscall aufgerufen, die Parameter für diesen sind in den Registern:
eax -> Syscall (4 für write)
ebx -> Output (1 für stout also Commandline)
ecx -> Text (msg)
edx -> Länge der Nachricht (len)
Um eine Ausgabe zu erzeugen, brauchen wir den vierten Syscall, darum wird die 4 in eax gepackt.
Kompilieren:
gcc -c hello.s -o hello.o
gcc -no-pie hello.o -o hello.text
.data
.global main
main:
movl $4, %eax
movl $1, %ebx
movl $one, %ecx
movl $onelen, %edx
int $0x80
movl $0, %ebx
movl $1, %eax
int $0x80
one:
.ascii "1"
onelen = . - onecmp $3, %esi # vergleich 3 mit esi
jne notequal # Spring zu notequal wenn ungleichKompletter Assembler Quellcode Datei
Mit cmp also Compare lassen sich Dinge vergleichen. Mit Jumps lässt sich dann an die nächste stelle springen.
jmp -> Jump
je -> Equal
jne -> Not Equal
jg -> Greater
jge -> Greater or Equal
jl -> Less
jle -> Less or Equal
ja -> Above, ignoriert Vorzeichen
jae -> Above or Equal
jb -> Below
jbe -> Below or Equal
jo -> Overflow (Überlauf von Plus zu Minus)
jno -> No Overflow
jz -> Zero
jnz -> Not Zero
js -> Signed
jns -> Not Signed
add $3, %esi # Zahlen auf Register addieren
add %eax, %esi # eax auf esi addieren sub %eax, %esi
Die CPU kann eigentlich kein subtrahieren, darum wird ein kleiner Trick angewendet.
neg %eax
add %eax, %esieax negiert und dann auf esi addiert.
Der mul Befehl multipliziert immer aus eax. Das Ergebnis wird dann wieder in eax hineingeschrieben. (Wenn zu groß dann noch in edx)
movl $1, %eax # 1 in eax
mul %esi # multipliziert eax und esi
movl %eax, %esi # zurück movenErmöglicht multiplizieren mit
imul $3, %esi # 3 multiplizieren mit esi
Wie bei der Multiplikation wird immer mit eax gerechnet. Das Register edx wird bei der Berechnung mit verwendet und muss vorher “gesäubert” werden.
eax -> Quotient
edx -> Rest
movl $0, %edx
div %esi # Division %edx:%eax / %esi
movl %eax, %esiSchleife bis 5. Vergleich mit ecx, bis dieser gleich Null ist. ecx wird dabei um ein verringert.
movl $5, %ecx
Schleife:
add $1, %esi
loop Schleife loop -> Schleife, wird ausgeführt, bis ecx = 0, verringert ecx immer um 1
loope -> equals (ob das Zero Bit auf Null gesetzt wurde, also die letzte Aufruf Null ergeben hat.) (ecx muss größer Null sein.)
loopz -> genauso wie loope
UND auf zwei Registern:
movl $0xFFFF, %esi
movl $0x0, %ecx
andl %ecx, %esx # Gespeichert in esxorl -> OR
xorl -> XOR
Schift Links um eins:
movl $1, %esi #000...0001 in %esi
shll $1, %esi #000...0010 in %esishrl -> Rechts (das l für dobbel word.)
Bei Prozeduren, die nicht der Reihe aufgerufen werden, werden Stack Frames gebraucht.
main:
...
call poc2
proc1:
...
proc2:
call proc1
ret esp-->+---------------+
| vars proc1 |
frame pointer-->+---------------+
| return addr |
+---------------+
| params proc1 |
+---------------+
| Vars proc2 |
+---------------+
| return addr |
+---------------+
| params proc2 |
+---------------+frame pointer zeigt die Adresse auf die zurück gesprungen werden soll.
Der Frame besteht immer aus den 3 Angaben von variabeln, return adresse und Parametern.
.file "example.c" # Debugger kann nachvollziehen aus welcher Datei das kommt.
.text # Könnte Code beinhalten.
.section .rodata # Read Only Data
.LC0: #
.string "Hello World" # Nur lesbar (Darf nicht verändert werden.)
.LC1:
.string " and bye"
.text
.globl main # .globl wird das Programm aufgerufen
.type main, @function # Funktion main die Aufgerufen werden kann.
main:
.LFB0: # Local Function Begin (Nummer)
.cfi_startproc # Starten einer Prozedur (checken das ein Frame Pointer vorhanden ist)
pushq %rbp
.cfi_def_cfa_offset 16 # Canonical Frame Address Pointer CFA (zeigt auf Frame vor dem akktuellen Frame)
.cfi_offset 6, -16 # Frame Pointer
movq %rsp, %rbp
.cfi_def_cfa_register 6 # CFA = Register Nummer 6
leaq .LC0(%rip), %rdi # LC0 Auslesen und in rdi verschieben
movl $0, %eax
call printf@PLT # printf aufrufen aus procedure linkage table (PLT)
leaq .LC1(%rip), %rdi # LC1 in rdi
call puts@PLT # Put aufrufen (weil Output schon offen ist, durch printf)
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc # Debbuger info
.LFE0: # Local Function End (Nummer)
.size main, .-main
.ident "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
.section .note.GNU-stack,"",@progbits #include <stdio.h>
int main (void)
{
int num = 24, output;
asm("movl %1, %%ebx;" // Prozentzeichen doppelt um zugriff zu erhalten
"movl %%ebx, %0 ;"
: "=r" (output) // OUTPUT
: "r" (num) // INPUT
:"%ebx" // USED REGISTERS
);
printf("%d\n", output);
return 0;
}