GCC's dead code elimination seems to be the culprit -- nothing to do with SEH. When the bottom block of the function is not reachable, those statements are eliminated. GCC then arbitrarily creates a label at the location where we took the address.
Since we're manipulating program flow directly, the rules of C tell us that we're not allowed to complain when the compiler does something wacky like this.
Here is a more minimal example:
static void* handler_eip;
static void badcall(void) {
handler_eip = &&fail;
int value = 100/0;
printf("not handled.\n");
//goto fail;
return;
fail:
printf("error handled.\n");
return;
}
Note the location of L2 with the goto in place:
_badcall:
pushl %ebp
movl %esp, %ebp
subl $56, %esp
movl $L2, _handler_eip
movl $100, %eax
movl $0, -28(%ebp)
movl %eax, %edx
sarl $31, %edx
idivl -28(%ebp)
movl %eax, -12(%ebp)
movl $LC0, (%esp)
call _puts
nop
L2:
movl $LC1, (%esp)
call _puts
nop
leave
ret
And without:
_badcall:
pushl %ebp
movl %esp, %ebp
subl $56, %esp
L2:
movl $L2, _handler_eip
movl $100, %eax
movl $0, -28(%ebp)
movl %eax, %edx
sarl $31, %edx
idivl -28(%ebp)
movl %eax, -12(%ebp)
movl $LC0, (%esp)
call _puts
nop
leave
ret