ERLEDIGT
JA
JA
ANTWORTEN
2
2
ZUGRIFFE
306
306
EMPFEHLEN
-
Hallo,
Habe mich etwas in die try catch throw Sache reingelesen. Und würde gerne wissen ob ein throw in einer Funktion den Stack verletzt.
Funktioniert das wie ein goto?
Code cpp:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
int f(void) { throw "Error!"; return 0; } int main(void) { try { f(); } catch(char *error) { printf("%s", error); } return 0; }
In asm funktioniert eine Funktion doch so:
Code cpp:1 2 3 4 5 6 7 8 9 10
main: call funktion; // hier wird die Rücksprungaddresse in den Stack gespeichert end funktion: INC R0; // was passiert wenn hier in C ein throw aufgerufen wird? // wird die Rücksprungaddresse im Stack gelassen? RET; // hier wird die Rücksprungaddresse vom Stack // geholt und nach oben zurückgesprungen (return in C)
Oder ist es besser alles mit IF abzufangen?
-
Hallo,
Nein throw beschädigt den stack nicht.
throw ist intern n bisl komplizierter. Ich versuch es mal etwas zu beschreiben, jedoch bin ich kein compiler developer, und habe dementsprechend selber nur groben einblick in das was da eigendlich vorgeht. Es ist also durchaus möglich, dass einige details hier etwas von der realität abweichen, aber es geht ja erstmal nur um das grundprinzip.
hier mal der Assemblercode der aus deinem beispiel resultiert:
Code :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
.file "test.cpp" .section .rodata .LC0: .string "Error!" .text .globl _Z1fv .type _Z1fv, @function _Z1fv: .LFB0: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 pushq %rbp .cfi_def_cfa_offset 16 movq %rsp, %rbp .cfi_offset 6, -16 .cfi_def_cfa_register 6 movl $8, %edi call __cxa_allocate_exception movq %rax, %rdx movq $.LC0, (%rdx) movl $0, %edx movl $_ZTIPKc, %esi movq %rax, %rdi call __cxa_throw .cfi_endproc .LFE0: .size _Z1fv, .-_Z1fv .section .rodata .LC1: .string "%s" .globl _Unwind_Resume .text .globl main .type main, @function main: .LFB1: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 .cfi_lsda 0x3,.LLSDA1 pushq %rbp .cfi_def_cfa_offset 16 movq %rsp, %rbp .cfi_offset 6, -16 .cfi_def_cfa_register 6 pushq %r12 pushq %rbx subq $16, %rsp .LEHB0: .cfi_offset 3, -32 .cfi_offset 12, -24 call _Z1fv .LEHE0: jmp .L4 .L11: cmpq $1, %rdx je .L5 movq %rax, %rdi .LEHB1: call _Unwind_Resume .LEHE1: .L5: movq %rax, %rdi call __cxa_begin_catch movq %rax, -24(%rbp) movq -24(%rbp), %rax movq %rax, %rsi movl $.LC1, %edi movl $0, %eax .LEHB2: call printf .LEHE2: jmp .L12 .L10: .L7: movl %edx, %ebx movq %rax, %r12 call __cxa_end_catch movq %r12, %rax movslq %ebx,%rdx movq %rax, %rdi .LEHB3: call _Unwind_Resume .L12: call __cxa_end_catch .LEHE3: .L4: movl $0, %eax addq $16, %rsp popq %rbx popq %r12 leave ret .cfi_endproc .LFE1: .size main, .-main .globl __gxx_personality_v0 .section .gcc_except_table,"a",@progbits .align 4 .LLSDA1: .byte 0xff .byte 0x3 .uleb128 .LLSDATT1-.LLSDATTD1 .LLSDATTD1: .byte 0x1 .uleb128 .LLSDACSE1-.LLSDACSB1 .LLSDACSB1: .uleb128 .LEHB0-.LFB1 .uleb128 .LEHE0-.LEHB0 .uleb128 .L11-.LFB1 .uleb128 0x1 .uleb128 .LEHB1-.LFB1 .uleb128 .LEHE1-.LEHB1 .uleb128 0x0 .uleb128 0x0 .uleb128 .LEHB2-.LFB1 .uleb128 .LEHE2-.LEHB2 .uleb128 .L10-.LFB1 .uleb128 0x0 .uleb128 .LEHB3-.LFB1 .uleb128 .LEHE3-.LEHB3 .uleb128 0x0 .uleb128 0x0 .LLSDACSE1: .byte 0x1 .byte 0x0 .align 4 .long _ZTIPc .LLSDATT1: .text .ident "GCC: (Gentoo 4.4.5 p1.2, pie-0.4.5) 4.4.5" .section .note.GNU-stack,"",@progbits
Die magie von throw steckt grossteils in der funktion __cxa_throw.
Wie du sicher weisst ist ein funktionsaufruf nicht mir einem call erledigt.
Auch wenn man in assembler in vielen fällen sich das ganze drum herum spaart, der C++ Compiler macht dies auf jedenfall sauber.
ein funktionsaufruf aus sicht des C Compiler läuft im wesendlichen (stark vereinfacht) so ab:
1.) call
2.) call speichert automatisch die rücksprungaddresse auf den stack
3.) kopieren des stack-pointers in das base-pointer register
4.) pushen aller register die durch die funktion verändert werden.
5.) decrementieren des stack pointers um platz für lokale variablen zu schaffen.
dieser speicherbereich der sich nun zwischen %ESP und %EBP bildet wird als stackframe bezeichnet.
Um das ganze bei einem throw nun sauber aufzurollen, braucht man also nur basepointer in stackpointer verschieben, und den neuen basepointer vom stack lesen.
Das ganze wiederholt man solange, bis die rücksprungaddresse innerhalb eines vorher irgendwo definierten speicherbeichs liegt.
Das die daten selber weiter im speicherbereich des stacks stehen, ist irrelevant, da der speicherbereich unterhalb des stackpointers als "frei" betrachtet wird. Man muss also nicht zwangsläufig mit pop oder ret werte vom stack holen. Ein modifizieren des stackpointers reicht aus.
Nun wird überprüft ob die catch bedingung zutrifft. Wenn ja wird der code ausgeführt. Wenn nein wird _Unwind_Resume aufgerufen, und die exception fliegt weiter den (call)stack nach oben, bis sie auf einen passenden catch block trifft, oder der stack leer ist.
Nichts viel anders arbeiten übrigens debugger wenn sie einen callstack anzeigen.Geändert von chibisuke (02.08.11 um 15:09 Uhr)
-
Danke für die ausführliche und zeitaufwendige Antwort!
Hast mir sehr weitergeholfen.
Gruß posi90
Ähnliche Themen
-
(Pointer -> Array ) In Funktion befüllen aber nach Ende der Funktion leer
Von Shadow im Forum C/C++Antworten: 3Letzter Beitrag: 13.03.10, 10:22 -
Verkürzung von sprintf() und throw Exception
Von CSANecromancer im Forum C/C++Antworten: 8Letzter Beitrag: 09.04.09, 15:51 -
Einer C Funktion eine C++ Member-Funktion zum Callback übergeben.
Von arothe im Forum C/C++Antworten: 3Letzter Beitrag: 11.02.09, 12:31 -
Javscript funktion als String an eine Javaskript funktion übergeben
Von Mazzok im Forum Javascript & AjaxAntworten: 1Letzter Beitrag: 16.12.08, 08:43 -
[VS05]: Problem mit Ausnahmespezifikation (throw)
Von DarthShader im Forum C/C++Antworten: 1Letzter Beitrag: 20.02.07, 15:15





Zitieren
Login






