[PATCH v4 2/4] ntdll: Implement RtlWalkFrameChain() for x86 and x86_64.

Paul Gofman pgofman at codeweavers.com
Mon Nov 1 06:13:58 CDT 2021


Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
v4:
    - use helper common with RtlCaptureStackBackTrace() and avoid using temporary buffer.

 dlls/ntdll/exception.c              | 24 ++++++++-
 dlls/ntdll/ntdll.spec               |  2 +-
 dlls/ntdll/signal_i386.c            | 17 +++---
 dlls/ntdll/signal_x86_64.c          |  7 +--
 dlls/ntdll/tests/exception.c        | 83 +++++++++++++++++++++++++++++
 dlls/ntoskrnl.exe/ntoskrnl.exe.spec |  2 +-
 include/ddk/ntddk.h                 |  3 ++
 include/winnt.h                     |  2 +
 8 files changed, 124 insertions(+), 16 deletions(-)

diff --git a/dlls/ntdll/exception.c b/dlls/ntdll/exception.c
index 02bf672f8fb..ea0d0df6694 100644
--- a/dlls/ntdll/exception.c
+++ b/dlls/ntdll/exception.c
@@ -30,6 +30,7 @@
 #define WIN32_NO_STATUS
 #include "windef.h"
 #include "winternl.h"
+#include "ddk/ntddk.h"
 #include "ddk/wdm.h"
 #include "wine/exception.h"
 #include "wine/list.h"
@@ -1074,6 +1075,25 @@ USHORT WINAPI RtlCaptureStackBackTrace( ULONG skip, ULONG count, PVOID *buffer,
 {
     ULONG ret;
 
-    ret = capture_stack_back_trace( skip, count, buffer, hash );
-    return min( ret, ~(USHORT)0 );
+    ret = capture_stack_back_trace( skip, skip + count, buffer, hash );
+    if (ret < skip) return 0;
+    return min( ret - skip, ~(USHORT)0 );
+}
+
+
+/**********************************************************************
+ *              RtlWalkFrameChain      (NTDLL.@)
+ */
+ULONG WINAPI RtlWalkFrameChain( void **callers, ULONG count, ULONG skip )
+{
+    TRACE( "callers %p, count %u, skip %#x.\n", callers, count, skip );
+
+    if (skip & ~(0xff << RTL_STACK_WALKING_MODE_FRAMES_TO_SKIP_SHIFT))
+    {
+        WARN( "Invalid flags %#x.\n", skip );
+        return 0;
+    }
+    skip >>= RTL_STACK_WALKING_MODE_FRAMES_TO_SKIP_SHIFT;
+
+    return capture_stack_back_trace( skip, count, callers, NULL );
 }
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index 13e65f65139..c54b2e205aa 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -1073,7 +1073,7 @@
 @ stdcall RtlWakeAddressSingle(ptr)
 @ stdcall RtlWakeAllConditionVariable(ptr)
 @ stdcall RtlWakeConditionVariable(ptr)
-@ stub RtlWalkFrameChain
+@ stdcall RtlWalkFrameChain(ptr long long)
 @ stdcall RtlWalkHeap(long ptr)
 @ stdcall RtlWow64EnableFsRedirection(long)
 @ stdcall RtlWow64EnableFsRedirectionEx(long ptr)
diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index 26150ce877b..ff3e837c784 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -488,27 +488,26 @@ ULONG WINAPI capture_stack_back_trace( ULONG skip, ULONG count, PVOID *buffer, U
     CONTEXT context;
     ULONG i;
     ULONG *frame;
+    ULONG num_entries = 0;
 
     ++skip;
+    ++count;
 
     RtlCaptureContext( &context );
     if (hash) *hash = 0;
     frame = (ULONG *)context.Ebp;
 
-    while (skip--)
-    {
-        if (!is_valid_frame( frame )) return 0;
-        frame = (ULONG *)*frame;
-    }
-
     for (i = 0; i < count; i++)
     {
         if (!is_valid_frame( frame )) break;
-        buffer[i] = (void *)frame[1];
-        if (hash) *hash += frame[1];
+        if (i >= skip)
+        {
+            buffer[num_entries++] = (void *)frame[1];
+            if (hash) *hash += frame[1];
+        }
         frame = (ULONG *)*frame;
     }
-    return i;
+    return i ? i - 1 : 0;
 }
 
 
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c
index 5f3c8f70208..4a9c7d4b9ff 100644
--- a/dlls/ntdll/signal_x86_64.c
+++ b/dlls/ntdll/signal_x86_64.c
@@ -1504,16 +1504,17 @@ ULONG WINAPI capture_stack_back_trace( ULONG skip, ULONG count, PVOID *buffer, U
     TRACE( "(%u, %u, %p, %p)\n", skip, count, buffer, hash );
 
     ++skip;
+    ++count;
 
     RtlCaptureContext( &context );
     dispatch.TargetIp      = 0;
     dispatch.ContextRecord = &context;
     dispatch.HistoryTable  = &table;
     if (hash) *hash = 0;
-    for (i = 0; i < skip + count; i++)
+    for (i = 0; i < count; i++)
     {
         status = virtual_unwind( UNW_FLAG_NHANDLER, &dispatch, &context );
-        if (status != STATUS_SUCCESS) return i;
+        if (status != STATUS_SUCCESS) break;
 
         if (!dispatch.EstablisherFrame) break;
 
@@ -1532,7 +1533,7 @@ ULONG WINAPI capture_stack_back_trace( ULONG skip, ULONG count, PVOID *buffer, U
     }
     if (hash && num_entries > 0) *hash = hash_pointers( buffer, num_entries );
     TRACE( "captured %u frames\n", num_entries );
-    return num_entries;
+    return i ? i - 1 : 0;
 }
 
 
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index 6af68317732..d73e75ee529 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -28,6 +28,7 @@
 #include "winnt.h"
 #include "winreg.h"
 #include "winternl.h"
+#include "ddk/ntddk.h"
 #include "ddk/wdm.h"
 #include "excpt.h"
 #include "wine/test.h"
@@ -9113,6 +9114,87 @@ static void test_copy_context(void)
 }
 #endif
 
+static void test_walk_stack(void)
+{
+    ULONG count, expected, frame_count, requested_count, skip_count;
+    void *addrs[256], *addrs2[256];
+    void *start, *end;
+    unsigned int i, j;
+
+    memset(addrs, 0xcc, sizeof(addrs));
+    memset(addrs2, 0xcc, sizeof(addrs2));
+
+    frame_count = RtlCaptureStackBackTrace(0, ARRAY_SIZE(addrs), addrs, NULL);
+    count = RtlWalkFrameChain(addrs2, ARRAY_SIZE(addrs2), 0);
+
+    trace("frame_count %u.\n", frame_count);
+
+    ok(frame_count > 1, "Got zero frame_count.\n");
+    ok(count == frame_count, "Got unexpected frame_count %u, count %u.\n", frame_count, count);
+
+    start = test_walk_stack;
+    end = (BYTE *)start + 0x1000;
+    todo_wine_if(sizeof(void *) == 4)
+    ok(addrs[0] >= start && addrs[0] < end, "Address is not inside test function, start %p, end %p, addr %p.\n",
+            start, end, addrs[0]);
+    todo_wine_if(sizeof(void *) == 4)
+    ok(addrs2[0] >= start && addrs2[0] < end, "Address is not inside test function, start %p, end %p, addr %p.\n",
+            start, end, addrs2[0]);
+
+    for (i = 1; i < frame_count; ++i)
+    {
+        ok(addrs[i] == addrs2[i], "i %u, addresses do not match, %p vs %p.\n", i, addrs[i], addrs2[i]);
+    }
+    todo_wine ok(!!addrs[frame_count - 1], "Expected non-NULL last address.\n");
+
+    for (requested_count = frame_count - 1; requested_count < frame_count + 1; ++requested_count)
+    {
+        for (i = 0; i < 32; ++i)
+        {
+            winetest_push_context("requested_count %u, i %u", requested_count, i);
+            skip_count = (1 << i) >> 8;
+
+            if (i < RTL_STACK_WALKING_MODE_FRAMES_TO_SKIP_SHIFT
+                    || i >= RTL_STACK_WALKING_MODE_FRAMES_TO_SKIP_SHIFT + 8)
+                expected = 0;
+            else
+                expected = min(frame_count, requested_count);
+
+            memset(addrs2, 0xcc, sizeof(addrs2));
+            count = RtlWalkFrameChain(addrs2, requested_count, 1 << i);
+            ok(count == expected, "Got unexpected frame_count %u, expected %u.\n", count, expected);
+
+            if (skip_count < count)
+                count -= skip_count;
+            else
+                count = 0;
+
+            for (j = 0; j < count; ++j)
+                ok( addrs2[j] != (void *)(ULONG_PTR)0xcccccccccccccccc, "Address is not set, j %u.\n", j );
+            for (; j < ARRAY_SIZE(addrs2); ++j)
+                ok( addrs2[j] == (void *)(ULONG_PTR)0xcccccccccccccccc, "Address is set, j %u.\n", j );
+
+            if (!count)
+            {
+                winetest_pop_context();
+                continue;
+            }
+
+            memset(addrs, 0xcc, sizeof(addrs));
+            expected = skip_count > frame_count ? 0 : min(frame_count - skip_count, requested_count);
+            count = RtlCaptureStackBackTrace(skip_count, requested_count, addrs, NULL);
+            ok(count == expected, "Got unexpected frame_count %u, expected %u. i %u.\n", count, expected, i);
+
+            count = min(frame_count, requested_count) - skip_count;
+            for (j = 0; j < count; ++j)
+            {
+                ok(addrs[j] == addrs2[j], "Addresses do not match, j %u, %p, %p.\n", j, addrs[j], addrs2[j]);
+            }
+            winetest_pop_context();
+        }
+    }
+}
+
 START_TEST(exception)
 {
     HMODULE hntdll = GetModuleHandleA("ntdll.dll");
@@ -9339,5 +9421,6 @@ START_TEST(exception)
     test_suspend_thread();
     test_suspend_process();
     test_unload_trace();
+    test_walk_stack();
     VirtualFree(code_mem, 0, MEM_RELEASE);
 }
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
index 0208e2f633f..b94e58ccb26 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
+++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
@@ -1300,7 +1300,7 @@
 @ stdcall RtlVerifyVersionInfo(ptr long int64)
 @ stdcall -arch=arm64,x86_64 RtlVirtualUnwind(long long long ptr ptr ptr ptr ptr)
 @ stub RtlVolumeDeviceToDosName
-@ stub RtlWalkFrameChain
+@ stdcall RtlWalkFrameChain(ptr long long)
 @ stdcall RtlWriteRegistryValue(long ptr ptr long ptr long)
 @ stub RtlZeroHeap
 @ stdcall RtlZeroMemory(ptr long)
diff --git a/include/ddk/ntddk.h b/include/ddk/ntddk.h
index 41ad3d721bd..ce558139aa6 100644
--- a/include/ddk/ntddk.h
+++ b/include/ddk/ntddk.h
@@ -245,6 +245,8 @@ typedef EXPAND_STACK_CALLOUT *PEXPAND_STACK_CALLOUT;
 typedef GUID UUID;
 #endif
 
+#define RTL_STACK_WALKING_MODE_FRAMES_TO_SKIP_SHIFT 8
+
 NTSTATUS  WINAPI ExUuidCreate(UUID*);
 NTSTATUS  WINAPI IoQueryDeviceDescription(PINTERFACE_TYPE,PULONG,PCONFIGURATION_TYPE,PULONG,
                                   PCONFIGURATION_TYPE,PULONG,PIO_QUERY_DEVICE_ROUTINE,PVOID);
@@ -267,5 +269,6 @@ NTSTATUS  WINAPI PsSetCreateThreadNotifyRoutine(PCREATE_THREAD_NOTIFY_ROUTINE);
 NTSTATUS  WINAPI PsSetLoadImageNotifyRoutine(PLOAD_IMAGE_NOTIFY_ROUTINE);
 void      WINAPI RtlInitializeGenericTableAvl(PRTL_AVL_TABLE,PRTL_AVL_COMPARE_ROUTINE,PRTL_AVL_ALLOCATE_ROUTINE, PRTL_AVL_FREE_ROUTINE,void *);
 void      WINAPI RtlInsertElementGenericTableAvl(PRTL_AVL_TABLE,void *,ULONG,BOOL*);
+ULONG     WINAPI RtlWalkFrameChain(void **,ULONG,ULONG);
 
 #endif
diff --git a/include/winnt.h b/include/winnt.h
index ef731e29c52..eb1bd165720 100644
--- a/include/winnt.h
+++ b/include/winnt.h
@@ -1840,6 +1840,8 @@ NTSYSAPI PVOID   WINAPI RtlVirtualUnwind(DWORD,ULONG_PTR,ULONG_PTR,RUNTIME_FUNCT
 
 #endif
 
+NTSYSAPI USHORT  WINAPI RtlCaptureStackBackTrace(ULONG,ULONG,void **,ULONG *);
+
 /*
  * Product types
  */
-- 
2.31.1




More information about the wine-devel mailing list