|
II-INFORMATIK3 WS04
|
Bei der Arbeit mit dem exzellenten Buch von [BRYANT 2003] stellte sich leider erst jetzt heraus, dass der Y86-Simulator in seiner aktuellen Version unter den neueren SuSe-Versionen nicht so funktioniert, wie es für die Vorlesung notwendig wäre. Dies bedeutet, dass die Durcharbeitung der Y86-Prozessorarchitektur nicht so vorgenommen werden kann, wie geplant. Es besteht zwar zur Zeit eine lebhafte email-Korrespondenz mit dem Autor Randy Bryant über das Problem; aber bislang konnte bzgl. des SW-Einsatzes noch keine brauchbaren Ergebnisse erzielt werden. Es ist allerdings zu erwarten, dass das Problem in den naechsten Wochen geloest wird.
Für die aktuell laufende Vorlesung heisst dies aber, dass der ursprüngliche Plan so nicht fortgeführt werden kann. In einem längeren Gespräch der Studenten mit dem Dozenten kristallisierte sich heraus, dass die überwältigende Mehrheit nicht den Javabasierten Ansatz mit dem Tanenbaum Simulator wählen will, sondern lieber mit dem C-basierten Ansatz des Buches [BRYANT 2003] weiterarbeiten will. Dies passt auch besser zu den übrigen systemnahen LVs. Es wurde daher ein neues Programm vereinbart, das den Zugang zur Hardware über einen C-Assembler-basierten Ansatz beibehält und in diesem Kontext Fragen der Hardwarearchitektur behandelt. Wir folgen hier dem Kap.3 von [BRYANT 2003]. Siehe dazu auch [BREY 2003] sowie die offiziellen Dokumente der Firma Intel.
Nachdem der erste Plan gescheitert ist, die Funktionesweise der Hardware durch das Y86-Programmiermodell zu untersuchen, soll nun der Ansatz gewählt werden, dass die IA32-Architektur aus Sicht des IA32-Befehlssatzes untersucht wird. Dabei soll --analog dem Vorgehen von [BRYANT 2003]-- der IA32-Befehlssaatz immer auch von der Ebene eines C-Programms aus betrachtet werden.
C-Assembler-Binaer-ISA
Dies sei hier an einem Beispiel verdeutlicht. Als Ausgangspunkt nehmen wir zwei einfache C-Dateien. Eine main-Datei, die eine Funktion sum() aufruft und eine Datei mit dieser Funktion sum(). Beim Aufruf übergibt die main-Funktion der Funktion sum() zwei Argumente, die addiert werden. Das Ergebnis wird zurück geliefert.
/**************************** * * bsp1_main.c * * idea: simple demo for calling a function * **********************************/ int main(){ int accum = 0; accum = sum(1,3); return 0; }
/**************************** * * bsp1_sum.c * * idea: simple illustration * **********************************/ int sum(int x, int y){ int t; t = x + y; return t; }
Als nächstes kann man diesen c-Kode in IA32-Assemblerkode übersetzen. Dies lässt sich unter Linux mit dem gcc-Compiler bewerkstelligen, wenn man ihn mit der Option -S aufruft, also
gerd@kant:~/public_html/fh/II-INF3/II-INF3-TH/VL4> gcc -v -S bsp1_main.c bsp1_sum.c
Das Ergebnis sieht dann wie folgt aus:
.file "bsp1_main.c" .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp subl $8, %esp andl $-16, %esp movl $0, %eax subl %eax, %esp movl $0, -4(%ebp) subl $8, %esp pushl $3 pushl $1 call sum addl $16, %esp movl %eax, -4(%ebp) movl $0, %eax leave ret .size main, .-main .section .note.GNU-stack,"",@progbits .ident "GCC: (GNU) 3.3.3 (SuSE Linux)"
.file "bsp1_sum.c" .text .globl sum .type sum, @function sum: pushl %ebp movl %esp, %ebp subl $4, %esp movl 12(%ebp), %eax addl 8(%ebp), %eax movl %eax, -4(%ebp) movl -4(%ebp), %eax leave ret .size sum, .-sum .section .note.GNU-stack,"",@progbits .ident "GCC: (GNU) 3.3.3 (SuSE Linux)"
Man kann zwar erkennen, dass es sich irgendwie um Assemblerkode handelt, da die IA32-Befehle aber noch nicht explizit behandelt wurden, ist dieser Kode an dieser Stelle noch unverständlich. Untersuchen wir es weiter.
Assemblerkode ist ja, wie bekannt, noch nicht jenes Format, was dann tatsaechlich von einem Mikroprozessor verstanden werden kann. Die Welt des Mikroprozessors besteht nur aus 1en und 0en, dem Binärkode. Es muss also der Assemblerkode noch weiter übersetzt werden, bis er als eine Folge von 1en und 0en vorliegt. Dies kann man mit dem Compiler gcc erreichen, wenn man garkeine Option angibt. Dann werden die Dateien, die man als Argumente übergibt, in Binärkode übersetzt und zugleich noch mittels des Linkers automatich zu einem einzigen ausführbaren Kode zusammengebunden, also:
gerd@kant:~/public_html/fh/II-INF3/II-INF3-TH/VL4> gcc -o bsp1 bsp1_main.s bsp1_sum.s
Als Ergebnis liegt die ausführbare Datei "bsp1" vor (der Name wurde mit der Option -o erzwungen). Dieser Kode besteht jetzt nur noch aus einer Folge von 1en und 0en, die ein IA32-Prozessor nun direkt lesen könnte. Um das Format dieses Kodes einigermassen lesbar zu machen, gibt es unter Linux den Befehl objdump:
gerd@kant:~/public_html/fh/II-INF3/II-INF3-TH/VL4> objdump -d bsp1
bsp1: file format elf32-i386 Disassembly of section .init: 08048264 <_init>: 8048264: 55 push %ebp 8048265: 89 e5 mov %esp,%ebp 8048267: 83 ec 08 sub $0x8,%esp 804826a: e8 55 00 00 00 call 80482c4 <call_gmon_start> 804826f: e8 bc 00 00 00 call 8048330 <frame_dummy> 8048274: e8 f7 01 00 00 call 8048470 <__do_global_ctors_aux> 8048279: c9 leave 804827a: c3 ret Disassembly of section .plt: 0804827c <.plt>: 804827c: ff 35 a8 95 04 08 pushl 0x80495a8 8048282: ff 25 ac 95 04 08 jmp *0x80495ac 8048288: 00 00 add %al,(%eax) 804828a: 00 00 add %al,(%eax) 804828c: ff 25 b0 95 04 08 jmp *0x80495b0 8048292: 68 00 00 00 00 push $0x0 8048297: e9 e0 ff ff ff jmp 804827c <_init+0x18> Disassembly of section .text: 080482a0 <_start>: 80482a0: 31 ed xor %ebp,%ebp 80482a2: 5e pop %esi 80482a3: 89 e1 mov %esp,%ecx 80482a5: 83 e4 f0 and $0xfffffff0,%esp 80482a8: 50 push %eax 80482a9: 54 push %esp 80482aa: 52 push %edx 80482ab: 68 a0 83 04 08 push $0x80483a0 80482b0: 68 10 84 04 08 push $0x8048410 80482b5: 51 push %ecx 80482b6: 56 push %esi 80482b7: 68 5c 83 04 08 push $0x804835c 80482bc: e8 cb ff ff ff call 804828c <_init+0x28> 80482c1: f4 hlt 80482c2: 90 nop 80482c3: 90 nop 080482c4 <call_gmon_start>: 80482c4: 55 push %ebp 80482c5: 89 e5 mov %esp,%ebp 80482c7: 53 push %ebx 80482c8: e8 00 00 00 00 call 80482cd <call_gmon_start+0x9> 80482cd: 5b pop %ebx 80482ce: 81 c3 d7 12 00 00 add $0x12d7,%ebx 80482d4: 52 push %edx 80482d5: 8b 83 10 00 00 00 mov 0x10(%ebx),%eax 80482db: 85 c0 test %eax,%eax 80482dd: 74 02 je 80482e1 <call_gmon_start+0x1d> 80482df: ff d0 call *%eax 80482e1: 58 pop %eax 80482e2: 5b pop %ebx 80482e3: c9 leave 80482e4: c3 ret 80482e5: 90 nop 80482e6: 90 nop 80482e7: 90 nop 80482e8: 90 nop 80482e9: 90 nop 80482ea: 90 nop 80482eb: 90 nop 80482ec: 90 nop 80482ed: 90 nop 80482ee: 90 nop 80482ef: 90 nop 080482f0 <__do_global_dtors_aux>: 80482f0: 55 push %ebp 80482f1: 89 e5 mov %esp,%ebp 80482f3: 50 push %eax 80482f4: 50 push %eax 80482f5: 80 3d b8 95 04 08 00 cmpb $0x0,0x80495b8 80482fc: 75 2e jne 804832c <__do_global_dtors_aux+0x3c> 80482fe: a1 c0 94 04 08 mov 0x80494c0,%eax 8048303: 8b 10 mov (%eax),%edx 8048305: 85 d2 test %edx,%edx 8048307: 74 1c je 8048325 <__do_global_dtors_aux+0x35> 8048309: 8d b4 26 00 00 00 00 lea 0x0(%esi),%esi 8048310: 83 c0 04 add $0x4,%eax 8048313: a3 c0 94 04 08 mov %eax,0x80494c0 8048318: ff d2 call *%edx 804831a: a1 c0 94 04 08 mov 0x80494c0,%eax 804831f: 8b 10 mov (%eax),%edx 8048321: 85 d2 test %edx,%edx 8048323: 75 eb jne 8048310 <__do_global_dtors_aux+0x20> 8048325: c6 05 b8 95 04 08 01 movb $0x1,0x80495b8 804832c: c9 leave 804832d: c3 ret 804832e: 89 f6 mov %esi,%esi 08048330 <frame_dummy>: 8048330: 55 push %ebp 8048331: 89 e5 mov %esp,%ebp 8048333: 51 push %ecx 8048334: 51 push %ecx 8048335: 8b 15 a0 95 04 08 mov 0x80495a0,%edx 804833b: 85 d2 test %edx,%edx 804833d: 74 19 je 8048358 <frame_dummy+0x28> 804833f: b8 00 00 00 00 mov $0x0,%eax 8048344: 85 c0 test %eax,%eax 8048346: 74 10 je 8048358 <frame_dummy+0x28> 8048348: 83 ec 0c sub $0xc,%esp 804834b: 68 a0 95 04 08 push $0x80495a0 8048350: e8 ab 7c fb f7 call 0 <_init-0x8048264> 8048355: 83 c4 10 add $0x10,%esp 8048358: c9 leave 8048359: c3 ret 804835a: 90 nop 804835b: 90 nop 0804835c <main>: 804835c: 55 push %ebp 804835d: 89 e5 mov %esp,%ebp 804835f: 83 ec 08 sub $0x8,%esp 8048362: 83 e4 f0 and $0xfffffff0,%esp 8048365: b8 00 00 00 00 mov $0x0,%eax 804836a: 29 c4 sub %eax,%esp 804836c: c7 45 fc 00 00 00 00 movl $0x0,0xfffffffc(%ebp) 8048373: 83 ec 08 sub $0x8,%esp 8048376: 6a 03 push $0x3 8048378: 6a 01 push $0x1 804837a: e8 0d 00 00 00 call 804838c <sum> 804837f: 83 c4 10 add $0x10,%esp 8048382: 89 45 fc mov %eax,0xfffffffc(%ebp) 8048385: b8 00 00 00 00 mov $0x0,%eax 804838a: c9 leave 804838b: c3 ret 0804838c <sum>: 804838c: 55 push %ebp 804838d: 89 e5 mov %esp,%ebp 804838f: 83 ec 04 sub $0x4,%esp 8048392: 8b 45 0c mov 0xc(%ebp),%eax 8048395: 03 45 08 add 0x8(%ebp),%eax 8048398: 89 45 fc mov %eax,0xfffffffc(%ebp) 804839b: 8b 45 fc mov 0xfffffffc(%ebp),%eax 804839e: c9 leave 804839f: c3 ret 080483a0 <__libc_csu_fini>: 80483a0: 55 push %ebp 80483a1: 89 e5 mov %esp,%ebp 80483a3: 83 ec 18 sub $0x18,%esp 80483a6: 89 5d f4 mov %ebx,0xfffffff4(%ebp) 80483a9: e8 ba 00 00 00 call 8048468 <__i686.get_pc_thunk.bx> 80483ae: 81 c3 f6 11 00 00 add $0x11f6,%ebx 80483b4: 89 7d fc mov %edi,0xfffffffc(%ebp) 80483b7: 8d 83 14 ff ff ff lea 0xffffff14(%ebx),%eax 80483bd: 8d bb 14 ff ff ff lea 0xffffff14(%ebx),%edi 80483c3: 89 75 f8 mov %esi,0xfffffff8(%ebp) 80483c6: 29 f8 sub %edi,%eax 80483c8: c1 f8 02 sar $0x2,%eax 80483cb: 85 c0 test %eax,%eax 80483cd: 8d 70 ff lea 0xffffffff(%eax),%esi 80483d0: 75 12 jne 80483e4 <__libc_csu_fini+0x44> 80483d2: e8 bd 00 00 00 call 8048494 <_fini> 80483d7: 8b 5d f4 mov 0xfffffff4(%ebp),%ebx 80483da: 8b 75 f8 mov 0xfffffff8(%ebp),%esi 80483dd: 8b 7d fc mov 0xfffffffc(%ebp),%edi 80483e0: 89 ec mov %ebp,%esp 80483e2: 5d pop %ebp 80483e3: c3 ret 80483e4: ff 14 b7 call *(%edi,%esi,4) 80483e7: 89 f0 mov %esi,%eax 80483e9: 4e dec %esi 80483ea: 85 c0 test %eax,%eax 80483ec: 75 f6 jne 80483e4 <__libc_csu_fini+0x44> 80483ee: 89 f6 mov %esi,%esi 80483f0: e8 9f 00 00 00 call 8048494 <_fini> 80483f5: 8b 5d f4 mov 0xfffffff4(%ebp),%ebx 80483f8: 8b 75 f8 mov 0xfffffff8(%ebp),%esi 80483fb: 8b 7d fc mov 0xfffffffc(%ebp),%edi 80483fe: 89 ec mov %ebp,%esp 8048400: 5d pop %ebp 8048401: c3 ret 8048402: 8d b4 26 00 00 00 00 lea 0x0(%esi),%esi 8048409: 8d bc 27 00 00 00 00 lea 0x0(%edi),%edi 08048410 <__libc_csu_init>: 8048410: 55 push %ebp 8048411: 89 e5 mov %esp,%ebp 8048413: 83 ec 18 sub $0x18,%esp 8048416: 89 5d f4 mov %ebx,0xfffffff4(%ebp) 8048419: 89 75 f8 mov %esi,0xfffffff8(%ebp) 804841c: 31 f6 xor %esi,%esi 804841e: e8 45 00 00 00 call 8048468 <__i686.get_pc_thunk.bx> 8048423: 81 c3 81 11 00 00 add $0x1181,%ebx 8048429: 89 7d fc mov %edi,0xfffffffc(%ebp) 804842c: e8 33 fe ff ff call 8048264 <_init> 8048431: 8d 93 14 ff ff ff lea 0xffffff14(%ebx),%edx 8048437: 8d 83 14 ff ff ff lea 0xffffff14(%ebx),%eax 804843d: 29 c2 sub %eax,%edx 804843f: c1 fa 02 sar $0x2,%edx 8048442: 39 d6 cmp %edx,%esi 8048444: 73 15 jae 804845b <__libc_csu_init+0x4b> 8048446: 89 45 f0 mov %eax,0xfffffff0(%ebp) 8048449: 89 d7 mov %edx,%edi 804844b: 90 nop 804844c: 8d 74 26 00 lea 0x0(%esi),%esi 8048450: ff 14 b0 call *(%eax,%esi,4) 8048453: 46 inc %esi 8048454: 8b 45 f0 mov 0xfffffff0(%ebp),%eax 8048457: 39 fe cmp %edi,%esi 8048459: 72 f5 jb 8048450 <__libc_csu_init+0x40> 804845b: 8b 5d f4 mov 0xfffffff4(%ebp),%ebx 804845e: 8b 75 f8 mov 0xfffffff8(%ebp),%esi 8048461: 8b 7d fc mov 0xfffffffc(%ebp),%edi 8048464: 89 ec mov %ebp,%esp 8048466: 5d pop %ebp 8048467: c3 ret 08048468 <__i686.get_pc_thunk.bx>: 8048468: 8b 1c 24 mov (%esp),%ebx 804846b: c3 ret 804846c: 90 nop 804846d: 90 nop 804846e: 90 nop 804846f: 90 nop 08048470 <__do_global_ctors_aux>: 8048470: 55 push %ebp 8048471: 89 e5 mov %esp,%ebp 8048473: 53 push %ebx 8048474: 52 push %edx 8048475: bb 90 95 04 08 mov $0x8049590,%ebx 804847a: a1 90 95 04 08 mov 0x8049590,%eax 804847f: 83 f8 ff cmp $0xffffffff,%eax 8048482: 74 0c je 8048490 <__do_global_ctors_aux+0x20> 8048484: 83 eb 04 sub $0x4,%ebx 8048487: ff d0 call *%eax 8048489: 8b 03 mov (%ebx),%eax 804848b: 83 f8 ff cmp $0xffffffff,%eax 804848e: 75 f4 jne 8048484 <__do_global_ctors_aux+0x14> 8048490: 58 pop %eax 8048491: 5b pop %ebx 8048492: 5d pop %ebp 8048493: c3 ret Disassembly of section .fini: 08048494 <_fini>: 8048494: 55 push %ebp 8048495: 89 e5 mov %esp,%ebp 8048497: 53 push %ebx 8048498: e8 00 00 00 00 call 804849d <_fini+0x9> 804849d: 5b pop %ebx 804849e: 81 c3 07 11 00 00 add $0x1107,%ebx 80484a4: 50 push %eax 80484a5: e8 46 fe ff ff call 80482f0 <__do_global_dtors_aux> 80484aa: 59 pop %ecx 80484ab: 5b pop %ebx 80484ac: c9 leave 80484ad: c3 ret
In dieser Darstellung kann man in der linken Spalte absolute Adressen von Speicherzellen erkennen. In der zweiten Sektion findet man Bytefolgen im Hex-Format, die den eigentlichen Objektkodecrepräsentieren. Rechts davon finden sich nochmals die Assemblerbefehle, die diese Bytefolgen kodieren.
In den nächsten Schritten soll erläutert werden, wie man solchen Kode interpretieren kann. Dieses Verständnis soll dabei über den eigentlichen Kode hinausgehen und mittels des Kodes einen Einblick in die zugrundeliegende Hardware vermitteln. Um dies tun zu können benötigt man die Möglichkeit, den jeweiligen Kode in Verbindung mit der Hardware anschauen zu können, d.h. die Register der CPU sowie der Stack-Speicher, der benutzt wird. Diess Werkzeug ist der GNU Debugger gdb.
Der GNU Debuger gdb wird hier kurz eingeführt; anschliessend wird die IA32-Architektur beschrieben.
Eine sehr ausführliche Information zum GNU Debugger --z.T. mit Beispielen-- findet sich unter Linux mit dem Befehl
info gdb
Im folgenden stellen wir nur die Befehle und Optionen vor, die wir für die nächsten Schritte benötigen.
Der GNU Debugger gdb ist ein exzellentes Stück opensource Software verbunden mit einer entsprechenden opensource Dokumentation. Im vollen Umfang unterstützt er die Sprachen C und C++; andere Sprachen nur zum Teil. Der Debugger erlaubt es einem Softwareentwickler, die Abarbeitung eines Programms auf der Hardware in --nahezu-- allen Aspekten zu kontrollieren, um auf diese Weise Fehler aufzuspüren.
Will man ein Programm mit dem Debugger gdb debuggen, dann muss man es zuvor mit der Option -g kompiliert haben. Das soll jetzt mit den beiden kleinen Beispielprogrammen nachgeholt werden:
gerd@kant:~/public_html/fh/II-INF3/II-INF3-TH/VL4> gcc -g -o bsp1 bsp1_main.c bsp1_sum.c
Will man sich die Arbeitsweise eines normalen Programms anschauen, dann reicht es, wenn man beim Aufruf von gdb einfach dieses Namen angibt. Ist das Programm wegen eines Fehlers abgestürzt und es wurde ein sogenannter core dump erzeut, dann kann man diesen mit angeben und es wird gleich die Stelle angezeigt, an der das Programm abgestürzt ist. Wir betrachten hier erst den einfachen Fall ohne core dump:
gerd@kant:~/public_html/fh/II-INF3/II-INF3-TH/VL4> gdb bsp1 GNU gdb 6.1 Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i586-suse-linux"...Using host libthread_db library "/lib/tls/libthread_db.so.1". (gdb)
Den Programmkode kann man mit dem Befehl list [l] anzeigen lassen, dazu einige weitere Optionen:
(gdb) l 3 * bsp1_main.c 4 * 5 * idea: simple demo for calling a function 6 * 7 **********************************/ 8 9 10 int main(){ 11 12 int accum = 0; (gdb) l 13 14 accum = sum(1,3); 15 16 return 0; 17 18 } (gdb) l -16 1 /**************************** 2 * 3 * bsp1_main.c 4 * 5 * idea: simple demo for calling a function 6 * 7 **********************************/ 8 9 10 int main(){ (gdb) l 11 12 int accum = 0; 13 14 accum = sum(1,3); 15 16 return 0; 17 18 } (gdb) l sum 6 * 7 **********************************/ 8 9 10 11 int sum(int x, int y){ 12 13 int t; 14 15 t = x + y; (gdb) l 16 17 return t; 18 19 } (gdb)
Man kann hier sehen, wie man mit l zunächst 10 Zeilen des Quelltextes von main() angezeigt bekommt; mit einem weiteren l die nächsten Zeilen. Ist man bei Zeile 16 angekommen und man gibt l -16 ein, dann dann werden die letzten 16 Zeilen vor dem aktuellen Punkt angezeigt, allerdings momentan nur 10 auf einmal. Gibt man l Funktionsname ein, dann wird die Funktion aufgelistet. Hier die wichtigsten Befehle aus der GNU-info-Datei:
`list LINENUM' Print lines centered around line number LINENUM in the current source file. `list FUNCTION' Print lines centered around the beginning of function FUNCTION. `list' Print more lines. If the last lines printed were printed with a `list' command, this prints lines following the last lines printed; however, if the last line printed was a solitary line printed as part of displaying a stack frame (*note Examining the Stack: Stack.), this prints lines centered around that line. `list -' Print lines just before the lines last printed. By default, GDB prints ten source lines with any of these forms of the `list' command. You can change this using `set listsize': `set listsize COUNT' Make the `list' command display COUNT source lines (unless the `list' argument explicitly specifies some other number). `show listsize' Display the number of lines that `list' prints. Repeating a `list' command with <RET> discards the argument, so it is equivalent to typing just `list'. This is more useful than listing the same lines again. An exception is made for an argument of `-'; that argument is preserved in repetition so that each repetition moves up in the source file.
Es soll jetzt das Programm zunächst einmal ganz nomal mit dem Befehl run [r] gestarttet werden:
(gdb) r Starting program: /home/gerd/public_html/fh/II-INF3/II-INF3-TH/VL4/bsp1 Program exited normally. (gdb)
Das Programm startet und wird beendet, ohne dass weitere Informationen ausgegeben werden. Will man mehr wissen, dann muss man in das Programm hineinschauen. Zu diesem Zweck kann man sogenannte breakpoints [b] (Unterbrechungspunkte, Haltepunkte) festlegen, an denen das Programm anhalten soll.
Im folgenden Beispiel wird nach Aufruf des Debuggers mit dem Programm
gerd@kant:~/public_html/fh/II-INF3/II-INF3-TH/VL4> gdb bsp1 GNU gdb 6.1 Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i586-suse-linux"...Using host libthread_db library "/lib/tls/libthread_db.so.1". (gdb) set listsize 25 (gdb) l 1 /**************************** 2 * 3 * bsp1_main.c 4 * 5 * idea: simple demo for calling a function 6 * 7 **********************************/ 8 9 10 int main(){ 11 12 int accum = 0; 13 14 accum = sum(1,3); 15 16 return 0; 17 18 }
Dann wird ein erster Breakpoint in Zeile 14 gesetzt:
(gdb) b 14 Breakpoint 1 at 0x8048373: file bsp1_main.c, line 14.
Dann wird der Quelltext der funktion
(gdb) l sum 1 /**************************** 2 * 3 * bsp1_sum.c 4 * 5 * idea: simple illustration 6 * 7 **********************************/ 8 9 10 11 int sum(int x, int y){ 12 13 int t; 14 15 t = x + y; 16 17 return t; 18 19 }
Auch in diesem Quelltext wird ein Breakpoint gesetzt:
(gdb) b 13 Breakpoint 2 at 0x8048392: file bsp1_sum.c, line 13.
Schliesslich wird der Quelltext der Funktion main() nochmals gelistet und darin ein Breakpoint in Zeile 16 gesetzt:
(gdb) l main 1 /**************************** 2 * 3 * bsp1_main.c 4 * 5 * idea: simple demo for calling a function 6 * 7 **********************************/ 8 9 10 int main(){ 11 12 int accum = 0; 13 14 accum = sum(1,3); 15 16 return 0; 17 18 } (gdb) b 16 Breakpoint 3 at 0x8048385: file bsp1_main.c, line 16. (gdb)
Lässt man das programm nun mit
(gdb) r Starting program: /home/gerd/public_html/fh/II-INF3/II-INF3-TH/VL4/bsp1 Breakpoint 1, main () at bsp1_main.c:14 14 accum = sum(1,3);
Will man wissen, welchen Wert die jeweiligen Variablen haben, kann man sich den Wert z.B. mit dem Befehl print [p] anzeigen lassen:
(gdb) p accum $1 = 0
Wie man sehen kann, hat die Variable accum an dieser Stelle noch ihren startwert, da die Funktion sum zur Berechnung eines neuen Wertes noch nichtausgeführt worden ist. Den Programmlauf cann man mit dem Befehl continue [c] fortsetzen.
(gdb) c Continuing. Breakpoint 2, sum (x=1, y=3) at bsp1_sum.c:15 15 t = x + y;
Wieder kann man sich die Werte der Variablen anzeigen lassen:
(gdb) p x $2 = 1 (gdb) p y $3 = 3 (gdb) p t $4 = -1073746076
Während die Variablen x und y schon einen Wert aufgrund des Funktionsaufrufs haben, hat die Variable t noch keinen definierten Wert, da die Addition noch nicht durchgeführt worden ist. Fortsetzung mit 'c':
(gdb) c Continuing. Breakpoint 3, main () at bsp1_main.c:16 16 return 0; (gdb) p accum $5 = 4 (gdb) c Continuing. Program exited normally.
Wie man erkennen kann, hat die Variable accum schliesslich den Wert, den man erwartet.
Um Breakpoints zu verwalten, stehen einige Befehle zur Verfügung. Hier die wichtigsten.
Hat man mehrere breakpoints gesetzt, dann kann es hilfreich sein, sich alle breakpoints anzeigen zu lassen, um den Übergblick zu wahren. Dazu gibnt es den Befehl info:
(gdb) info breakpoints Num Type Disp Enb Address What 1 breakpoint keep y 0x08048373 in main at bsp1_main.c:14 breakpoint already hit 1 time 2 breakpoint keep y 0x08048392 in sum at bsp1_sum.c:13 breakpoint already hit 1 time 3 breakpoint keep y 0x08048385 in main at bsp1_main.c:16 breakpoint already hit 1 time
Will man aus dieser Liste einen Breakpoint löschen, kann man den Befehl delete benutzen gefolgt von der Nr. des zu löschen Breakpoints:
(gdb) delete 1 (gdb) info breakpoints Num Type Disp Enb Address What 2 breakpoint keep y 0x08048392 in sum at bsp1_sum.c:13 breakpoint already hit 1 time 3 breakpoint keep y 0x08048385 in main at bsp1_main.c:16 breakpoint already hit 1 time
Man kann aber auch einen Breakpoint auch nur vorübergehend, zeitweise neutralisieren, indem man ihn mit dem Befehl disable abschaltet:
(gdb) disable 2 (gdb) info breakpoints Num Type Disp Enb Address What 2 breakpoint keep n 0x08048392 in sum at bsp1_sum.c:13 breakpoint already hit 1 time 3 breakpoint keep y 0x08048385 in main at bsp1_main.c:16 breakpoint already hit 1 time (gdb) r Starting program: /home/gerd/public_html/fh/II-INF3/II-INF3-TH/VL4/bsp1 Breakpoint 3, main () at bsp1_main.c:16 16 return 0; (gdb) info breakpoints Num Type Disp Enb Address What 2 breakpoint keep n 0x08048392 in sum at bsp1_sum.c:13 3 breakpoint keep y 0x08048385 in main at bsp1_main.c:16 breakpoint already hit 1 time
Die Reaktivierung eines vorübergehend ausgeschalteten Haltepunkts gelingt wieder mit dem Befehl enable:
(gdb) enable 2 (gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/gerd/public_html/fh/II-INF3/II-INF3-TH/VL4/bsp1 Breakpoint 2, sum (x=1, y=3) at bsp1_sum.c:15 15 t = x + y; (gdb) c Continuing. Breakpoint 3, main () at bsp1_main.c:16 16 return 0; (gdb) info breakpoints Num Type Disp Enb Address What 2 breakpoint keep y 0x08048392 in sum at bsp1_sum.c:13 breakpoint already hit 1 time 3 breakpoint keep y 0x08048385 in main at bsp1_main.c:16 breakpoint already hit 1 time (gdb) delete 2 (gdb) delete 3 (gdb) info breakpoints No breakpoints or watchpoints. (gdb) c Continuing. Program exited normally. (gdb)
Will man sich den Inhalt einer Variablen automatisch anzeigen lassen, dann kann man den Befehl display benutzen. Zuerst setzt man einen Breakpoint auf die Stelle, bei der man eine bestimmte Variable abfragen kann, dann gibt man den Befehl display mit dieser Variablen an, und dann kann man das Programm laufen lassen:
(gdb) l 4 * 5 * idea: simple demo for calling a function 6 * 7 **********************************/ 8 9 10 int main(){ 11 12 int accum = 0; 13 14 accum = sum(1,3); 15 16 return 0; 17 18 } (gdb) b 16 Breakpoint 4 at 0x8048385: file bsp1_main.c, line 16. (gdb) r Starting program: /home/gerd/public_html/fh/II-INF3/II-INF3-TH/VL4/bsp1 Breakpoint 4, main () at bsp1_main.c:16 16 return 0; (gdb) display accum 1: accum = 4 (gdb) c Continuing. Program exited normally. (gdb) r Starting program: /home/gerd/public_html/fh/II-INF3/II-INF3-TH/VL4/bsp1 Breakpoint 4, main () at bsp1_main.c:16 16 return 0; 1: accum = 4 (gdb) c Continuing. Program exited normally. (gdb) r Starting program: /home/gerd/public_html/fh/II-INF3/II-INF3-TH/VL4/bsp1 Breakpoint 4, main () at bsp1_main.c:16 16 return 0; 1: accum = 4
Der Debugger gdb bietet auch die Möglichkeit, sich den aktuellen Stack mit dem Befehl backtrace [bt] anzuschauen. Im info-text heisst es dazu: Each line in the backtrace shows the frame number and the function name. The program counter value is also shown--unless you use `set print address off'. The backtrace also shows the source file name and line number, as well as the arguments to the function. The program counter value is omitted if it is at the beginning of the code for that line number. Dazu ein Beispiel. Wir setzen in der funktion sum einen Breakpoint und rufen von dort den Stapel ab:
(gdb) r Starting program: /home/gerd/public_html/fh/II-INF3/II-INF3-TH/VL4/bsp1 Breakpoint 3, sum (x=1, y=3) at bsp1_sum.c:17 17 return t; (gdb) bt #0 sum (x=1, y=3) at bsp1_sum.c:17 #1 0x0804837f in main () at bsp1_main.c:14 (gdb) c Continuing. Program exited normally.
Man kann sehen, dass auf dem stack zu diesem Zeitpunkt zwei Funktionen liegen: die main()-Funktion als die Startfunktion eines C-Programms sowie die sum()-Funktion, die noch nicht beendet ist.
Innerhalb des Stacks kann man sich auch mit den Befehlen up und down hoch- oder runterbewegen:
(gdb) up #1 0x0804837f in main () at bsp1_main.c:14 14 accum = sum(1,3); (gdb) up Initial frame selected; you cannot go up. (gdb) down #0 sum (x=1, y=3) at bsp1_sum.c:17 17 return t; (gdb) down Bottom (i.e., innermost) frame selected; you cannot go down.
Im info-File kann man zusaetzlich u.a. nachlesen:
`frame N' `f N' Select frame number N. Recall that frame zero is the innermost (currently executing) frame, frame one is the frame that called the innermost one, and so on. The highest-numbered frame is the one for `main'. `frame ADDR' `f ADDR' Select the frame at address ADDR. This is useful mainly if the chaining of stack frames has been damaged by a bug, making it impossible for GDB to assign numbers properly to all frames. In addition, this can be useful when your program has multiple stacks and switches between them. On the SPARC architecture, `frame' needs two addresses to select an arbitrary frame: a frame pointer and a stack pointer. On the MIPS and Alpha architecture, it needs two addresses: a stack pointer and a program counter. On the 29k architecture, it needs three addresses: a register stack pointer, a program counter, and a memory stack pointer. `up N' Move N frames up the stack. For positive numbers N, this advances toward the outermost frame, to higher frame numbers, to frames that have existed longer. N defaults to one. `down N' Move N frames down the stack. For positive numbers N, this advances toward the innermost frame, to lower frame numbers, to frames that were created more recently. N defaults to one. You may abbreviate `down' as `do'. All of these commands end by printing two lines of output describing the frame. The first line shows the frame number, the function name, the arguments, and the source file and line number of execution in that frame. The second line shows the text of that source line. ... There are several other commands to print information about the selected stack frame. `frame' `f' When used without any argument, this command does not change which frame is selected, but prints a brief description of the currently selected stack frame. It can be abbreviated `f'. With an argument, this command is used to select a stack frame. *Note Selecting a frame: Selection. `info frame' `info f' This command prints a verbose description of the selected stack frame, including: * the address of the frame * the address of the next frame down (called by this frame) * the address of the next frame up (caller of this frame) * the language in which the source code corresponding to this frame is written * the address of the frame's arguments * the address of the frame's local variables * the program counter saved in it (the address of execution in the caller frame) * which registers were saved in the frame The verbose description is useful when something has gone wrong that has made the stack format fail to fit the usual conventions. `info frame ADDR' `info f ADDR' Print a verbose description of the frame at address ADDR, without selecting that frame. The selected frame remains unchanged by this command. This requires the same kind of address (more than one for some architectures) that you specify in the `frame' command. *Note Selecting a frame: Selection. `info args' Print the arguments of the selected frame, each on a separate line. `info locals' Print the local variables of the selected frame, each on a separate line. These are all variables (declared either static or automatic) accessible at the point of execution of the selected frame. `info catch' Print a list of all the exception handlers that are active in the current stack frame at the current point of execution. To see other exception handlers, visit the associated frame (using the `up', `down', or `frame' commands); then type `info catch'. *Note Setting catchpoints: Set Catchpoints.
Wendet man diese zusätzlichen Befehle auf das beispiel an, dann erhält man zusätzliche Informationen:
(gdb) f 0 #0 sum (x=1, y=3) at bsp1_sum.c:17 17 return t; (gdb) info frame Stack level 0, frame at 0xbfffef60: eip = 0x804839b in sum (bsp1_sum.c:17); saved eip 0x804837f called by frame at 0xbfffef80 source language c. Arglist at 0xbfffef58, args: x=1, y=3 Locals at 0xbfffef58, Previous frame's sp is 0xbfffef60 Saved registers: ebp at 0xbfffef58, eip at 0xbfffef5c (gdb) down Bottom (i.e., innermost) frame selected; you cannot go down. (gdb) up #1 0x0804837f in main () at bsp1_main.c:14 14 accum = sum(1,3); (gdb) info frame Stack level 1, frame at 0xbfffef80: eip = 0x804837f in main (bsp1_main.c:14); saved eip 0x40047500 caller of frame at 0xbfffef60 source language c. Arglist at 0xbfffef78, args: Locals at 0xbfffef78, Previous frame's sp is 0xbfffef80 Saved registers: ebp at 0xbfffef78, eip at 0xbfffef7c
Eine weitere interessante Möglichkeit ist die, sich die Inhalte der Register der CPU anzeigen zu lassen. Im info-File kann man lesen:
You can refer to machine register contents, in expressions, as variables with names starting with `$'. The names of registers are different for each machine; use `info registers' to see the names used on your machine. `info registers' Print the names and values of all registers except floating-point and vector registers (in the selected stack frame). `info all-registers' Print the names and values of all registers, including floating-point and vector registers (in the selected stack frame).
Wendet man diese Befehle an, erhält man im vorliegenden Kontext folgende Ausgaben:
(gdb) info registers eax 0x4 4 ecx 0x400474c5 1074033861 edx 0x1 1 ebx 0x40143bd0 1075067856 esp 0xbfffef60 0xbfffef60 ebp 0xbfffef78 0xbfffef78 esi 0x400168c0 1073834176 edi 0x80483a0 134513568 eip 0x804837f 0x804837f eflags 0x200202 2097666 cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 (gdb) info all-registers eax 0x4 4 ecx 0x400474c5 1074033861 edx 0x1 1 ebx 0x40143bd0 1075067856 esp 0xbfffef60 0xbfffef60 ebp 0xbfffef78 0xbfffef78 esi 0x400168c0 1073834176 edi 0x80483a0 134513568 eip 0x804837f 0x804837f eflags 0x200202 2097666 cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 st0 0 (raw 0x00000000000000000000) st1 0 (raw 0x00000000000000000000) st2 0 (raw 0x00000000000000000000) st3 0 (raw 0x00000000000000000000) st4 0 (raw 0x00000000000000000000) st5 0 (raw 0x00000000000000000000) st6 0 (raw 0x00000000000000000000) st7 0 (raw 0x00000000000000000000) fctrl 0x37f 895 fstat 0x0 0 ftag 0xffff 65535 fiseg 0x0 0 fioff 0x0 0 foseg 0x0 0 fooff 0x0 0 fop 0x0 0 xmm0 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000} xmm1 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000} xmm2 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000} xmm3 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000} xmm4 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000} xmm5 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000} xmm6 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000} xmm7 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000} mxcsr 0x1f80 8064 mm0 {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} mm1 {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} mm2 {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} mm3 {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} mm4 {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} mm5 {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} mm6 {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} mm7 {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} (gdb)
In der info-Datei kann man weiterhin lesen:
..... GDB has four "standard" register names that are available (in expressions) on most machines--whenever they do not conflict with an architecture's canonical mnemonics for registers. The register names `$pc' and `$sp' are used for the program counter register and the stack pointer. `$fp' is used for a register that contains a pointer to the current stack frame, and `$ps' is used for a register that contains the processor status. For example, you could print the program counter in hex with p/x $pc or print the instruction to be executed next with x/i $pc or add four to the stack pointer(1) with set $sp += 4 Whenever possible, these four standard register names are available on your machine even though the machine has different canonical mnemonics, so long as there is no conflict. The `info registers' command shows the canonical names. For example, on the SPARC, `info registers' displays the processor status register as `$psr' but you can also refer to it as `$ps'; and on x86-based machines `$ps' is an alias for the EFLAGS register. GDB always considers the contents of an ordinary register as an integer when the register is examined in this way. Some machines have special registers which can hold nothing but floating point; these registers are considered to have floating point values. There is no way to refer to the contents of an ordinary register as floating point value (although you can _print_ it as a floating point value with `print/f $REGNAME'). Some registers have distinct "raw" and "virtual" data formats. This means that the data format in which the register contents are saved by the operating system is not the same one that your program normally sees. For example, the registers of the 68881 floating point coprocessor are always saved in "extended" (raw) format, but all C programs expect to work with "double" (virtual) format. In such cases, GDB normally works with the virtual format only (the format that makes sense for your program), but the `info registers' command prints the data in both formats. Normally, register values are relative to the selected stack frame (*note Selecting a frame: Selection.). This means that you get the value that the register would contain if all stack frames farther in were exited and their saved registers restored. In order to see the true contents of hardware registers, you must select the innermost frame (with `frame 0'). However, GDB must deduce where registers are saved, from the machine code generated by your compiler. If some registers are not saved, or if GDB is unable to locate the saved registers, the selected stack frame makes no difference. ---------- Footnotes ---------- (1) This is a way of removing one word from the stack, on machines where stacks grow downward in memory (most machines, nowadays). This assumes that the innermost stack frame is selected; setting `$sp' is not allowed when other stack frames are selected. To pop entire frames off the stack, regardless of machine architecture, use `return'; see *Note Returning from a function: Returning.
Floating point hardware ======================= Depending on the configuration, GDB may be able to give you more information about the status of the floating point hardware. `info float' Display hardware-dependent information about the floating point unit. The exact contents and layout vary depending on the floating point chip. Currently, `info float' is supported on the ARM and x86 machines.
Vector Unit =========== Depending on the configuration, GDB may be able to give you more information about the status of the vector unit. `info vector' Display information about the vector unit. The exact contents and layout vary depending on the hardware.
Operating system auxiliary vector ================================= Some operating systems supply an "auxiliary vector" to programs at startup. This is akin to the arguments and environment that you specify for a program, but contains a system-dependent variety of binary values that tell system libraries important details about the hardware, operating system, and process. Each value's purpose is identified by an integer tag; the meanings are well-known but system-specific. Depending on the configuration and operating system facilities, GDB may be able to show you this information. `info auxv' Display the auxiliary vector of the inferior, which can be either a live process or a core dump file. GDB prints each tag value numerically, and also shows names and text descriptions for recognized tags. Some values in the vector are numbers, some bit masks, and some pointers to strings or other data. GDB displays each value in the most appropriate form for a recognized tag, and in hexadecimal for an unrecognized tag.
Wendet man diese zusätzlichen info-Befehle an, dann erhält man eine nach funktionalen Gruppen strukturierte Ausgabe, allerdings erst dann, nachdem das Programm gestartet wurde.
gerd@kant:~/public_html/fh/II-INF3/II-INF3-TH/VL4> gdb bsp1 GNU gdb 6.1 Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i586-suse-linux"...Using host libthread_db library "/lib/tls/libthread_db.so.1". (gdb) info float The program has no registers now. (gdb) r Starting program: /home/gerd/public_html/fh/II-INF3/II-INF3-TH/VL4/bsp1 Program exited normally. (gdb) set listsize 25 (gdb) l 1 /**************************** 2 * 3 * bsp1_main.c 4 * 5 * idea: simple demo for calling a function 6 * 7 **********************************/ 8 9 10 int main(){ 11 12 int accum = 0; 13 14 accum = sum(1,3); 15 16 return 0; 17 18 } (gdb) b 16 Breakpoint 1 at 0x8048385: file bsp1_main.c, line 16. (gdb) info float The program has no registers now. (gdb) info vector The program has no registers now. (gdb) info auxv The program has no auxiliary information now. (gdb) info registers The program has no registers now. (gdb) r Starting program: /home/gerd/public_html/fh/II-INF3/II-INF3-TH/VL4/bsp1 Breakpoint 1, main () at bsp1_main.c:16 16 return 0; (gdb) info registers eax 0x4 4 ecx 0x400474c5 1074033861 edx 0x1 1 ebx 0x40143bd0 1075067856 esp 0xbfffef70 0xbfffef70 ebp 0xbfffef78 0xbfffef78 esi 0x400168c0 1073834176 edi 0x80483a0 134513568 eip 0x8048385 0x8048385 eflags 0x200282 2097794 cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 (gdb) info float R7: Empty 0x00000000000000000000 R6: Empty 0x00000000000000000000 R5: Empty 0x00000000000000000000 R4: Empty 0x00000000000000000000 R3: Empty 0x00000000000000000000 R2: Empty 0x00000000000000000000 R1: Empty 0x00000000000000000000 =>R0: Empty 0x00000000000000000000 Status Word: 0x0000 TOP: 0 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0xffff Instruction Pointer: 0x00:0x00000000 Operand Pointer: 0x00:0x00000000 Opcode: 0x0000 (gdb) info vector xmm0 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000} xmm1 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000} xmm2 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000} xmm3 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000} xmm4 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000} xmm5 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000} xmm6 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000} xmm7 {v4_float = {0x0, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x0 <repeats 16 times>}, v8_int16 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x0, 0x0, 0x0, 0x0}, v2_int64 = {0x0, 0x0}, uint128 = 0x00000000000000000000000000000000} mxcsr 0x1f80 8064 mm0 {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} mm1 {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} mm2 {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} mm3 {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} mm4 {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} mm5 {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} mm6 {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} mm7 {uint64 = 0x0, v2_int32 = {0x0, 0x0}, v4_int16 = {0x0, 0x0, 0x0, 0x0}, v8_int8 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} (gdb) info auxv 32 AT_SYSINFO Special system info/entry points 0xffffe400 33 AT_SYSINFO_EHDR System-supplied DSO's ELF header 0xffffe000 16 AT_HWCAP Machine-dependent CPU capability hints 0xbfebf9ff 6 AT_PAGESZ System page size 4096 17 AT_CLKTCK Frequency of times() 100 3 AT_PHDR Program headers for program 0x8048034 4 AT_PHENT Size of program header entry 32 5 AT_PHNUM Number of program headers 8 7 AT_BASE Base address of interpreter 0x40000000 8 AT_FLAGS Flags 0x0 9 AT_ENTRY Entry point of program 0x80482a0 11 AT_UID Real user ID 1000 12 AT_EUID Effective user ID 1000 13 AT_GID Real group ID 100 14 AT_EGID Effective group ID 100 23 AT_SECURE Boolean, was exec setuid-like? 0 15 AT_PLATFORM String identifying platform 0xbffff1fd "i686" 0 AT_NULL End of vector 0x0 (gdb)
Diese Beispiele mögen genügen, um zu zeigen, wie mächtig der Debugger gdb ist. Zusätzliche Befehle kann man in der info-Datei nachlesen.
Um diesen Debugger nun sinnvoll einsetzen zu können, wird eine ausführlichere Kenntnis der Hardware einer IA32-CPU benötigt. Diese zusätzlichen Informationen sollen in der nachfolgenden Vorlesung vorgestellt werden. Als Quellen werden die eingangs zitierten Bücher und Manuals benutzt.
Wie ist der Zusammenhang zwischen der Formulierung einer Problemstellung, dem C-Kode, dem Assemblerkode, dem Binärkode, der ISA-Schnitttelle und der CPU-Hardware?
Mit welchen Befehlen können Sie C-Quelltext in Assembler und Binärkode übersetzen?
Mit welchem Befehl können Sie sich unter Linux Binärdateien lesbar anzeigen lassen?
Mit welchem Softwarewerkzeug kann man unter Linux den Ablauf von C und C++-Programmen kontrollieren?
Schreiben sie ein kleines C-Programm mit mindestens einem Funktionsaufruf, kompilieren Sie es und zeigen Sie, wie man mit Hilfe des Debuggers gdb den Inhalt der Dateien auflisten kann. Wie können sie die Anzahl der aufgelisteten Zeilen verändern?
Was sind Haltepunkte (breakpoints)? Wie können Sie Haltepunkte in ihrem Programm setzen? Wie können Sie sich alle Haltepunkte anzeigen lassen? Wie können Sie Haltepunkte löschen/ deaktivieren/ wieder neu aktivieren ?
Wie können sie sich die Inhalte von Variablen anzeigen lassen? Was bewirkt der Befehl 'display'?
Welche Möglichkeiten gibt es, sich den Stackinhalt anzeigen zu lassen? Was ist ein Frame? Wie können sie gezielt Informationen über einen bestimmten Frame abrufen?
Was sind Register? Wie können sie sich die verfügbaren Register anzeigen lassen? Gibt es verschiedene Gruppen von Registern? Welche? Wie kann man sich die Inhalte der Register anzeigen lassen?