Skip to content

Latest commit

 

History

History

ASM-GUENTHER

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

Rechnerarchitekturen/hardwarenahe Programmierung

Inhaltsverzeichnis

Java und Make

  • mit javac kompiliert man eine .java-Datei zu Bytecode
  • mit java klassenname kann man ausführen
  • make -n macht einen Dry-Run

Beispiel-Makefile:

SOURCE=hello.java

OUTPUT=hello

COMPILER=javac

JRE=java

all: $(OUTPUT) # Syntax ziel: vorbedingungen

$(OUTPUT): $(SOURCE) Makefile
        $(COMPILER) $< # $< heißt, der erste Teil wird verwendet

clean:
        rm -rvf ($OUTPUT).class

run: $(OUTPUT)
        $(JRE) $(OUTPUT)

C und Make

  • gcc -o dateiname source.c kompiliert eine C Quelldatei in eine Binary
  • make misst am Timestamp einer Datei, ob sie ggf. neu kompiliert o.ä. werden muss
  • Vorteil von Make: bei ungeänderten Quellcode wird für make run nicht neu kompiliert
  • make -j X mit X Prozessorkernen führt Aufgaben parallel aus

Beispiel-Makefile:

SOURCE=main.c

COMPILER=gcc

C-COMP-PARAM=--std=gnu99 -Wall -Wextra #C99 mit lang exts.

OUTFILE=asdf

all: $(COMPILER-OUTPUT)

$(COMPILER-OUTPUT): $(SOURCE) Makefile
        $(COMPILER) $(C-COMP-PARAM) $(SOURCE) -o $(OUTFILE)

clean:
    rm -rvf $(COMPILER-OUTPUT)

run: $(COMPILER-OUTPUT)
    ./$(COMPILER-OUTPUT)

wichtig: aufgabe1 aufgabe2 # zu beachten: $() gehört nur zu Variablen
        ./messung.sh
        @echo "alles fertig" # @ unterdrückt, dass die Aufgabe geprintet wird

aufgabe1:
    echo "a1 start"
    sleep 5
    echo "a1 end"

aufgabe2:
    echo "a2 start"
    sleep 5
    echo "a2 end"

C++ und Make

Aufgabe: jetzt sollen wir ein Makefile schreiben

CPPSOURCE=main.cpp
CSOURCE=main.c
CPPCOMPILER=g++
CCOMPILER=gcc
CPPARAM=-Wall -Wextra
OUTFILE_CPP=asdf
OUTFILE_C=xyz
CPARAM=--std=gnu99 -Wall -Wextra #C99 mit lang exts.


compile-cpp: $(CPPSOURCE) Makefile
        $(CPPCOMPILER) $(CPPARAM) $(CPPSOURCE) -o $(OUTFILE_CPP)

compile-c: $(CSOURCE) Makefile
        $(CCOMPILER) $(CPARAM) $(CSOURCE) -o $(OUTFILE_C)

compile-all: compile-cpp compile-c

clean-all: clean-c clean-cpp

clean-c:
        rm -rvf $(OUTFILE_C)

clean-cpp:
        rm -rvf $(OUTFILE_CPP)

# run unterstützt kein on-demand-kompilieren

run-all: run-c run-cpp

run-c: $(OUTFILE_C)
        ./$(OUTFILE_C)

run-cpp: $(OUTFILE_CPP)
        ./$(OUTFILE_CPP)

Assembler

Inline Assembler in C

Über die asm-Funktion können Assembler-Befehle direkt in C genutzt werden.

Für GCC ist hier eine Dokumentation verfügbar

int main(){
  asm("mov rax, 7"); // setze den Wert des Register "rax" auf 7
  asm("mov rbx, 35"); // setze den Wert des Register "rbx" auf 35
  asm("add rbx, rax"); // addiere die Register "rax" und "rbx" -> Ergebnis in "rbx"
  return 0;
}

Die Werte in den Registern können mit Hilfe eines Debuggers, wie gdb betrachtet werden.

Einfaches Debugging mit gdb

  1. Mit gdb das zu debuggende compilierte Programm öffnen: gdb ./main.o
  2. Breakpoint setzen: break linenumber
  3. Programm auführen: run [argv]
  4. Programm wird an Breakpoint gestoppt

Zustand des Programmes inspizieren

  • Wert der Register ausgeben: info registers
  • Stepping: step nächste Instruktion (auch in Funktionen springen)
  • Stepping: next nächste Instruktion (nicht in Funktionen springen)

Geteilte Variablen zwischen C und Assembler

long a = 7;
long b = 35;
long c;

asm("mov rax, %1;"
        "mov rbx, %2;"
        "add rbx, rax;"
        "mov %0, rbx;"
        : "=r" (c) /* output operands */
        : "r" (a), "r" (b) /* input operands */
        : "rbx", "rax" /* list of clobbered registers */
);
  • Ein-/Ausgabeoperanden werden durch die Syntax "constraint" ( operand ) definiert
    • der constraint gibt an, in welchem Register gcc die Operanden speichern soll (r steht dabei für ein automatisch gewähltes Register)
    • für Ausgabe-Operanden wird vor dem constraint ein = gesetzt (z.B. "=r")
    • gibt es ausschließlich Eingabe-Operanden, wird dies durch :: definiert
    • operand gibt an, aus/in welchem Wert die Ein-/Ausgabe gelesen/geschrieben werden soll
    • in den asm-Befehlen werden die Operanden durch %index verwendet
  • die clobbered registers geben lediglich an, welche Register von den asm-Befehlen schreibend verwendet werden
    • wird definiert, damit GCC nicht annimmt, dass diese Register am Ende des Assemblerteils noch dieselben Werte haben

Binärschnittstelle (Application Binary Interface)

  • definierte Schnittstelle, die Assembler-Verarbeitung ermöglicht (vgl. Inline-Assembler)
  • ermöglicht direkten Aufruf von Assembler-Routinen
  • Nutzung:
    • gesuchte Funktion extern deklarieren extern unsigned int popcnt(unsigned int a);
    • ABI aufrufen
    • mittels Assembler die gesuchte Funktion zur Compile-Zeit zu einer Object-File verarbeiten: as --64 popcnt.s -o popcnt.o
    • beim Linken wird mit einer impliziten Regel Patternsubstitution verwendet:
%.o: %.s Makefile
        $(ASSEMBLER) $(ASM-PARAMETER) $< -o $@
  • gemäß C Calling Convention muss ein 32-Bit (!!) Assemblerprogramm folgenden Rahmen haben, um in einem C-Programm aufrufbar zu sein:
global myFunc

section .text

myFunc:

push ebp
; vorbereitung
mov ebp, esp ; neuer call frame
; -- dein code --
mov eax, [ebp+8] ; erster Parameter in Ausgaberegister eax (eax ist immer Ausgaberegister!)
; nachbereitung
mov     esp, ebp ;
pop     ebp      ; restore old call frame
ret              ; return

Kompilieren und Linken einer C- und Assembler Datei

as --32 popcnt.s -o popcnt.o
gcc -c --std=gnu99 -m32 main.c -o main.o
gcc -m32 popcnt.o main.o -o main
./main

Mögliche Prüfungsaufgaben

  • Wir kriegen ein Konstrukt aus Make-Targets mit definierten Zeitaufwänden, von denen manche nebenläufig ausgeführt werden.
    • Zu einer gegebenen Anzahl zu verwendender Kerne soll der Gesamtzeitaufwand bestimmt werden.