[PATCH v2] vcruntime140_1: Support two continuation addresses for catchblock.

Paul Gofman pgofman at codeweavers.com
Tue Mar 2 13:35:56 CST 2021


MSVC generates that sometimes when there is a goto from catch block.

Fixes a crash in Company of Heroes 2.

Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
v2:
    - also update exception parameter count.

 dlls/vcruntime140_1/except_x86_64.c | 56 ++++++++++++++++++++++-------
 1 file changed, 43 insertions(+), 13 deletions(-)

diff --git a/dlls/vcruntime140_1/except_x86_64.c b/dlls/vcruntime140_1/except_x86_64.c
index a5a760b7b08..73d0d1e452c 100644
--- a/dlls/vcruntime140_1/except_x86_64.c
+++ b/dlls/vcruntime140_1/except_x86_64.c
@@ -66,12 +66,14 @@ typedef struct
     UINT type_info;
     int offset;
     UINT handler;
-    UINT ret_addr;
+    UINT ret_addr[2];
 } catchblock_info;
 #define CATCHBLOCK_FLAGS     0x01
 #define CATCHBLOCK_TYPE_INFO 0x02
 #define CATCHBLOCK_OFFSET    0x04
-#define CATCHBLOCK_RET_ADDR  0x10
+#define CATCHBLOCK_RET_ADDR_MASK 0x30
+#define CATCHBLOCK_RET_ADDR      0x10
+#define CATCHBLOCK_TWO_RET_ADDRS 0x20
 
 #define TYPE_FLAG_CONST      1
 #define TYPE_FLAG_VOLATILE   2
@@ -82,6 +84,8 @@ typedef struct
 #define UNWIND_TYPE_DTOR_PTR   2
 #define UNWIND_TYPE_FRAME      3
 
+#define CONSOLIDATE_UNWIND_PARAMETER_COUNT 10
+
 typedef struct
 {
     UINT start_level;
@@ -214,19 +218,31 @@ static void read_tryblock_info(BYTE **b, tryblock_info *ti, ULONG64 image_base)
 
 static BOOL read_catchblock_info(BYTE **b, catchblock_info *ci)
 {
+    BYTE ret_addr_type;
     memset(ci, 0, sizeof(*ci));
     ci->header = **b;
     (*b)++;
-    if (ci->header & ~(CATCHBLOCK_FLAGS | CATCHBLOCK_TYPE_INFO | CATCHBLOCK_OFFSET | CATCHBLOCK_RET_ADDR))
+    if (ci->header & ~(CATCHBLOCK_FLAGS | CATCHBLOCK_TYPE_INFO | CATCHBLOCK_OFFSET | CATCHBLOCK_RET_ADDR_MASK))
     {
         FIXME("unknown header: %x\n", ci->header);
         return FALSE;
     }
+    ret_addr_type = ci->header & CATCHBLOCK_RET_ADDR_MASK;
+    if (ret_addr_type && ret_addr_type != CATCHBLOCK_RET_ADDR && ret_addr_type != CATCHBLOCK_TWO_RET_ADDRS)
+    {
+        FIXME("unsupported ret addr type %#x.\n", ret_addr_type);
+        return FALSE;
+    }
+
     if (ci->header & CATCHBLOCK_FLAGS) ci->flags = decode_uint(b);
     if (ci->header & CATCHBLOCK_TYPE_INFO) ci->type_info = read_rva(b);
     if (ci->header & CATCHBLOCK_OFFSET) ci->offset = decode_uint(b);
     ci->handler = read_rva(b);
-    if (ci->header & CATCHBLOCK_RET_ADDR) ci->ret_addr = decode_uint(b);
+    if (ret_addr_type == CATCHBLOCK_RET_ADDR || ret_addr_type == CATCHBLOCK_TWO_RET_ADDRS)
+        ci->ret_addr[0] = decode_uint(b);
+    if (ret_addr_type == CATCHBLOCK_TWO_RET_ADDRS)
+        ci->ret_addr[1] = decode_uint(b);
+
     return TRUE;
 }
 
@@ -303,9 +319,9 @@ static BOOL validate_cxx_function_descr4(const cxx_function_descr *descr, DISPAT
             catchblock_info ci;
             if (!read_catchblock_info(&catchblock, &ci)) return FALSE;
             TRACE("        %d: header 0x%x offset %d handler 0x%x(%p) "
-                    "ret addr %x type %x %s\n", j, ci.header, ci.offset,
+                    "ret addr[0] %#x ret_addr[1] %#x type %#x %s\n", j, ci.header, ci.offset,
                     ci.handler, rva_to_ptr(ci.handler, image_base),
-                    ci.ret_addr, ci.type_info,
+                    ci.ret_addr[0], ci.ret_addr[1], ci.type_info,
                     dbgstr_type_info(rva_to_ptr(ci.type_info, image_base)));
         }
     }
@@ -535,14 +551,25 @@ static void* WINAPI call_catch_block4(EXCEPTION_RECORD *rec)
     __FINALLY_CTX(cxx_catch_cleanup, &ctx)
 
     FlsSetValue(fls_index, (void*)-2);
-    if (rec->ExceptionInformation[8]) return (void*)rec->ExceptionInformation[8];
-    return ret_addr;
+    TRACE("handler returned %p, ret_addr[0] %p, ret_addr[1] %p.\n",
+            ret_addr, rec->ExceptionInformation[8], rec->ExceptionInformation[9]);
+
+    if (rec->ExceptionInformation[9])
+    {
+        if ((ULONG_PTR)ret_addr > 1)
+        {
+            FIXME("unexpected handler result %p.\n", ret_addr);
+            return rec->ExceptionInformation[8] ? (void *)rec->ExceptionInformation[8] : ret_addr;
+        }
+        return (void*)rec->ExceptionInformation[8 + (ULONG_PTR)ret_addr];
+    }
+    return rec->ExceptionInformation[8] ? (void *)rec->ExceptionInformation[8] : ret_addr;
 }
 
 static inline BOOL cxx_is_consolidate(const EXCEPTION_RECORD *rec)
 {
-    return rec->ExceptionCode==STATUS_UNWIND_CONSOLIDATE && rec->NumberParameters==9 &&
-        rec->ExceptionInformation[0]==(ULONG_PTR)call_catch_block4;
+    return rec->ExceptionCode==STATUS_UNWIND_CONSOLIDATE && rec->NumberParameters == CONSOLIDATE_UNWIND_PARAMETER_COUNT
+            && rec->ExceptionInformation[0]==(ULONG_PTR)call_catch_block4;
 }
 
 static inline void find_catch_block4(EXCEPTION_RECORD *rec, CONTEXT *context,
@@ -610,7 +637,7 @@ static inline void find_catch_block4(EXCEPTION_RECORD *rec, CONTEXT *context,
             memset(&catch_record, 0, sizeof(catch_record));
             catch_record.ExceptionCode = STATUS_UNWIND_CONSOLIDATE;
             catch_record.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
-            catch_record.NumberParameters = 9;
+            catch_record.NumberParameters = CONSOLIDATE_UNWIND_PARAMETER_COUNT;
             catch_record.ExceptionInformation[0] = (ULONG_PTR)call_catch_block4;
             catch_record.ExceptionInformation[1] = orig_frame;
             catch_record.ExceptionInformation[2] = tryblock.catch_level;
@@ -620,9 +647,12 @@ static inline void find_catch_block4(EXCEPTION_RECORD *rec, CONTEXT *context,
                 (ULONG_PTR)rva_to_ptr(ci.handler, dispatch->ImageBase);
             catch_record.ExceptionInformation[6] = (ULONG_PTR)untrans_rec;
             catch_record.ExceptionInformation[7] = (ULONG_PTR)context;
-            if (ci.ret_addr)
+            if (ci.ret_addr[0])
                 catch_record.ExceptionInformation[8] = (ULONG_PTR)rva_to_ptr(
-                    ci.ret_addr + dispatch->FunctionEntry->BeginAddress, dispatch->ImageBase);
+                    ci.ret_addr[0] + dispatch->FunctionEntry->BeginAddress, dispatch->ImageBase);
+            if (ci.ret_addr[1])
+                catch_record.ExceptionInformation[9] = (ULONG_PTR)rva_to_ptr(
+                    ci.ret_addr[1] + dispatch->FunctionEntry->BeginAddress, dispatch->ImageBase);
             RtlUnwindEx((void*)frame, (void*)dispatch->ControlPc, &catch_record, NULL, &ctx, NULL);
         }
     }
-- 
2.29.2




More information about the wine-devel mailing list