Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-winehq
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-winehq
Commits
f42316c8
Commit
f42316c8
authored
Mar 12, 2024
by
Alexandre Julliard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ntdll: Port the RtlRestoreContext test to ARM.
parent
74d1dbb9
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
195 additions
and
16 deletions
+195
-16
signal_arm.c
dlls/ntdll/signal_arm.c
+16
-16
exception.c
dlls/ntdll/tests/exception.c
+179
-0
No files found.
dlls/ntdll/signal_arm.c
View file @
f42316c8
...
...
@@ -360,22 +360,18 @@ __ASM_GLOBAL_FUNC( KiUserCallbackDispatcher,
/**********************************************************************
* c
all_c
onsolidate_callback
* consolidate_callback
*
* Wrapper function to call a consolidate callback from a fake frame.
* If the callback executes RtlUnwindEx (like for example done in C++ handlers),
* we have to skip all frames which were already processed. To do that we
* trick the unwinding functions into thinking the call came from somewhere
* else. All CFI instructions are either DW_CFA_def_cfa_expression or
* DW_CFA_expression, and the expressions have the following format:
*
* DW_OP_breg13; sleb128 <OFFSET> | Load SP + struct member offset
* [DW_OP_deref] | Dereference, only for CFA
* else.
*/
extern
void
*
WINAPI
call_
consolidate_callback
(
CONTEXT
*
context
,
void
WINAPI
DECLSPEC_NORETURN
consolidate_callback
(
CONTEXT
*
context
,
void
*
(
CALLBACK
*
callback
)(
EXCEPTION_RECORD
*
),
EXCEPTION_RECORD
*
rec
);
__ASM_GLOBAL_FUNC
(
c
all_c
onsolidate_callback
,
__ASM_GLOBAL_FUNC
(
consolidate_callback
,
"push {r0-r2,lr}
\n\t
"
".seh_nop
\n\t
"
"sub sp, sp, #0x1a0
\n\t
"
...
...
@@ -386,14 +382,16 @@ __ASM_GLOBAL_FUNC( call_consolidate_callback,
".seh_nop
\n\t
"
"mov r2, #0x1a0
\n\t
"
".seh_nop_w
\n\t
"
"bl
"
__ASM_NAME
(
"memcpy"
)
"
\n\t
"
"bl
memcpy
\n\t
"
".seh_custom 0xee,0x02
\n\t
"
/* MSFT_OP_CONTEXT */
".seh_endprologue
\n\t
"
"ldrd r1, r2, [sp, #0x1a4]
\n\t
"
"mov r0, r2
\n\t
"
"blx r1
\n\t
"
"add sp, sp, #0x1ac
\n\t
"
"pop {pc}
\n\t
"
)
"str r0, [sp, #0x40]
\n\t
"
/* context->Pc */
"mov r0, sp
\n\t
"
"mov r1, #1
\n\t
"
"b NtContinue"
)
...
...
@@ -410,7 +408,7 @@ void CDECL RtlRestoreContext( CONTEXT *context, EXCEPTION_RECORD *rec )
memcpy
(
&
context
->
R4
,
&
jmp
->
R4
,
8
*
sizeof
(
DWORD
)
);
memcpy
(
&
context
->
D
[
8
],
&
jmp
->
D
[
0
],
8
*
sizeof
(
ULONGLONG
)
);
context
->
Lr
=
jmp
->
Pc
;
context
->
Pc
=
jmp
->
Pc
;
context
->
Sp
=
jmp
->
Sp
;
context
->
Fpscr
=
jmp
->
Fpscr
;
}
...
...
@@ -418,9 +416,7 @@ void CDECL RtlRestoreContext( CONTEXT *context, EXCEPTION_RECORD *rec )
{
PVOID
(
CALLBACK
*
consolidate
)(
EXCEPTION_RECORD
*
)
=
(
void
*
)
rec
->
ExceptionInformation
[
0
];
TRACE
(
"calling consolidate callback %p (rec=%p)
\n
"
,
consolidate
,
rec
);
rec
->
ExceptionInformation
[
10
]
=
(
ULONG_PTR
)
&
context
->
R4
;
context
->
Pc
=
(
DWORD
)
call_consolidate_callback
(
context
,
consolidate
,
rec
);
consolidate_callback
(
context
,
consolidate
,
rec
);
}
/* hack: remove no longer accessible TEB frames */
...
...
@@ -565,8 +561,12 @@ void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec
*
context
=
new_context
;
}
context
->
R0
=
(
DWORD
)
retval
;
if
(
rec
->
ExceptionCode
!=
STATUS_UNWIND_CONSOLIDATE
)
context
->
Pc
=
(
DWORD
)
target_ip
;
else
if
(
rec
->
ExceptionInformation
[
10
]
==
-
1
)
rec
->
ExceptionInformation
[
10
]
=
(
ULONG_PTR
)
&
nonvol_regs
;
context
->
R0
=
(
DWORD
)
retval
;
RtlRestoreContext
(
context
,
rec
);
}
...
...
dlls/ntdll/tests/exception.c
View file @
f42316c8
...
...
@@ -6220,6 +6220,184 @@ static void test_rtlraiseexception(void)
run_rtlraiseexception_test
(
EXCEPTION_INVALID_HANDLE
);
}
static
LONG
consolidate_dummy_called
;
static
LONG
pass
;
static
const
WORD
call_rtlunwind
[]
=
{
0xf8dd
,
0xc00c
,
/* ldr r12, [sp, #0xc] */
0xe8ac
,
0x0ff0
,
/* stm r12!, {r4-r11} */
0xec8c
,
0x8b10
,
/* vstm r12, {d8-d15} */
0xf8dd
,
0xc008
,
/* ldr r12, [sp, #0x8] */
0x4760
,
/* bx r12 */
};
static
PVOID
CALLBACK
test_consolidate_dummy
(
EXCEPTION_RECORD
*
rec
)
{
CONTEXT
*
ctx
=
(
CONTEXT
*
)
rec
->
ExceptionInformation
[
1
];
DWORD
*
saved_regs
=
(
DWORD
*
)
rec
->
ExceptionInformation
[
3
];
DWORD
*
regs
=
(
DWORD
*
)
rec
->
ExceptionInformation
[
10
];
int
i
;
switch
(
InterlockedIncrement
(
&
consolidate_dummy_called
))
{
case
1
:
/* RtlRestoreContext */
ok
(
ctx
->
Pc
==
0xdeadbeef
,
"RtlRestoreContext wrong Pc, expected: 0xdeadbeef, got: %lx
\n
"
,
ctx
->
Pc
);
ok
(
rec
->
ExceptionInformation
[
10
]
==
-
1
,
"wrong info %Ix
\n
"
,
rec
->
ExceptionInformation
[
10
]
);
break
;
case
2
:
/* RtlUnwindEx */
ok
(
ctx
->
Pc
!=
0xdeadbeef
,
"RtlUnwindEx wrong Pc, got: %lx
\n
"
,
ctx
->
Pc
);
ok
(
rec
->
ExceptionInformation
[
10
]
!=
-
1
,
"wrong info %Ix
\n
"
,
rec
->
ExceptionInformation
[
10
]
);
for
(
i
=
0
;
i
<
8
;
i
++
)
ok
(
saved_regs
[
i
]
==
regs
[
i
],
"wrong reg R%u, expected: %lx, got: %lx
\n
"
,
i
+
4
,
saved_regs
[
i
],
regs
[
i
]
);
regs
+=
8
;
saved_regs
+=
8
;
for
(
i
=
0
;
i
<
8
;
i
++
)
ok
(
((
DWORD64
*
)
saved_regs
)[
i
]
==
((
DWORD64
*
)
regs
)[
i
],
"wrong reg D%u, expected: %I64x, got: %I64x
\n
"
,
i
+
8
,
((
DWORD64
*
)
saved_regs
)[
i
],
((
DWORD64
*
)
regs
)[
i
]
);
break
;
}
return
(
PVOID
)
rec
->
ExceptionInformation
[
2
];
}
static
void
test_restore_context
(
void
)
{
EXCEPTION_RECORD
rec
;
_JUMP_BUFFER
buf
;
CONTEXT
ctx
;
int
i
;
if
(
!
pRtlUnwindEx
||
!
pRtlRestoreContext
||
!
pRtlCaptureContext
)
{
skip
(
"RtlUnwindEx/RtlCaptureContext/RtlRestoreContext not found
\n
"
);
return
;
}
/* test simple case of capture and restore context */
pass
=
0
;
InterlockedIncrement
(
&
pass
);
/* interlocked to prevent compiler from moving after capture */
pRtlCaptureContext
(
&
ctx
);
if
(
InterlockedIncrement
(
&
pass
)
==
2
)
/* interlocked to prevent compiler from moving before capture */
{
pRtlRestoreContext
(
&
ctx
,
NULL
);
ok
(
0
,
"shouldn't be reached
\n
"
);
}
else
ok
(
pass
<
4
,
"unexpected pass %ld
\n
"
,
pass
);
/* test with jmp using RtlRestoreContext */
pass
=
0
;
InterlockedIncrement
(
&
pass
);
RtlCaptureContext
(
&
ctx
);
InterlockedIncrement
(
&
pass
);
/* only called once */
setjmp
((
_JBTYPE
*
)
&
buf
);
InterlockedIncrement
(
&
pass
);
if
(
pass
==
3
)
{
rec
.
ExceptionCode
=
STATUS_LONGJUMP
;
rec
.
NumberParameters
=
1
;
rec
.
ExceptionInformation
[
0
]
=
(
DWORD
)
&
buf
;
/* uses buf.Pc instead of ctx.Pc */
pRtlRestoreContext
(
&
ctx
,
&
rec
);
ok
(
0
,
"shouldn't be reached
\n
"
);
}
else
if
(
pass
==
4
)
{
ok
(
buf
.
R4
==
ctx
.
R4
,
"longjmp failed for R4, expected: %lx, got: %lx
\n
"
,
buf
.
R4
,
ctx
.
R4
);
ok
(
buf
.
R5
==
ctx
.
R5
,
"longjmp failed for R5, expected: %lx, got: %lx
\n
"
,
buf
.
R5
,
ctx
.
R5
);
ok
(
buf
.
R6
==
ctx
.
R6
,
"longjmp failed for R6, expected: %lx, got: %lx
\n
"
,
buf
.
R6
,
ctx
.
R6
);
ok
(
buf
.
R7
==
ctx
.
R7
,
"longjmp failed for R7, expected: %lx, got: %lx
\n
"
,
buf
.
R7
,
ctx
.
R7
);
ok
(
buf
.
R8
==
ctx
.
R8
,
"longjmp failed for R8, expected: %lx, got: %lx
\n
"
,
buf
.
R8
,
ctx
.
R8
);
ok
(
buf
.
R9
==
ctx
.
R9
,
"longjmp failed for R9, expected: %lx, got: %lx
\n
"
,
buf
.
R9
,
ctx
.
R9
);
ok
(
buf
.
R10
==
ctx
.
R10
,
"longjmp failed for R10, expected: %lx, got: %lx
\n
"
,
buf
.
R10
,
ctx
.
R10
);
ok
(
buf
.
R11
==
ctx
.
R11
,
"longjmp failed for R11, expected: %lx, got: %lx
\n
"
,
buf
.
R11
,
ctx
.
R11
);
for
(
i
=
0
;
i
<
8
;
i
++
)
ok
(
buf
.
D
[
i
]
==
ctx
.
D
[
i
+
8
],
"longjmp failed for D%u, expected: %I64x, got: %I64x
\n
"
,
i
+
8
,
buf
.
D
[
i
],
ctx
.
D
[
i
+
8
]);
pRtlRestoreContext
(
&
ctx
,
&
rec
);
ok
(
0
,
"shouldn't be reached
\n
"
);
}
else
ok
(
pass
==
5
,
"unexpected pass %ld
\n
"
,
pass
);
/* test with jmp through RtlUnwindEx */
pass
=
0
;
InterlockedIncrement
(
&
pass
);
pRtlCaptureContext
(
&
ctx
);
InterlockedIncrement
(
&
pass
);
/* only called once */
setjmp
((
_JBTYPE
*
)
&
buf
);
InterlockedIncrement
(
&
pass
);
if
(
pass
==
3
)
{
rec
.
ExceptionCode
=
STATUS_LONGJUMP
;
rec
.
NumberParameters
=
1
;
rec
.
ExceptionInformation
[
0
]
=
(
DWORD
)
&
buf
;
/* uses buf.Pc instead of bogus 0xdeadbeef */
pRtlUnwindEx
((
void
*
)
buf
.
Sp
,
(
void
*
)
0xdeadbeef
,
&
rec
,
NULL
,
&
ctx
,
NULL
);
ok
(
0
,
"shouldn't be reached
\n
"
);
}
else
ok
(
pass
==
4
,
"unexpected pass %ld
\n
"
,
pass
);
/* test with consolidate */
pass
=
0
;
InterlockedIncrement
(
&
pass
);
RtlCaptureContext
(
&
ctx
);
InterlockedIncrement
(
&
pass
);
if
(
pass
==
2
)
{
rec
.
ExceptionCode
=
STATUS_UNWIND_CONSOLIDATE
;
rec
.
NumberParameters
=
3
;
rec
.
ExceptionInformation
[
0
]
=
(
DWORD
)
test_consolidate_dummy
;
rec
.
ExceptionInformation
[
1
]
=
(
DWORD
)
&
ctx
;
rec
.
ExceptionInformation
[
2
]
=
ctx
.
Pc
;
rec
.
ExceptionInformation
[
10
]
=
-
1
;
ctx
.
Pc
=
0xdeadbeef
;
pRtlRestoreContext
(
&
ctx
,
&
rec
);
ok
(
0
,
"shouldn't be reached
\n
"
);
}
else
if
(
pass
==
3
)
ok
(
consolidate_dummy_called
==
1
,
"test_consolidate_dummy not called
\n
"
);
else
ok
(
0
,
"unexpected pass %ld
\n
"
,
pass
);
/* test with consolidate through RtlUnwindEx */
pass
=
0
;
InterlockedIncrement
(
&
pass
);
pRtlCaptureContext
(
&
ctx
);
InterlockedIncrement
(
&
pass
);
if
(
pass
==
2
)
{
void
(
*
func
)(
DWORD
,
DWORD
,
EXCEPTION_RECORD
*
,
DWORD
,
CONTEXT
*
,
void
*
,
void
*
,
void
*
);
DWORD64
nonvol_regs
[
12
];
func
=
(
void
*
)((
ULONG_PTR
)
code_mem
|
1
);
/* thumb */
rec
.
ExceptionCode
=
STATUS_UNWIND_CONSOLIDATE
;
rec
.
NumberParameters
=
4
;
rec
.
ExceptionInformation
[
0
]
=
(
DWORD
)
test_consolidate_dummy
;
rec
.
ExceptionInformation
[
1
]
=
(
DWORD
)
&
ctx
;
rec
.
ExceptionInformation
[
2
]
=
ctx
.
Pc
;
rec
.
ExceptionInformation
[
3
]
=
(
DWORD
)
&
nonvol_regs
;
rec
.
ExceptionInformation
[
10
]
=
-
1
;
/* otherwise it doesn't get set */
ctx
.
Pc
=
0xdeadbeef
;
/* uses consolidate callback Pc instead of bogus 0xdeadbeef */
memcpy
(
code_mem
,
call_rtlunwind
,
sizeof
(
call_rtlunwind
)
);
FlushInstructionCache
(
GetCurrentProcess
(),
code_mem
,
sizeof
(
call_rtlunwind
)
);
func
(
buf
.
Frame
,
0xdeadbeef
,
&
rec
,
0
,
&
ctx
,
NULL
,
pRtlUnwindEx
,
nonvol_regs
);
ok
(
0
,
"shouldn't be reached
\n
"
);
}
else
if
(
pass
==
3
)
ok
(
consolidate_dummy_called
==
2
,
"test_consolidate_dummy not called
\n
"
);
else
ok
(
0
,
"unexpected pass %ld
\n
"
,
pass
);
}
#elif defined(__aarch64__)
static
void
test_thread_context
(
void
)
...
...
@@ -10791,6 +10969,7 @@ START_TEST(exception)
test_nested_exception
();
test_collided_unwind
();
test_restore_context
();
#endif
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment