Aus RN-Wissen.de
Version vom 13. Februar 2007, 11:02 Uhr von SprinterSB_alt (Diskussion | Beiträge) (Assembler dumpt Assembler)

Wechseln zu: Navigation, Suche
LiFePO4 Speicher Test

Aus den unterschiedlichsten Gründen kann es wünschenswert sein, den Assembler-Code zu sehen, den avr-gcc (oder gcc im Allgemeinen) aus einer C/C++-Quelle erzeugt. Die Ausgabe von gcc geschieht immer in Form von Assembler-Code als ASCII-Datei. Ohne die Angabe spezieller Optionen ist diese Datei jedoch nur temporär und wird nach Beenden der von gcc aufgerufenen Programme cc1 (Compiler), as (Assembler) und ld (Linker) wieder gelöscht.

Ein Weg, an den zu einer C-Quelle gehörenden Assembler-Code zu kommen ist also, diese temporäre Datei zu erhalten. Dazu gibt man die gcc-Option -save-temps an oder man lässt gcc lediglich assemblieren, was mit der Option -S geschieht.

Alternativ dazu kann man mit objcopy ein Disassemble der erzeugten Objekte erzeugen.

Ja nach dem, welchen dieser Möglichkeiten man wählt, enthält die Assembler-Ausgabe unterschiedliche Informationen, Darstellungen und Erläuterungen. Während die Assembler-Ausgabe von gcc direkt als Eingabe für den GNU-Assembler verwendet werden kann, sind die Disassembles und Assembler-Dumps des Assemblers für diesen Zweck nicht verwendbar.

Zum besseren Verständnis der folgenden Abschnitte ist es hilfreich, den Ablauf der Codegenerierung bei gcc zu kennen.

C-Quelle

Als C-Beispiel dient eine kleine Funktion, die einem Foren-Beitrag zu selbigem Thema entstammt. Die Assembler-Dumps sind jedoch nur Snips der kleinen Funktion foo, um die es hier gehen soll. Für komplexere Funktionen oder andere gcc-Targets (etwa arm7-gcc) ist die Vorgehensweise analog. Die main-Funktion wird nur gebraucht, damit für das letzte Beispiel ein elf erzeugt werden kann, da hier gegen main gelinkt wird.

unsigned char command_rc5; 
unsigned char Ir_data_tmp; 

void foo()
{
   uint8_t cmd = command_rc5 & ~(1<<7);

   if (Ir_data_tmp & (1<<5))       
      cmd |= (1<<7);
   
   command_rc5 = cmd;   
} 

int main main()
{
	return 0;
}

Compiler dumpt Assembler

kein Assemblieren

Die Arbeit von gcc endet mit Schreiben der Assembler-Ausgabe. Es wird also auch kein Object (*.o) erzeugt.

Kommando:

> gcc -S -fverbose-asm ...

Ausgabe (hier für avr-gcc):

.global   foo
        .type   foo, @function
foo:
/* prologue: frame size=0 */
/* prologue end (size=0) */
        lds r25,command_rc5      ;  cmd, command_rc5
        andi r25,lo8(127)        ;  cmd,
        lds r24,Ir_data_tmp      ;  Ir_data_tmp, Ir_data_tmp
        sbrc r24,5       ;  Ir_data_tmp,
        ori r25,lo8(-128)        ;  cmd,
.L2:
        sts command_rc5,r25      ;  command_rc5, cmd
/* epilogue: frame size=0 */
        ret
/* epilogue end (size=1) */
/* function foo size 11 (10) */
        .size   foo, .-foo

mit Assemblieren

Um die temporäre Assembler-Datei auch zu erhalten wenn Assembliert wird, dient folgender Aufruf, der auch die Ausgabe des Precompilers (*.i) erhält. Die Option -fverbose-asm reichert die Ausgabe mit zusätzlichen Informationen an.

> gcc -c -save-temps -fverbose-asm ...

Alternativ kann man das Erzeugen des Objects in zwei Schritte aufteilen: Compileren *.c → *.s) und Assemblieren (*.s → *.o). Insgesamt hat man dann also das gleiche Resultat wie bei -c.

> gcc -S foo.c -fverbose-asm ...
> gcc -c foo.s ...

Die Ausgabe ist dann wie im vorherigen Abschnitt, nur daß jetzt auch assembliert wird.

Bei diesem Ansatz ist es günstig, ohne Debug-Informationen zu übersetzen, da ansonsten die Assembler-Ausgabe unleserlicher und deutlich größer wird.

Assembler dumpt Assembler

Kommando:

> gcc -Wa,-alhd=dump.s ...

Die Option -Wa bewirkt, daß die Zeichen nach dem Komma mit an die Assembler-Kommandozeile übergeben werden. Der Assembler wird also zusätzlich mit der Option -alhd=dump.s gestartet, was das Schreiben der Ausgabe bewirkt.

Ausgabe:

  18                      .global foo
  20                    foo:
  21                    .LFB2:
  22                    .LM1:
  23                    /* prologue: frame size=0 */
  24                    /* prologue end (size=0) */
  25                    .LM2:
  26 0000 9091 0000             lds r25,command_rc5
  27 0004 9F77                  andi r25,lo8(127)
  28                    .LM3:
  29 0006 8091 0000             lds r24,Ir_data_tmp
  30 000a 85FD                  sbrc r24,5
  31                    .LM4:
  32 000c 9068                  ori r25,lo8(-128)
  33                    .L2:
  34                    .LM5:
  35 000e 9093 0000             sts command_rc5,r25
  36                    /* epilogue: frame size=0 */
  37 0012 0895                  ret
  38                    /* epilogue end (size=1) */
  39                    /* function foo size 11 (10) */

objdump erzeugt Disassemble

aus Object (nicht loakatiert)

Damit die Quellschnippsel im Dump angezeigt werden muss das Object mit Debug-Informationen erzeugt worden sein, und die Quelle muss noch vorhanden sein.

Kommando:

> avr-objdump -d -S ...

Ausgabe:

00000000 <foo>:
uint8_t command_rc5, Ir_data_tmp; 

void foo()
{
   uint8_t cmd = command_rc5 & ~(1<<7);
   0:   90 91 00 00     lds     r25, 0x0000
   4:   9f 77           andi    r25, 0x7F       ; 127

   if (Ir_data_tmp & (1<<5))       
   6:   80 91 00 00     lds     r24, 0x0000
   a:   85 fd           sbrc    r24, 5
      cmd |= (1<<7);
   c:   90 68           ori     r25, 0x80       ; 128
   
   command_rc5 = cmd;   
   e:   90 93 00 00     sts     0x0000, r25
  12:   08 95           ret

aus elf (lokatiert)

Kommando:

> avr-objdump -h -S ...

Ausgabe:

0000005c <foo>:
uint8_t command_rc5, Ir_data_tmp; 

void foo()
{
   uint8_t cmd = command_rc5 & ~(1<<7);
  5c:   90 91 60 00     lds     r25, 0x0060
  60:   9f 77           andi    r25, 0x7F       ; 127

   if (Ir_data_tmp & (1<<5))       
  62:   80 91 61 00     lds     r24, 0x0061
  66:   85 fd           sbrc    r24, 5
      cmd |= (1<<7);
  68:   90 68           ori     r25, 0x80       ; 128
   
   command_rc5 = cmd;   
  6a:   90 93 60 00     sts     0x0060, r25
  6e:   08 95           ret

aus ihex (lokatiert)

Intel-HEX ist ein recht "dummes" Format, es speichert weder Section-Informationen noch Symbole, Debug-Informationen oder Quellbezüge. Dementsprechend muss man wissen, wo man nach was zu suchen hat im disassemblierten Spaghetti-Code. Zudem kann objdump nicht wissen, ob eine Sequqnz als Befehl oder als Datum zu interpretieren ist und hat keine Alignment-Info, was zu verwirrenden Disassembles führen kann, wenn objcopy z.B. mitten in einem 4-Byte-Befehl aufsetzt.

Da dieses Format auch nicht weiß, für welche Maschine es codiert, muss man dies wissen und angeben. Hier wurde der Code für einen ATmega8 erzeugt, welcher zur Gruppe avr4 gehört.

Kommando:

> avr-objdump -D -mavr4 ...

Ausgabe:

     file format ihex

Disassembly of section .sec1:

00000000 <.sec1>:
...
  5c:	90 91 60 00 	lds	r25, 0x0060
  60:	9f 77       	andi	r25, 0x7F	; 127
  62:	80 91 61 00 	lds	r24, 0x0061
  66:	85 fd       	sbrc	r24, 5
  68:	90 68       	ori	r25, 0x80	; 128
  6a:	90 93 60 00 	sts	0x0060, r25
  6e:	08 95       	ret
  ...

Siehe auch


LiFePO4 Speicher Test