So, I am doing my own personality function (because I can).
In this function, I am parsing a call site table. Each record has four fields – callsite offset, callsite length, landing pad offset and action idx.
My algorithm in relation to this table is pretty much as follows:
-
If the address of instruction that threw is less then any callsite address – terminate;
-
If landing pad offset is zero – continue unwinding;
-
If action idx is zero:
a. if still looking – continue unwinding;
b. if doing clean-up – transfer control to landing pad.
Every record is parsed sequentially from lowest to highest.
For a simple C++ code like this:
void raise() {
printf("Beggining of raisen");
struct DestroyMe {
volatile int payload;
DestroyMe() {
printf("DESTROY ME raisen");
payload = 1;
};
~DestroyMe() {
printf("I AM DESTOYED raisen");
payload = 0;
};
};
DestroyMe dm{};
printf("Before thrown");
throw Exception();
}
clang gives the following call site table:
.Lcst_begin0:
.uleb128 .Lfunc_begin0-.Lfunc_begin0 # >> Call Site 1 <<
.uleb128 .Ltmp0-.Lfunc_begin0 # Call between .Lfunc_begin0 and .Ltmp0
.byte 0 # has no landing pad
.byte 0 # On action: cleanup
.uleb128 .Ltmp0-.Lfunc_begin0 # >> Call Site 2 <<
.uleb128 .Ltmp1-.Ltmp0 # Call between .Ltmp0 and .Ltmp1
.uleb128 .Ltmp4-.Lfunc_begin0 # jumps to .Ltmp4
.byte 0 # On action: cleanup
.uleb128 .Ltmp1-.Lfunc_begin0 # >> Call Site 3 <<
.uleb128 .Ltmp2-.Ltmp1 # Call between .Ltmp1 and .Ltmp2
.byte 0 # has no landing pad
.byte 0 # On action: cleanup
.uleb128 .Ltmp2-.Lfunc_begin0 # >> Call Site 4 <<
.uleb128 .Ltmp3-.Ltmp2 # Call between .Ltmp2 and .Ltmp3
.uleb128 .Ltmp4-.Lfunc_begin0 # jumps to .Ltmp4
.byte 0 # On action: cleanup
.uleb128 .Ltmp3-.Lfunc_begin0 # >> Call Site 5 <<
.uleb128 .Lfunc_end0-.Ltmp3 # Call between .Ltmp3 and .Lfunc_end0
.byte 0 # has no landing pad
.byte 0 # On action: cleanup
My questions are:
-
How come landing pad and action idx are both 0 when there are destructors to call?
-
Even if this is normal, then what should be checked first?
-
Even if this is normal (#2), how can I transfer control to cleanup when landing pad is 0? It will just go to the start of the function.
I checked with this implementation, and my logic seem to align with it. Handlers are working fine and control is passed to the expected catch blocks. This is just a problem with clean-ups that I cannot solve for weeks.
I also used mostly these links (among others) as a reference, but none of them answer my questions:
Itanium spec;
Another Itanium spec;
Awesome blog post.