Signed-off-by: Daniel Lehman dlehman25@gmail.com ---
supercedes https://source.winehq.org/patches/data/185973
with /O2 the unwind table provides a direct frame+offset of the object to unwind with /Od the unwind table sets flag 0x2 signaling only a frame is passed; the handler itself adds the offset of the object to unwind
seems safe to pass both; both %rcx and %rdx are volatile and one is ignored
by comparison, FH3 currently always calls: unwind_handler(0, frame)
sample repro: /* cl /EHs /Od /MD test.cpp */ '#include <stdio.h>
class test { public: test(int d) : id(d) { printf("ctor %x\n", id); } ~test() { printf("dtor %x\n", id); } int id; };
int main(void) { try { test t(0x42); throw 1; } catch (int i) { printf("catch %d\n", i); } return 0; }
Signed-off-by: Daniel Lehman dlehman25@gmail.com --- dlls/vcruntime140_1/except_x86_64.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-)
diff --git a/dlls/vcruntime140_1/except_x86_64.c b/dlls/vcruntime140_1/except_x86_64.c index e1a39c0662..fec133932f 100644 --- a/dlls/vcruntime140_1/except_x86_64.c +++ b/dlls/vcruntime140_1/except_x86_64.c @@ -77,6 +77,9 @@ typedef struct #define TYPE_FLAG_VOLATILE 2 #define TYPE_FLAG_REFERENCE 8
+#define UNWIND_FLAG_HANDLER 1 +#define UNWIND_FLAG_USE_FRAME 2 + typedef struct { UINT start_level; @@ -156,7 +159,7 @@ static inline void* rva_to_ptr(UINT rva, ULONG64 base) return rva ? (void*)(base+rva) : NULL; }
-static BOOL read_unwind_info(BYTE **b, unwind_info *ui) +static void read_unwind_info(BYTE **b, unwind_info *ui) { BYTE *p = *b;
@@ -165,18 +168,12 @@ static BOOL read_unwind_info(BYTE **b, unwind_info *ui) ui->prev = p - (ui->flags >> 2); ui->flags &= 0x3;
- if (ui->flags & 0x1) + if (ui->flags & UNWIND_FLAG_HANDLER) { ui->handler = read_rva(b); - ui->object = decode_uint(b); /* frame offset */ - } - - if (ui->flags & 0x2) - { - FIXME("unknown flag: %x\n", ui->flags); - return FALSE; + if (!(ui->flags & UNWIND_FLAG_USE_FRAME)) + ui->object = decode_uint(b); /* frame offset */ } - return TRUE; }
static void read_tryblock_info(BYTE **b, tryblock_info *ti, ULONG64 image_base) @@ -268,7 +265,7 @@ static BOOL validate_cxx_function_descr4(const cxx_function_descr *descr, DISPAT BYTE *entry = unwind_map; unwind_info ui;
- if (!read_unwind_info(&unwind_map, &ui)) return FALSE; + read_unwind_info(&unwind_map, &ui); if (ui.prev < (BYTE*)rva_to_ptr(descr->unwind_map, image_base)) ui.prev = NULL; TRACE(" %d (%p): flags 0x%x prev %p func 0x%x(%p) object 0x%x\n", i, entry, ui.flags, ui.prev, ui.handler, @@ -403,7 +400,7 @@ static inline void copy_exception(void *object, ULONG64 frame, DISPATCHER_CONTEX static void cxx_local_unwind4(ULONG64 frame, DISPATCHER_CONTEXT *dispatch, const cxx_function_descr *descr, int trylevel, int last_level) { - void (__cdecl *handler_dtor)(void *obj); + void (__cdecl *handler_dtor)(void *obj, ULONG64 frame); BYTE *unwind_data, *last; unwind_info ui; void *obj; @@ -442,7 +439,7 @@ static void cxx_local_unwind4(ULONG64 frame, DISPATCHER_CONTEXT *dispatch, handler_dtor = rva_to_ptr(ui.handler, dispatch->ImageBase); obj = rva_to_ptr(ui.object, frame); TRACE("handler: %p object: %p\n", handler_dtor, obj); - handler_dtor(obj); + handler_dtor(obj, frame); } } }
Signed-off-by: Daniel Lehman dlehman25@gmail.com --- dlls/vcruntime140_1/except_x86_64.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-)
diff --git a/dlls/vcruntime140_1/except_x86_64.c b/dlls/vcruntime140_1/except_x86_64.c index fec133932f..b0c494ce1c 100644 --- a/dlls/vcruntime140_1/except_x86_64.c +++ b/dlls/vcruntime140_1/except_x86_64.c @@ -116,34 +116,36 @@ typedef struct static UINT decode_uint(BYTE **b) { UINT ret; + BYTE *p = *b;
- if ((**b & 1) == 0) + if ((*p & 1) == 0) { - ret = *b[0] >> 1; - *b += 1; + ret = p[0] >> 1; + p += 1; } - else if ((**b & 3) == 1) + else if ((*p & 3) == 1) { - ret = (*b[0] >> 2) + (*b[1] << 6); - *b += 2; + ret = (p[0] >> 2) + (p[1] << 6); + p += 2; } - else if ((**b & 7) == 3) + else if ((*p & 7) == 3) { - ret = (*b[0] >> 3) + (*b[1] << 5) + (*b[2] << 13); - *b += 3; + ret = (p[0] >> 3) + (p[1] << 5) + (p[2] << 13); + p += 3; } - else if ((**b & 15) == 7) + else if ((*p & 15) == 7) { - ret = (*b[0] >> 4) + (*b[1] << 4) + (*b[2] << 12) + (*b[3] << 20); - *b += 4; + ret = (p[0] >> 4) + (p[1] << 4) + (p[2] << 12) + (p[3] << 20); + p += 4; } else { FIXME("not implemented - expect crash\n"); ret = 0; - *b += 5; + p += 5; }
+ *b = p; return ret; }
Signed-off-by: Daniel Lehman dlehman25@gmail.com ---
-1 is a valid state, but nothing to unwind
sample: cl /EHa /Od /MD test.cpp
try { std::wstring value(L""); value = value.substr(std::wstring::npos, 1); } catch (std::exception) { printf("catch\n"); } --- dlls/vcruntime140_1/except_x86_64.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/dlls/vcruntime140_1/except_x86_64.c b/dlls/vcruntime140_1/except_x86_64.c index b0c494ce1c..ef171a5f6c 100644 --- a/dlls/vcruntime140_1/except_x86_64.c +++ b/dlls/vcruntime140_1/except_x86_64.c @@ -416,6 +416,9 @@ static void cxx_local_unwind4(ULONG64 frame, DISPATCHER_CONTEXT *dispatch,
TRACE("current level: %d, last level: %d\n", trylevel, last_level);
+ if (trylevel==-1) + return; + if (trylevel<0 || trylevel>=descr->unwind_count) { ERR("invalid trylevel %d\n", trylevel);
Signed-off-by: Daniel Lehman dlehman25@gmail.com ---
'last' points to state 0 so the (unwind_data > last) stops too soon and a handler for state 0 is skipped
void throw_int(int x) { test t(0xcafef00d); /* at 0 */ throw x; }
void test_unwind_to_minus_one(void) { try { throw_int(42); } catch(int i) { printf("catch %d\n", i); } } --- dlls/vcruntime140_1/except_x86_64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/vcruntime140_1/except_x86_64.c b/dlls/vcruntime140_1/except_x86_64.c index ef171a5f6c..f10c67c573 100644 --- a/dlls/vcruntime140_1/except_x86_64.c +++ b/dlls/vcruntime140_1/except_x86_64.c @@ -426,7 +426,7 @@ static void cxx_local_unwind4(ULONG64 frame, DISPATCHER_CONTEXT *dispatch, }
unwind_data = rva_to_ptr(descr->unwind_map, dispatch->ImageBase); - last = unwind_data; + last = unwind_data - 1; for (i = 0; i < trylevel; i++) { BYTE *addr = unwind_data;
Signed-off-by: Daniel Lehman dlehman25@gmail.com ---
nothing to unwind if trylevel is already at the last level. sample:
void test_same_state(void) { test t(1); try { try { throw test(0x42); } catch (test x) { printf("catch %x\n", x.id); throw; } } catch (test x) { printf("catch %x\n", x.id); } } --- dlls/vcruntime140_1/except_x86_64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/vcruntime140_1/except_x86_64.c b/dlls/vcruntime140_1/except_x86_64.c index f10c67c573..31871c73ec 100644 --- a/dlls/vcruntime140_1/except_x86_64.c +++ b/dlls/vcruntime140_1/except_x86_64.c @@ -416,7 +416,7 @@ static void cxx_local_unwind4(ULONG64 frame, DISPATCHER_CONTEXT *dispatch,
TRACE("current level: %d, last level: %d\n", trylevel, last_level);
- if (trylevel==-1) + if (trylevel==-1 || trylevel==last_level) return;
if (trylevel<0 || trylevel>=descr->unwind_count)