tutorials.de Buch-Aktion 05/2012
ERLEDIGT
JA
ANTWORTEN
2
ZUGRIFFE
306
EMPFEHLEN
  • An Twitter übertragen
  • An Facebook übertragen
AUF DIESES THEMA
ANTWORTEN
  1. #1
    posi90 posi90 ist offline Mitglied Gold
    Registriert seit
    Aug 2010
    Beiträge
    113
    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?
     

  2. #2
    chibisuke chibisuke ist offline Mitglied Brillant
    Registriert seit
    Sep 2003
    Beiträge
    807
    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)
     

  3. #3
    posi90 posi90 ist offline Mitglied Gold
    Registriert seit
    Aug 2010
    Beiträge
    113
    Danke für die ausführliche und zeitaufwendige Antwort!

    Hast mir sehr weitergeholfen.

    Gruß posi90
     

Ähnliche Themen

  1. Antworten: 3
    Letzter Beitrag: 13.03.10, 10:22
  2. Verkürzung von sprintf() und throw Exception
    Von CSANecromancer im Forum C/C++
    Antworten: 8
    Letzter Beitrag: 09.04.09, 15:51
  3. Antworten: 3
    Letzter Beitrag: 11.02.09, 12:31
  4. Antworten: 1
    Letzter Beitrag: 16.12.08, 08:43
  5. [VS05]: Problem mit Ausnahmespezifikation (throw)
    Von DarthShader im Forum C/C++
    Antworten: 1
    Letzter Beitrag: 20.02.07, 15:15