Disassembly-Analyse

Im Folgenden werden wir den Maschinencode betrachten, den der Compiler aus Ihrem Programm erzeugt hat.

  1. Erstellen Sie eine Datei gauss.c mit folgendem Inhalt:

    #include <stdio.h>
    
    
    int main(int argc, char **argv) {
      int sum = 0;
      for (int i = 1; i <= 100; i++)
        sum += i;
      printf("sum = %d\n", sum);
      return 0;
    }
    
  2. Kompilieren Sie das Programm wie folgt: gcc -o gauss gauss.c

  3. Verwenden Sie nun den Befehl objdump, um den Maschinencode der kompilierten Datei in lesbarer Form anzuzeigen: objdump -d -M intel gauss | less

    Sie können die Ansicht von less mit der Taste q beenden. Die Repräsentation der Ausgabe von objdump ist:

    <address>: instruction_bytes instruction_mnemonic
    

    Die lesbare Repräsentation des Programms findet sich in instruction_mnemonic.

  4. Suchen Sie in der Ausgabe von objdump (der sogenannten Disassembly) nach der Funktion main. Können Sie die Schleife aus der Hochsprache im Assemblercode lokalisieren?

    Lösung

    Die Funktion main sieht wie folgt aus; die Schleife befindet sich zwischen den Adressen 0x670 und 0x67e. Zu erkennen ist diese am Sprung zum Compare und dem bedingten Sprung zurück. Das Programm ist einfacher zu verstehen, wenn man sich klar macht, dass die Variable i an der Adresse rbp-0x4 steht und die Variable sum an der Adresse rbp-0x8.

    000000000000064a <main>:
      // ...
      659: c745f800000000  mov DWORD PTR [rbp-0x8],0x0  // sum = 0
      660: c745fc00000000  mov DWORD PTR [rbp-0x4],0x0  // i = 0
      667: c745fc01000000  mov DWORD PTR [rbp-0x4],0x1  // i = 1
      66e: eb0a            jmp 67a <main+0x30>          // Sprung zu Vergleich
      670: 8b45fc          mov eax,DWORD PTR [rbp-0x4]  // eax = i
      673: 0145f8          add DWORD PTR [rbp-0x8],eax  // sum = sum + eax
      676: 8345fc01        add DWORD PTR [rbp-0x4],0x1  // i = i + 1
      67a: 837dfc64        cmp DWORD PTR [rbp-0x4],0x64 // Vergleich i, 100
      67e: 7ef0            jle 670 <main+0x26>          // Sprung, wenn i<=100
      // ...
      69c: c3              ret
    
  5. Kompilieren Sie Ihr Programm erneut unter der Verwendung der Optionen -O0, -O1 oder -O2. Wie verändert sich die Disassembly?

    Lösung

    Bei höheren Optimierungsstufen (GCC unterstüzt die Stufen bis -O3) fällt auf, dass die Schleife nicht mehr existert; sie wurde zur Compile-Zeit berechnet und im Programm durch eine Konstante ersetzt.

    Als Vorgriff: Zudem werden nicht alle Variablen ständig auf dem Stack gespeichert und wieder geladen.