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
a5a25382
Commit
a5a25382
authored
Feb 14, 2024
by
Alexandre Julliard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ntdll: Move exception unwinding code to a separate file.
parent
d88f92eb
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
1686 additions
and
1793 deletions
+1686
-1793
Makefile.in
dlls/ntdll/Makefile.in
+1
-0
signal_arm.c
dlls/ntdll/signal_arm.c
+0
-626
signal_arm64.c
dlls/ntdll/signal_arm64.c
+0
-537
signal_arm64ec.c
dlls/ntdll/signal_arm64ec.c
+0
-100
signal_x86_64.c
dlls/ntdll/signal_x86_64.c
+0
-530
unwind.c
dlls/ntdll/unwind.c
+1685
-0
No files found.
dlls/ntdll/Makefile.in
View file @
a5a25382
...
...
@@ -66,6 +66,7 @@ SOURCES = \
unix/tape.c
\
unix/thread.c
\
unix/virtual.c
\
unwind.c
\
version.c
\
version.rc
\
wcstring.c
...
...
dlls/ntdll/signal_arm.c
View file @
a5a25382
...
...
@@ -586,589 +586,6 @@ __ASM_GLOBAL_FUNC( KiUserCallbackDispatcher,
"udf #1"
)
/***********************************************************************
* Definitions for Win32 unwind tables
*/
struct
unwind_info
{
DWORD
function_length
:
18
;
DWORD
version
:
2
;
DWORD
x
:
1
;
DWORD
e
:
1
;
DWORD
f
:
1
;
DWORD
epilog
:
5
;
DWORD
codes
:
4
;
};
struct
unwind_info_ext
{
WORD
epilog
;
BYTE
codes
;
BYTE
reserved
;
};
struct
unwind_info_epilog
{
DWORD
offset
:
18
;
DWORD
res
:
2
;
DWORD
cond
:
4
;
DWORD
index
:
8
;
};
static
const
BYTE
unwind_code_len
[
256
]
=
{
/* 00 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 20 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 40 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 60 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 80 */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* a0 */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* c0 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* e0 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
3
,
4
,
3
,
4
,
1
,
1
,
1
,
1
,
1
};
static
const
BYTE
unwind_instr_len
[
256
]
=
{
/* 00 */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* 20 */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* 40 */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* 60 */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* 80 */
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
/* a0 */
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
/* c0 */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
/* e0 */
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
2
,
2
,
0
,
4
,
0
,
0
,
0
,
0
,
0
,
4
,
4
,
2
,
2
,
4
,
4
,
2
,
4
,
2
,
4
,
0
};
/***********************************************************************
* get_sequence_len
*/
static
unsigned
int
get_sequence_len
(
BYTE
*
ptr
,
BYTE
*
end
,
int
include_end
)
{
unsigned
int
ret
=
0
;
while
(
ptr
<
end
)
{
if
(
*
ptr
>=
0xfd
)
{
if
(
*
ptr
<=
0xfe
&&
include_end
)
ret
+=
unwind_instr_len
[
*
ptr
];
break
;
}
ret
+=
unwind_instr_len
[
*
ptr
];
ptr
+=
unwind_code_len
[
*
ptr
];
}
return
ret
;
}
/***********************************************************************
* pop_regs_mask
*/
static
void
pop_regs_mask
(
int
mask
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
int
i
;
for
(
i
=
0
;
i
<=
12
;
i
++
)
{
if
(
!
(
mask
&
(
1
<<
i
)))
continue
;
if
(
ptrs
&&
i
>=
4
&&
i
<=
11
)
(
&
ptrs
->
R4
)[
i
-
4
]
=
(
DWORD
*
)
context
->
Sp
;
if
(
i
>=
4
)
(
&
context
->
R0
)[
i
]
=
*
(
DWORD
*
)
context
->
Sp
;
context
->
Sp
+=
4
;
}
}
/***********************************************************************
* pop_regs_range
*/
static
void
pop_regs_range
(
int
last
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
int
i
;
for
(
i
=
4
;
i
<=
last
;
i
++
)
{
if
(
ptrs
)
(
&
ptrs
->
R4
)[
i
-
4
]
=
(
DWORD
*
)
context
->
Sp
;
(
&
context
->
R0
)[
i
]
=
*
(
DWORD
*
)
context
->
Sp
;
context
->
Sp
+=
4
;
}
}
/***********************************************************************
* pop_lr
*/
static
void
pop_lr
(
int
increment
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
if
(
ptrs
)
ptrs
->
Lr
=
(
DWORD
*
)
context
->
Sp
;
context
->
Lr
=
*
(
DWORD
*
)
context
->
Sp
;
context
->
Sp
+=
increment
;
}
/***********************************************************************
* pop_fpregs_range
*/
static
void
pop_fpregs_range
(
int
first
,
int
last
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
int
i
;
for
(
i
=
first
;
i
<=
last
;
i
++
)
{
if
(
ptrs
&&
i
>=
8
&&
i
<=
15
)
(
&
ptrs
->
D8
)[
i
-
8
]
=
(
ULONGLONG
*
)
context
->
Sp
;
context
->
D
[
i
]
=
*
(
ULONGLONG
*
)
context
->
Sp
;
context
->
Sp
+=
8
;
}
}
/***********************************************************************
* ms_opcode
*/
static
void
ms_opcode
(
BYTE
opcode
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
switch
(
opcode
)
{
case
1
:
/* MSFT_OP_MACHINE_FRAME */
context
->
Pc
=
((
DWORD
*
)
context
->
Sp
)[
1
];
context
->
Sp
=
((
DWORD
*
)
context
->
Sp
)[
0
];
context
->
ContextFlags
&=
~
CONTEXT_UNWOUND_TO_CALL
;
break
;
case
2
:
/* MSFT_OP_CONTEXT */
{
int
i
;
CONTEXT
*
src
=
(
CONTEXT
*
)
context
->
Sp
;
*
context
=
*
src
;
if
(
!
ptrs
)
break
;
for
(
i
=
0
;
i
<
8
;
i
++
)
(
&
ptrs
->
R4
)[
i
]
=
&
src
->
R4
+
i
;
ptrs
->
Lr
=
&
src
->
Lr
;
for
(
i
=
0
;
i
<
8
;
i
++
)
(
&
ptrs
->
D8
)[
i
]
=
&
src
->
D
[
i
+
8
];
break
;
}
default:
WARN
(
"unsupported code %02x
\n
"
,
opcode
);
break
;
}
}
/***********************************************************************
* process_unwind_codes
*/
static
void
process_unwind_codes
(
BYTE
*
ptr
,
BYTE
*
end
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
,
int
skip
)
{
unsigned
int
val
,
len
;
unsigned
int
i
;
/* skip codes */
while
(
ptr
<
end
&&
skip
)
{
if
(
*
ptr
>=
0xfd
)
break
;
skip
-=
unwind_instr_len
[
*
ptr
];
ptr
+=
unwind_code_len
[
*
ptr
];
}
while
(
ptr
<
end
)
{
len
=
unwind_code_len
[
*
ptr
];
if
(
ptr
+
len
>
end
)
break
;
val
=
0
;
for
(
i
=
0
;
i
<
len
;
i
++
)
val
=
(
val
<<
8
)
|
ptr
[
i
];
if
(
*
ptr
<=
0x7f
)
/* add sp, sp, #x */
context
->
Sp
+=
4
*
(
val
&
0x7f
);
else
if
(
*
ptr
<=
0xbf
)
/* pop {r0-r12,lr} */
{
pop_regs_mask
(
val
&
0x1fff
,
context
,
ptrs
);
if
(
val
&
0x2000
)
pop_lr
(
4
,
context
,
ptrs
);
}
else
if
(
*
ptr
<=
0xcf
)
/* mov sp, rX */
context
->
Sp
=
(
&
context
->
R0
)[
val
&
0x0f
];
else
if
(
*
ptr
<=
0xd7
)
/* pop {r4-rX,lr} */
{
pop_regs_range
(
(
val
&
0x03
)
+
4
,
context
,
ptrs
);
if
(
val
&
0x04
)
pop_lr
(
4
,
context
,
ptrs
);
}
else
if
(
*
ptr
<=
0xdf
)
/* pop {r4-rX,lr} */
{
pop_regs_range
(
(
val
&
0x03
)
+
8
,
context
,
ptrs
);
if
(
val
&
0x04
)
pop_lr
(
4
,
context
,
ptrs
);
}
else
if
(
*
ptr
<=
0xe7
)
/* vpop {d8-dX} */
pop_fpregs_range
(
8
,
(
val
&
0x07
)
+
8
,
context
,
ptrs
);
else
if
(
*
ptr
<=
0xeb
)
/* add sp, sp, #x */
context
->
Sp
+=
4
*
(
val
&
0x3ff
);
else
if
(
*
ptr
<=
0xed
)
/* pop {r0-r12,lr} */
{
pop_regs_mask
(
val
&
0xff
,
context
,
ptrs
);
if
(
val
&
0x100
)
pop_lr
(
4
,
context
,
ptrs
);
}
else
if
(
*
ptr
<=
0xee
)
/* Microsoft-specific 0x00-0x0f, Available 0x10-0xff */
ms_opcode
(
val
&
0xff
,
context
,
ptrs
);
else
if
(
*
ptr
<=
0xef
&&
((
val
&
0xff
)
<=
0x0f
))
/* ldr lr, [sp], #x */
pop_lr
(
4
*
(
val
&
0x0f
),
context
,
ptrs
);
else
if
(
*
ptr
<=
0xf4
)
/* Available */
WARN
(
"unsupported code %02x
\n
"
,
*
ptr
);
else
if
(
*
ptr
<=
0xf5
)
/* vpop {dS-dE} */
pop_fpregs_range
(
(
val
&
0xf0
)
>>
4
,
(
val
&
0x0f
),
context
,
ptrs
);
else
if
(
*
ptr
<=
0xf6
)
/* vpop {dS-dE} */
pop_fpregs_range
(
((
val
&
0xf0
)
>>
4
)
+
16
,
(
val
&
0x0f
)
+
16
,
context
,
ptrs
);
else
if
(
*
ptr
==
0xf7
||
*
ptr
==
0xf9
)
/* add sp, sp, #x */
context
->
Sp
+=
4
*
(
val
&
0xffff
);
else
if
(
*
ptr
==
0xf8
||
*
ptr
==
0xfa
)
/* add sp, sp, #x */
context
->
Sp
+=
4
*
(
val
&
0xffffff
);
else
if
(
*
ptr
<=
0xfc
)
/* nop */
/* nop */
;
else
/* end */
break
;
ptr
+=
len
;
}
}
/***********************************************************************
* unwind_packed_data
*/
static
void
*
unwind_packed_data
(
ULONG_PTR
base
,
ULONG_PTR
pc
,
RUNTIME_FUNCTION
*
func
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
int
i
,
pos
=
0
;
int
pf
=
0
,
ef
=
0
,
fpoffset
=
0
,
stack
=
func
->
StackAdjust
;
int
prologue_regmask
=
0
;
int
epilogue_regmask
=
0
;
unsigned
int
offset
,
len
;
BYTE
prologue
[
10
],
*
prologue_end
,
epilogue
[
20
],
*
epilogue_end
;
TRACE
(
"function %lx-%lx: len=%#x flag=%x ret=%u H=%u reg=%u R=%u L=%u C=%u stackadjust=%x
\n
"
,
base
+
func
->
BeginAddress
,
base
+
func
->
BeginAddress
+
func
->
FunctionLength
*
2
,
func
->
FunctionLength
,
func
->
Flag
,
func
->
Ret
,
func
->
H
,
func
->
Reg
,
func
->
R
,
func
->
L
,
func
->
C
,
func
->
StackAdjust
);
offset
=
(
pc
-
base
)
-
func
->
BeginAddress
;
if
(
func
->
StackAdjust
>=
0x03f4
)
{
pf
=
func
->
StackAdjust
&
0x04
;
ef
=
func
->
StackAdjust
&
0x08
;
stack
=
(
func
->
StackAdjust
&
3
)
+
1
;
}
if
(
!
func
->
R
||
pf
)
{
int
first
=
4
,
last
=
func
->
Reg
+
4
;
if
(
pf
)
{
first
=
(
~
func
->
StackAdjust
)
&
3
;
if
(
func
->
R
)
last
=
3
;
}
for
(
i
=
first
;
i
<=
last
;
i
++
)
prologue_regmask
|=
1
<<
i
;
fpoffset
=
last
+
1
-
first
;
}
if
(
!
func
->
R
||
ef
)
{
int
first
=
4
,
last
=
func
->
Reg
+
4
;
if
(
ef
)
{
first
=
(
~
func
->
StackAdjust
)
&
3
;
if
(
func
->
R
)
last
=
3
;
}
for
(
i
=
first
;
i
<=
last
;
i
++
)
epilogue_regmask
|=
1
<<
i
;
}
if
(
func
->
C
)
{
prologue_regmask
|=
1
<<
11
;
epilogue_regmask
|=
1
<<
11
;
}
if
(
func
->
L
)
{
prologue_regmask
|=
1
<<
14
;
/* lr */
if
(
func
->
Ret
!=
0
)
epilogue_regmask
|=
1
<<
14
;
/* lr */
else
if
(
!
func
->
H
)
epilogue_regmask
|=
1
<<
15
;
/* pc */
}
/* Synthesize prologue opcodes */
if
(
stack
&&
!
pf
)
{
if
(
stack
<=
0x7f
)
{
prologue
[
pos
++
]
=
stack
;
/* sub sp, sp, #x */
}
else
{
prologue
[
pos
++
]
=
0xe8
|
(
stack
>>
8
);
/* sub.w sp, sp, #x */
prologue
[
pos
++
]
=
stack
&
0xff
;
}
}
if
(
func
->
R
&&
func
->
Reg
!=
7
)
prologue
[
pos
++
]
=
0xe0
|
func
->
Reg
;
/* vpush {d8-dX} */
if
(
func
->
C
&&
fpoffset
==
0
)
prologue
[
pos
++
]
=
0xfb
;
/* mov r11, sp - handled as nop16 */
else
if
(
func
->
C
)
prologue
[
pos
++
]
=
0xfc
;
/* add r11, sp, #x - handled as nop32 */
if
(
prologue_regmask
&
0xf00
)
/* r8-r11 set */
{
int
bitmask
=
prologue_regmask
&
0x1fff
;
if
(
prologue_regmask
&
(
1
<<
14
))
/* lr */
bitmask
|=
0x2000
;
prologue
[
pos
++
]
=
0x80
|
(
bitmask
>>
8
);
/* push.w {r0-r12,lr} */
prologue
[
pos
++
]
=
bitmask
&
0xff
;
}
else
if
(
prologue_regmask
)
/* r0-r7, lr set */
{
int
bitmask
=
prologue_regmask
&
0xff
;
if
(
prologue_regmask
&
(
1
<<
14
))
/* lr */
bitmask
|=
0x100
;
prologue
[
pos
++
]
=
0xec
|
(
bitmask
>>
8
);
/* push {r0-r7,lr} */
prologue
[
pos
++
]
=
bitmask
&
0xff
;
}
if
(
func
->
H
)
prologue
[
pos
++
]
=
0x04
;
/* push {r0-r3} - handled as sub sp, sp, #16 */
prologue
[
pos
++
]
=
0xff
;
/* end */
prologue_end
=
&
prologue
[
pos
];
/* Synthesize epilogue opcodes */
pos
=
0
;
if
(
stack
&&
!
ef
)
{
if
(
stack
<=
0x7f
)
{
epilogue
[
pos
++
]
=
stack
;
/* sub sp, sp, #x */
}
else
{
epilogue
[
pos
++
]
=
0xe8
|
(
stack
>>
8
);
/* sub.w sp, sp, #x */
epilogue
[
pos
++
]
=
stack
&
0xff
;
}
}
if
(
func
->
R
&&
func
->
Reg
!=
7
)
epilogue
[
pos
++
]
=
0xe0
|
func
->
Reg
;
/* vpush {d8-dX} */
if
(
epilogue_regmask
&
0x7f00
)
/* r8-r11, lr set */
{
int
bitmask
=
epilogue_regmask
&
0x1fff
;
if
(
epilogue_regmask
&
(
3
<<
14
))
/* lr or pc */
bitmask
|=
0x2000
;
epilogue
[
pos
++
]
=
0x80
|
(
bitmask
>>
8
);
/* push.w {r0-r12,lr} */
epilogue
[
pos
++
]
=
bitmask
&
0xff
;
}
else
if
(
epilogue_regmask
)
/* r0-r7, pc set */
{
int
bitmask
=
epilogue_regmask
&
0xff
;
if
(
epilogue_regmask
&
(
1
<<
15
))
/* pc */
bitmask
|=
0x100
;
/* lr */
epilogue
[
pos
++
]
=
0xec
|
(
bitmask
>>
8
);
/* push {r0-r7,lr} */
epilogue
[
pos
++
]
=
bitmask
&
0xff
;
}
if
(
func
->
H
&&
!
(
func
->
L
&&
func
->
Ret
==
0
))
epilogue
[
pos
++
]
=
0x04
;
/* add sp, sp, #16 */
else
if
(
func
->
H
&&
(
func
->
L
&&
func
->
Ret
==
0
))
{
epilogue
[
pos
++
]
=
0xef
;
/* ldr lr, [sp], #20 */
epilogue
[
pos
++
]
=
5
;
}
if
(
func
->
Ret
==
1
)
epilogue
[
pos
++
]
=
0xfd
;
/* bx lr */
else
if
(
func
->
Ret
==
2
)
epilogue
[
pos
++
]
=
0xfe
;
/* b address */
else
epilogue
[
pos
++
]
=
0xff
;
/* end */
epilogue_end
=
&
epilogue
[
pos
];
if
(
func
->
Flag
==
1
&&
offset
<
4
*
(
prologue_end
-
prologue
))
{
/* Check prologue */
len
=
get_sequence_len
(
prologue
,
prologue_end
,
0
);
if
(
offset
<
len
)
{
process_unwind_codes
(
prologue
,
prologue_end
,
context
,
ptrs
,
len
-
offset
);
return
NULL
;
}
}
if
(
func
->
Ret
!=
3
&&
2
*
func
->
FunctionLength
-
offset
<=
4
*
(
epilogue_end
-
epilogue
))
{
/* Check epilogue */
len
=
get_sequence_len
(
epilogue
,
epilogue_end
,
1
);
if
(
offset
>=
2
*
func
->
FunctionLength
-
len
)
{
process_unwind_codes
(
epilogue
,
epilogue_end
,
context
,
ptrs
,
offset
-
(
2
*
func
->
FunctionLength
-
len
)
);
return
NULL
;
}
}
/* Execute full prologue */
process_unwind_codes
(
prologue
,
prologue_end
,
context
,
ptrs
,
0
);
return
NULL
;
}
/***********************************************************************
* unwind_full_data
*/
static
void
*
unwind_full_data
(
ULONG_PTR
base
,
ULONG_PTR
pc
,
RUNTIME_FUNCTION
*
func
,
CONTEXT
*
context
,
PVOID
*
handler_data
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
struct
unwind_info
*
info
;
struct
unwind_info_epilog
*
info_epilog
;
unsigned
int
i
,
codes
,
epilogs
,
len
,
offset
;
void
*
data
;
BYTE
*
end
;
info
=
(
struct
unwind_info
*
)((
char
*
)
base
+
func
->
UnwindData
);
data
=
info
+
1
;
epilogs
=
info
->
epilog
;
codes
=
info
->
codes
;
if
(
!
codes
&&
!
epilogs
)
{
struct
unwind_info_ext
*
infoex
=
data
;
codes
=
infoex
->
codes
;
epilogs
=
infoex
->
epilog
;
data
=
infoex
+
1
;
}
info_epilog
=
data
;
if
(
!
info
->
e
)
data
=
info_epilog
+
epilogs
;
offset
=
(
pc
-
base
)
-
func
->
BeginAddress
;
end
=
(
BYTE
*
)
data
+
codes
*
4
;
TRACE
(
"function %lx-%lx: len=%#x ver=%u X=%u E=%u F=%u epilogs=%u codes=%u
\n
"
,
base
+
func
->
BeginAddress
,
base
+
func
->
BeginAddress
+
info
->
function_length
*
2
,
info
->
function_length
,
info
->
version
,
info
->
x
,
info
->
e
,
info
->
f
,
epilogs
,
codes
*
4
);
/* check for prolog */
if
(
offset
<
codes
*
4
*
4
&&
!
info
->
f
)
{
len
=
get_sequence_len
(
data
,
end
,
0
);
if
(
offset
<
len
)
{
process_unwind_codes
(
data
,
end
,
context
,
ptrs
,
len
-
offset
);
return
NULL
;
}
}
/* check for epilog */
if
(
!
info
->
e
)
{
for
(
i
=
0
;
i
<
epilogs
;
i
++
)
{
/* TODO: Currently not checking epilogue conditions. */
if
(
offset
<
2
*
info_epilog
[
i
].
offset
)
break
;
if
(
offset
-
2
*
info_epilog
[
i
].
offset
<
(
codes
*
4
-
info_epilog
[
i
].
index
)
*
4
)
{
BYTE
*
ptr
=
(
BYTE
*
)
data
+
info_epilog
[
i
].
index
;
len
=
get_sequence_len
(
ptr
,
end
,
1
);
if
(
offset
<=
2
*
info_epilog
[
i
].
offset
+
len
)
{
process_unwind_codes
(
ptr
,
end
,
context
,
ptrs
,
offset
-
2
*
info_epilog
[
i
].
offset
);
return
NULL
;
}
}
}
}
else
if
(
2
*
info
->
function_length
-
offset
<=
(
codes
*
4
-
epilogs
)
*
4
)
{
BYTE
*
ptr
=
(
BYTE
*
)
data
+
epilogs
;
len
=
get_sequence_len
(
ptr
,
end
,
1
);
if
(
offset
>=
2
*
info
->
function_length
-
len
)
{
process_unwind_codes
(
ptr
,
end
,
context
,
ptrs
,
offset
-
(
2
*
info
->
function_length
-
len
)
);
return
NULL
;
}
}
process_unwind_codes
(
data
,
end
,
context
,
ptrs
,
0
);
/* get handler since we are inside the main code */
if
(
info
->
x
)
{
DWORD
*
handler_rva
=
(
DWORD
*
)
data
+
codes
;
*
handler_data
=
handler_rva
+
1
;
return
(
char
*
)
base
+
*
handler_rva
;
}
return
NULL
;
}
/**********************************************************************
* find_function_info
*/
static
RUNTIME_FUNCTION
*
find_function_info
(
ULONG_PTR
pc
,
ULONG_PTR
base
,
RUNTIME_FUNCTION
*
func
,
ULONG
size
)
{
int
min
=
0
;
int
max
=
size
-
1
;
while
(
min
<=
max
)
{
int
pos
=
(
min
+
max
)
/
2
;
ULONG_PTR
start
=
base
+
(
func
[
pos
].
BeginAddress
&
~
1
);
if
(
pc
>=
start
)
{
struct
unwind_info
*
info
=
(
struct
unwind_info
*
)(
base
+
func
[
pos
].
UnwindData
);
if
(
pc
<
start
+
2
*
(
func
[
pos
].
Flag
?
func
[
pos
].
FunctionLength
:
info
->
function_length
))
return
func
+
pos
;
min
=
pos
+
1
;
}
else
max
=
pos
-
1
;
}
return
NULL
;
}
/***********************************************************************
* RtlVirtualUnwind (NTDLL.@)
*/
PVOID
WINAPI
RtlVirtualUnwind
(
ULONG
type
,
ULONG_PTR
base
,
ULONG_PTR
pc
,
RUNTIME_FUNCTION
*
func
,
CONTEXT
*
context
,
PVOID
*
handler_data
,
ULONG_PTR
*
frame_ret
,
KNONVOLATILE_CONTEXT_POINTERS
*
ctx_ptr
)
{
void
*
handler
;
TRACE
(
"type %lx pc %Ix sp %lx func %lx
\n
"
,
type
,
pc
,
context
->
Sp
,
base
+
func
->
BeginAddress
);
*
handler_data
=
NULL
;
context
->
Pc
=
0
;
if
(
func
->
Flag
)
handler
=
unwind_packed_data
(
base
,
pc
,
func
,
context
,
ctx_ptr
);
else
handler
=
unwind_full_data
(
base
,
pc
,
func
,
context
,
handler_data
,
ctx_ptr
);
TRACE
(
"ret: pc=%lx lr=%lx sp=%lx handler=%p
\n
"
,
context
->
Pc
,
context
->
Lr
,
context
->
Sp
,
handler
);
if
(
!
context
->
Pc
)
{
context
->
Pc
=
context
->
Lr
;
context
->
ContextFlags
|=
CONTEXT_UNWOUND_TO_CALL
;
}
*
frame_ret
=
context
->
Sp
;
return
handler
;
}
/**********************************************************************
* RtlLookupFunctionTable (NTDLL.@)
...
...
@@ -1184,49 +601,6 @@ PRUNTIME_FUNCTION WINAPI RtlLookupFunctionTable( ULONG_PTR pc, ULONG_PTR *base,
/**********************************************************************
* RtlLookupFunctionEntry (NTDLL.@)
*/
PRUNTIME_FUNCTION
WINAPI
RtlLookupFunctionEntry
(
ULONG_PTR
pc
,
ULONG_PTR
*
base
,
UNWIND_HISTORY_TABLE
*
table
)
{
RUNTIME_FUNCTION
*
func
;
ULONG_PTR
dynbase
;
ULONG
size
;
if
((
func
=
RtlLookupFunctionTable
(
pc
,
base
,
&
size
)))
return
find_function_info
(
pc
,
*
base
,
func
,
size
/
sizeof
(
*
func
));
if
((
func
=
lookup_dynamic_function_table
(
pc
,
&
dynbase
,
&
size
)))
{
RUNTIME_FUNCTION
*
ret
=
find_function_info
(
pc
,
dynbase
,
func
,
size
);
if
(
ret
)
*
base
=
dynbase
;
return
ret
;
}
*
base
=
0
;
return
NULL
;
}
/**********************************************************************
* RtlAddFunctionTable (NTDLL.@)
*/
BOOLEAN
CDECL
RtlAddFunctionTable
(
RUNTIME_FUNCTION
*
table
,
DWORD
count
,
ULONG_PTR
base
)
{
ULONG_PTR
end
=
base
;
void
*
ret
;
if
(
count
)
{
RUNTIME_FUNCTION
*
func
=
table
+
count
-
1
;
struct
unwind_info
*
info
=
(
struct
unwind_info
*
)(
base
+
func
->
UnwindData
);
end
+=
func
->
BeginAddress
+
2
*
(
func
->
Flag
?
func
->
FunctionLength
:
info
->
function_length
);
}
return
!
RtlAddGrowableFunctionTable
(
&
ret
,
table
,
count
,
0
,
base
,
end
);
}
/**********************************************************************
* call_consolidate_callback
*
* Wrapper function to call a consolidate callback from a fake frame.
...
...
dlls/ntdll/signal_arm64.c
View file @
a5a25382
...
...
@@ -579,499 +579,6 @@ __ASM_GLOBAL_FUNC( KiUserCallbackDispatcher,
"brk #1"
)
/***********************************************************************
* Definitions for Win32 unwind tables
*/
struct
unwind_info_ext
{
WORD
epilog
;
BYTE
codes
;
BYTE
reserved
;
};
struct
unwind_info_epilog
{
DWORD
offset
:
18
;
DWORD
res
:
4
;
DWORD
index
:
10
;
};
static
const
BYTE
unwind_code_len
[
256
]
=
{
/* 00 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 20 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 40 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 60 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 80 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* a0 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* c0 */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* e0 */
4
,
1
,
2
,
1
,
1
,
1
,
1
,
2
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
};
/***********************************************************************
* get_sequence_len
*/
static
unsigned
int
get_sequence_len
(
BYTE
*
ptr
,
BYTE
*
end
)
{
unsigned
int
ret
=
0
;
while
(
ptr
<
end
)
{
if
(
*
ptr
==
0xe4
||
*
ptr
==
0xe5
)
break
;
ptr
+=
unwind_code_len
[
*
ptr
];
ret
++
;
}
return
ret
;
}
/***********************************************************************
* restore_regs
*/
static
void
restore_regs
(
int
reg
,
int
count
,
int
pos
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
int
i
,
offset
=
max
(
0
,
pos
);
for
(
i
=
0
;
i
<
count
;
i
++
)
{
if
(
ptrs
&&
reg
+
i
>=
19
)
(
&
ptrs
->
X19
)[
reg
+
i
-
19
]
=
(
DWORD64
*
)
context
->
Sp
+
i
+
offset
;
context
->
X
[
reg
+
i
]
=
((
DWORD64
*
)
context
->
Sp
)[
i
+
offset
];
}
if
(
pos
<
0
)
context
->
Sp
+=
-
8
*
pos
;
}
/***********************************************************************
* restore_fpregs
*/
static
void
restore_fpregs
(
int
reg
,
int
count
,
int
pos
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
int
i
,
offset
=
max
(
0
,
pos
);
for
(
i
=
0
;
i
<
count
;
i
++
)
{
if
(
ptrs
&&
reg
+
i
>=
8
)
(
&
ptrs
->
D8
)[
reg
+
i
-
8
]
=
(
DWORD64
*
)
context
->
Sp
+
i
+
offset
;
context
->
V
[
reg
+
i
].
D
[
0
]
=
((
double
*
)
context
->
Sp
)[
i
+
offset
];
}
if
(
pos
<
0
)
context
->
Sp
+=
-
8
*
pos
;
}
static
void
do_pac_auth
(
CONTEXT
*
context
)
{
register
DWORD64
x17
__asm__
(
"x17"
)
=
context
->
Lr
;
register
DWORD64
x16
__asm__
(
"x16"
)
=
context
->
Sp
;
/* This is the autib1716 instruction. The hint instruction is used here
* as gcc does not assemble autib1716 for pre armv8.3a targets. For
* pre-armv8.3a targets, this is just treated as a hint instruction, which
* is ignored. */
__asm__
(
"hint 0xe"
:
"+r"
(
x17
)
:
"r"
(
x16
)
);
context
->
Lr
=
x17
;
}
/***********************************************************************
* process_unwind_codes
*/
static
void
process_unwind_codes
(
BYTE
*
ptr
,
BYTE
*
end
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
,
int
skip
)
{
unsigned
int
val
,
len
,
save_next
=
2
;
/* skip codes */
while
(
ptr
<
end
&&
skip
)
{
if
(
*
ptr
==
0xe4
||
*
ptr
==
0xe5
)
break
;
ptr
+=
unwind_code_len
[
*
ptr
];
skip
--
;
}
while
(
ptr
<
end
)
{
if
((
len
=
unwind_code_len
[
*
ptr
])
>
1
)
{
if
(
ptr
+
len
>
end
)
break
;
val
=
ptr
[
0
]
*
0x100
+
ptr
[
1
];
}
else
val
=
*
ptr
;
if
(
*
ptr
<
0x20
)
/* alloc_s */
context
->
Sp
+=
16
*
(
val
&
0x1f
);
else
if
(
*
ptr
<
0x40
)
/* save_r19r20_x */
restore_regs
(
19
,
save_next
,
-
(
val
&
0x1f
),
context
,
ptrs
);
else
if
(
*
ptr
<
0x80
)
/* save_fplr */
restore_regs
(
29
,
2
,
val
&
0x3f
,
context
,
ptrs
);
else
if
(
*
ptr
<
0xc0
)
/* save_fplr_x */
restore_regs
(
29
,
2
,
-
(
val
&
0x3f
)
-
1
,
context
,
ptrs
);
else
if
(
*
ptr
<
0xc8
)
/* alloc_m */
context
->
Sp
+=
16
*
(
val
&
0x7ff
);
else
if
(
*
ptr
<
0xcc
)
/* save_regp */
restore_regs
(
19
+
((
val
>>
6
)
&
0xf
),
save_next
,
val
&
0x3f
,
context
,
ptrs
);
else
if
(
*
ptr
<
0xd0
)
/* save_regp_x */
restore_regs
(
19
+
((
val
>>
6
)
&
0xf
),
save_next
,
-
(
val
&
0x3f
)
-
1
,
context
,
ptrs
);
else
if
(
*
ptr
<
0xd4
)
/* save_reg */
restore_regs
(
19
+
((
val
>>
6
)
&
0xf
),
1
,
val
&
0x3f
,
context
,
ptrs
);
else
if
(
*
ptr
<
0xd6
)
/* save_reg_x */
restore_regs
(
19
+
((
val
>>
5
)
&
0xf
),
1
,
-
(
val
&
0x1f
)
-
1
,
context
,
ptrs
);
else
if
(
*
ptr
<
0xd8
)
/* save_lrpair */
{
restore_regs
(
19
+
2
*
((
val
>>
6
)
&
0x7
),
1
,
val
&
0x3f
,
context
,
ptrs
);
restore_regs
(
30
,
1
,
(
val
&
0x3f
)
+
1
,
context
,
ptrs
);
}
else
if
(
*
ptr
<
0xda
)
/* save_fregp */
restore_fpregs
(
8
+
((
val
>>
6
)
&
0x7
),
save_next
,
val
&
0x3f
,
context
,
ptrs
);
else
if
(
*
ptr
<
0xdc
)
/* save_fregp_x */
restore_fpregs
(
8
+
((
val
>>
6
)
&
0x7
),
save_next
,
-
(
val
&
0x3f
)
-
1
,
context
,
ptrs
);
else
if
(
*
ptr
<
0xde
)
/* save_freg */
restore_fpregs
(
8
+
((
val
>>
6
)
&
0x7
),
1
,
val
&
0x3f
,
context
,
ptrs
);
else
if
(
*
ptr
==
0xde
)
/* save_freg_x */
restore_fpregs
(
8
+
((
val
>>
5
)
&
0x7
),
1
,
-
(
val
&
0x3f
)
-
1
,
context
,
ptrs
);
else
if
(
*
ptr
==
0xe0
)
/* alloc_l */
context
->
Sp
+=
16
*
((
ptr
[
1
]
<<
16
)
+
(
ptr
[
2
]
<<
8
)
+
ptr
[
3
]);
else
if
(
*
ptr
==
0xe1
)
/* set_fp */
context
->
Sp
=
context
->
Fp
;
else
if
(
*
ptr
==
0xe2
)
/* add_fp */
context
->
Sp
=
context
->
Fp
-
8
*
(
val
&
0xff
);
else
if
(
*
ptr
==
0xe3
)
/* nop */
/* nop */
;
else
if
(
*
ptr
==
0xe4
)
/* end */
break
;
else
if
(
*
ptr
==
0xe5
)
/* end_c */
break
;
else
if
(
*
ptr
==
0xe6
)
/* save_next */
{
save_next
+=
2
;
ptr
+=
len
;
continue
;
}
else
if
(
*
ptr
==
0xe9
)
/* MSFT_OP_MACHINE_FRAME */
{
context
->
Pc
=
((
DWORD64
*
)
context
->
Sp
)[
1
];
context
->
Sp
=
((
DWORD64
*
)
context
->
Sp
)[
0
];
context
->
ContextFlags
&=
~
CONTEXT_UNWOUND_TO_CALL
;
}
else
if
(
*
ptr
==
0xea
)
/* MSFT_OP_CONTEXT */
{
memcpy
(
context
,
(
DWORD64
*
)
context
->
Sp
,
sizeof
(
CONTEXT
)
);
}
else
if
(
*
ptr
==
0xfc
)
/* pac_sign_lr */
{
do_pac_auth
(
context
);
}
else
{
WARN
(
"unsupported code %02x
\n
"
,
*
ptr
);
return
;
}
save_next
=
2
;
ptr
+=
len
;
}
}
/***********************************************************************
* unwind_packed_data
*/
static
void
*
unwind_packed_data
(
ULONG_PTR
base
,
ULONG_PTR
pc
,
RUNTIME_FUNCTION
*
func
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
int
i
;
unsigned
int
len
,
offset
,
skip
=
0
;
unsigned
int
int_size
=
func
->
RegI
*
8
,
fp_size
=
func
->
RegF
*
8
,
regsave
,
local_size
;
unsigned
int
int_regs
,
fp_regs
,
saved_regs
,
local_size_regs
;
TRACE
(
"function %I64x-%I64x: len=%#x flag=%x regF=%u regI=%u H=%u CR=%u frame=%x
\n
"
,
base
+
func
->
BeginAddress
,
base
+
func
->
BeginAddress
+
func
->
FunctionLength
*
4
,
func
->
FunctionLength
,
func
->
Flag
,
func
->
RegF
,
func
->
RegI
,
func
->
H
,
func
->
CR
,
func
->
FrameSize
);
if
(
func
->
CR
==
1
)
int_size
+=
8
;
if
(
func
->
RegF
)
fp_size
+=
8
;
regsave
=
((
int_size
+
fp_size
+
8
*
8
*
func
->
H
)
+
0xf
)
&
~
0xf
;
local_size
=
func
->
FrameSize
*
16
-
regsave
;
int_regs
=
int_size
/
8
;
fp_regs
=
fp_size
/
8
;
saved_regs
=
regsave
/
8
;
local_size_regs
=
local_size
/
8
;
/* check for prolog/epilog */
if
(
func
->
Flag
==
1
)
{
offset
=
((
pc
-
base
)
-
func
->
BeginAddress
)
/
4
;
if
(
offset
<
17
||
offset
>=
func
->
FunctionLength
-
15
)
{
len
=
(
int_size
+
8
)
/
16
+
(
fp_size
+
8
)
/
16
;
switch
(
func
->
CR
)
{
case
2
:
len
++
;
/* pacibsp */
/* fall through */
case
3
:
len
++
;
/* mov x29,sp */
len
++
;
/* stp x29,lr,[sp,0] */
if
(
local_size
<=
512
)
break
;
/* fall through */
case
0
:
case
1
:
if
(
local_size
)
len
++
;
/* sub sp,sp,#local_size */
if
(
local_size
>
4088
)
len
++
;
/* sub sp,sp,#4088 */
break
;
}
len
+=
4
*
func
->
H
;
if
(
offset
<
len
)
/* prolog */
{
skip
=
len
-
offset
;
}
else
if
(
offset
>=
func
->
FunctionLength
-
(
len
+
1
))
/* epilog */
{
skip
=
offset
-
(
func
->
FunctionLength
-
(
len
+
1
));
}
}
}
if
(
!
skip
)
{
if
(
func
->
CR
==
3
||
func
->
CR
==
2
)
{
DWORD64
*
fp
=
(
DWORD64
*
)
context
->
Fp
;
/* X[29] */
context
->
Sp
=
context
->
Fp
;
context
->
X
[
29
]
=
fp
[
0
];
context
->
X
[
30
]
=
fp
[
1
];
}
context
->
Sp
+=
local_size
;
if
(
fp_size
)
restore_fpregs
(
8
,
fp_regs
,
int_regs
,
context
,
ptrs
);
if
(
func
->
CR
==
1
)
restore_regs
(
30
,
1
,
int_regs
-
1
,
context
,
ptrs
);
restore_regs
(
19
,
func
->
RegI
,
-
saved_regs
,
context
,
ptrs
);
}
else
{
unsigned
int
pos
=
0
;
switch
(
func
->
CR
)
{
case
3
:
case
2
:
/* mov x29,sp */
if
(
pos
++
>=
skip
)
context
->
Sp
=
context
->
Fp
;
if
(
local_size
<=
512
)
{
/* stp x29,lr,[sp,-#local_size]! */
if
(
pos
++
>=
skip
)
restore_regs
(
29
,
2
,
-
local_size_regs
,
context
,
ptrs
);
break
;
}
/* stp x29,lr,[sp,0] */
if
(
pos
++
>=
skip
)
restore_regs
(
29
,
2
,
0
,
context
,
ptrs
);
/* fall through */
case
0
:
case
1
:
if
(
!
local_size
)
break
;
/* sub sp,sp,#local_size */
if
(
pos
++
>=
skip
)
context
->
Sp
+=
(
local_size
-
1
)
%
4088
+
1
;
if
(
local_size
>
4088
&&
pos
++
>=
skip
)
context
->
Sp
+=
4088
;
break
;
}
if
(
func
->
H
)
pos
+=
4
;
if
(
fp_size
)
{
if
(
func
->
RegF
%
2
==
0
&&
pos
++
>=
skip
)
/* str d%u,[sp,#fp_size] */
restore_fpregs
(
8
+
func
->
RegF
,
1
,
int_regs
+
fp_regs
-
1
,
context
,
ptrs
);
for
(
i
=
(
func
->
RegF
+
1
)
/
2
-
1
;
i
>=
0
;
i
--
)
{
if
(
pos
++
<
skip
)
continue
;
if
(
!
i
&&
!
int_size
)
/* stp d8,d9,[sp,-#regsave]! */
restore_fpregs
(
8
,
2
,
-
saved_regs
,
context
,
ptrs
);
else
/* stp dn,dn+1,[sp,#offset] */
restore_fpregs
(
8
+
2
*
i
,
2
,
int_regs
+
2
*
i
,
context
,
ptrs
);
}
}
if
(
func
->
RegI
%
2
)
{
if
(
pos
++
>=
skip
)
{
/* stp xn,lr,[sp,#offset] */
if
(
func
->
CR
==
1
)
restore_regs
(
30
,
1
,
int_regs
-
1
,
context
,
ptrs
);
/* str xn,[sp,#offset] */
restore_regs
(
18
+
func
->
RegI
,
1
,
(
func
->
RegI
>
1
)
?
func
->
RegI
-
1
:
-
saved_regs
,
context
,
ptrs
);
}
}
else
if
(
func
->
CR
==
1
)
{
/* str lr,[sp,#offset] */
if
(
pos
++
>=
skip
)
restore_regs
(
30
,
1
,
func
->
RegI
?
int_regs
-
1
:
-
saved_regs
,
context
,
ptrs
);
}
for
(
i
=
func
->
RegI
/
2
-
1
;
i
>=
0
;
i
--
)
{
if
(
pos
++
<
skip
)
continue
;
if
(
i
)
/* stp xn,xn+1,[sp,#offset] */
restore_regs
(
19
+
2
*
i
,
2
,
2
*
i
,
context
,
ptrs
);
else
/* stp x19,x20,[sp,-#regsave]! */
restore_regs
(
19
,
2
,
-
saved_regs
,
context
,
ptrs
);
}
}
if
(
func
->
CR
==
2
)
do_pac_auth
(
context
);
return
NULL
;
}
/***********************************************************************
* unwind_full_data
*/
static
void
*
unwind_full_data
(
ULONG_PTR
base
,
ULONG_PTR
pc
,
RUNTIME_FUNCTION
*
func
,
CONTEXT
*
context
,
PVOID
*
handler_data
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA
*
info
;
struct
unwind_info_epilog
*
info_epilog
;
unsigned
int
i
,
codes
,
epilogs
,
len
,
offset
;
void
*
data
;
BYTE
*
end
;
info
=
(
IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA
*
)((
char
*
)
base
+
func
->
UnwindData
);
data
=
info
+
1
;
epilogs
=
info
->
EpilogCount
;
codes
=
info
->
CodeWords
;
if
(
!
codes
&&
!
epilogs
)
{
struct
unwind_info_ext
*
infoex
=
data
;
codes
=
infoex
->
codes
;
epilogs
=
infoex
->
epilog
;
data
=
infoex
+
1
;
}
info_epilog
=
data
;
if
(
!
info
->
EpilogInHeader
)
data
=
info_epilog
+
epilogs
;
offset
=
((
pc
-
base
)
-
func
->
BeginAddress
)
/
4
;
end
=
(
BYTE
*
)
data
+
codes
*
4
;
TRACE
(
"function %I64x-%I64x: len=%#x ver=%u X=%u E=%u epilogs=%u codes=%u
\n
"
,
base
+
func
->
BeginAddress
,
base
+
func
->
BeginAddress
+
info
->
FunctionLength
*
4
,
info
->
FunctionLength
,
info
->
Version
,
info
->
ExceptionDataPresent
,
info
->
EpilogInHeader
,
epilogs
,
codes
*
4
);
/* check for prolog */
if
(
offset
<
codes
*
4
)
{
len
=
get_sequence_len
(
data
,
end
);
if
(
offset
<
len
)
{
process_unwind_codes
(
data
,
end
,
context
,
ptrs
,
len
-
offset
);
return
NULL
;
}
}
/* check for epilog */
if
(
!
info
->
EpilogInHeader
)
{
for
(
i
=
0
;
i
<
epilogs
;
i
++
)
{
if
(
offset
<
info_epilog
[
i
].
offset
)
break
;
if
(
offset
-
info_epilog
[
i
].
offset
<
codes
*
4
-
info_epilog
[
i
].
index
)
{
BYTE
*
ptr
=
(
BYTE
*
)
data
+
info_epilog
[
i
].
index
;
len
=
get_sequence_len
(
ptr
,
end
);
if
(
offset
<=
info_epilog
[
i
].
offset
+
len
)
{
process_unwind_codes
(
ptr
,
end
,
context
,
ptrs
,
offset
-
info_epilog
[
i
].
offset
);
return
NULL
;
}
}
}
}
else
if
(
info
->
FunctionLength
-
offset
<=
codes
*
4
-
epilogs
)
{
BYTE
*
ptr
=
(
BYTE
*
)
data
+
epilogs
;
len
=
get_sequence_len
(
ptr
,
end
)
+
1
;
if
(
offset
>=
info
->
FunctionLength
-
len
)
{
process_unwind_codes
(
ptr
,
end
,
context
,
ptrs
,
offset
-
(
info
->
FunctionLength
-
len
)
);
return
NULL
;
}
}
process_unwind_codes
(
data
,
end
,
context
,
ptrs
,
0
);
/* get handler since we are inside the main code */
if
(
info
->
ExceptionDataPresent
)
{
DWORD
*
handler_rva
=
(
DWORD
*
)
data
+
codes
;
*
handler_data
=
handler_rva
+
1
;
return
(
char
*
)
base
+
*
handler_rva
;
}
return
NULL
;
}
/**********************************************************************
* find_function_info
*/
static
RUNTIME_FUNCTION
*
find_function_info
(
ULONG_PTR
pc
,
ULONG_PTR
base
,
RUNTIME_FUNCTION
*
func
,
ULONG
size
)
{
int
min
=
0
;
int
max
=
size
-
1
;
while
(
min
<=
max
)
{
int
pos
=
(
min
+
max
)
/
2
;
ULONG_PTR
start
=
base
+
func
[
pos
].
BeginAddress
;
if
(
pc
>=
start
)
{
ULONG
len
=
func
[
pos
].
Flag
?
func
[
pos
].
FunctionLength
:
((
IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA
*
)(
base
+
func
[
pos
].
UnwindData
))
->
FunctionLength
;
if
(
pc
<
start
+
4
*
len
)
return
func
+
pos
;
min
=
pos
+
1
;
}
else
max
=
pos
-
1
;
}
return
NULL
;
}
/**********************************************************************
* RtlVirtualUnwind (NTDLL.@)
*/
PVOID
WINAPI
RtlVirtualUnwind
(
ULONG
type
,
ULONG_PTR
base
,
ULONG_PTR
pc
,
RUNTIME_FUNCTION
*
func
,
CONTEXT
*
context
,
PVOID
*
handler_data
,
ULONG_PTR
*
frame_ret
,
KNONVOLATILE_CONTEXT_POINTERS
*
ctx_ptr
)
{
void
*
handler
;
TRACE
(
"type %lx pc %I64x sp %I64x func %I64x
\n
"
,
type
,
pc
,
context
->
Sp
,
base
+
func
->
BeginAddress
);
*
handler_data
=
NULL
;
context
->
Pc
=
0
;
if
(
func
->
Flag
)
handler
=
unwind_packed_data
(
base
,
pc
,
func
,
context
,
ctx_ptr
);
else
handler
=
unwind_full_data
(
base
,
pc
,
func
,
context
,
handler_data
,
ctx_ptr
);
TRACE
(
"ret: pc=%I64x lr=%I64x sp=%I64x handler=%p
\n
"
,
context
->
Pc
,
context
->
Lr
,
context
->
Sp
,
handler
);
if
(
!
context
->
Pc
)
{
context
->
Pc
=
context
->
Lr
;
context
->
ContextFlags
|=
CONTEXT_UNWOUND_TO_CALL
;
}
*
frame_ret
=
context
->
Sp
;
return
handler
;
}
/**********************************************************************
* RtlLookupFunctionTable (NTDLL.@)
*/
...
...
@@ -1086,50 +593,6 @@ PRUNTIME_FUNCTION WINAPI RtlLookupFunctionTable( ULONG_PTR pc, ULONG_PTR *base,
/**********************************************************************
* RtlLookupFunctionEntry (NTDLL.@)
*/
PRUNTIME_FUNCTION
WINAPI
RtlLookupFunctionEntry
(
ULONG_PTR
pc
,
ULONG_PTR
*
base
,
UNWIND_HISTORY_TABLE
*
table
)
{
RUNTIME_FUNCTION
*
func
;
ULONG_PTR
dynbase
;
ULONG
size
;
if
((
func
=
RtlLookupFunctionTable
(
pc
,
base
,
&
size
)))
return
find_function_info
(
pc
,
*
base
,
func
,
size
/
sizeof
(
*
func
));
if
((
func
=
lookup_dynamic_function_table
(
pc
,
&
dynbase
,
&
size
)))
{
RUNTIME_FUNCTION
*
ret
=
find_function_info
(
pc
,
dynbase
,
func
,
size
);
if
(
ret
)
*
base
=
dynbase
;
return
ret
;
}
*
base
=
0
;
return
NULL
;
}
/**********************************************************************
* RtlAddFunctionTable (NTDLL.@)
*/
BOOLEAN
CDECL
RtlAddFunctionTable
(
RUNTIME_FUNCTION
*
table
,
DWORD
count
,
ULONG_PTR
base
)
{
ULONG_PTR
end
=
base
;
void
*
ret
;
if
(
count
)
{
RUNTIME_FUNCTION
*
func
=
table
+
count
-
1
;
ULONG
len
=
func
->
Flag
?
func
->
FunctionLength
:
((
IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA
*
)(
base
+
func
->
UnwindData
))
->
FunctionLength
;
end
+=
func
->
BeginAddress
+
4
*
len
;
}
return
!
RtlAddGrowableFunctionTable
(
&
ret
,
table
,
count
,
0
,
base
,
end
);
}
/**********************************************************************
* call_consolidate_callback
*
* Wrapper function to call a consolidate callback from a fake frame.
...
...
dlls/ntdll/signal_arm64ec.c
View file @
a5a25382
...
...
@@ -229,52 +229,6 @@ static void context_arm_to_x64( CONTEXT *ctx, const ARM64_NT_CONTEXT *arm_ctx )
memcpy
(
ec_ctx
->
V
,
arm_ctx
->
V
,
sizeof
(
ec_ctx
->
V
)
);
}
static
RUNTIME_FUNCTION
*
find_function_info
(
ULONG_PTR
pc
,
ULONG_PTR
base
,
RUNTIME_FUNCTION
*
func
,
ULONG
size
)
{
int
min
=
0
;
int
max
=
size
-
1
;
while
(
min
<=
max
)
{
int
pos
=
(
min
+
max
)
/
2
;
if
(
pc
<
base
+
func
[
pos
].
BeginAddress
)
max
=
pos
-
1
;
else
if
(
pc
>=
base
+
func
[
pos
].
EndAddress
)
min
=
pos
+
1
;
else
{
func
+=
pos
;
while
(
func
->
UnwindData
&
1
)
/* follow chained entry */
func
=
(
RUNTIME_FUNCTION
*
)(
base
+
(
func
->
UnwindData
&
~
1
));
return
func
;
}
}
return
NULL
;
}
static
ARM64_RUNTIME_FUNCTION
*
find_function_info_arm64
(
ULONG_PTR
pc
,
ULONG_PTR
base
,
ARM64_RUNTIME_FUNCTION
*
func
,
ULONG
size
)
{
int
min
=
0
;
int
max
=
size
-
1
;
while
(
min
<=
max
)
{
int
pos
=
(
min
+
max
)
/
2
;
ULONG_PTR
start
=
base
+
func
[
pos
].
BeginAddress
;
if
(
pc
>=
start
)
{
ULONG
len
=
func
[
pos
].
Flag
?
func
[
pos
].
FunctionLength
:
((
IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA
*
)(
base
+
func
[
pos
].
UnwindData
))
->
FunctionLength
;
if
(
pc
<
start
+
4
*
len
)
return
func
+
pos
;
min
=
pos
+
1
;
}
else
max
=
pos
-
1
;
}
return
NULL
;
}
/*******************************************************************
* syscalls
...
...
@@ -1857,60 +1811,6 @@ PRUNTIME_FUNCTION WINAPI RtlLookupFunctionTable( ULONG_PTR pc, ULONG_PTR *base,
}
/**********************************************************************
* RtlLookupFunctionEntry (NTDLL.@)
*/
PRUNTIME_FUNCTION
WINAPI
RtlLookupFunctionEntry
(
ULONG_PTR
pc
,
ULONG_PTR
*
base
,
UNWIND_HISTORY_TABLE
*
table
)
{
RUNTIME_FUNCTION
*
func
;
ULONG_PTR
dynbase
;
ULONG
size
;
if
((
func
=
RtlLookupFunctionTable
(
pc
,
base
,
&
size
)))
{
if
(
RtlIsEcCode
(
(
void
*
)
pc
))
{
ARM64_RUNTIME_FUNCTION
*
arm64func
=
(
ARM64_RUNTIME_FUNCTION
*
)
func
;
size
/=
sizeof
(
*
arm64func
);
return
(
RUNTIME_FUNCTION
*
)
find_function_info_arm64
(
pc
,
*
base
,
arm64func
,
size
);
}
return
find_function_info
(
pc
,
*
base
,
func
,
size
/
sizeof
(
*
func
));
}
if
((
func
=
lookup_dynamic_function_table
(
pc
,
&
dynbase
,
&
size
)))
{
RUNTIME_FUNCTION
*
ret
;
if
(
RtlIsEcCode
(
(
void
*
)
pc
))
ret
=
(
RUNTIME_FUNCTION
*
)
find_function_info_arm64
(
pc
,
dynbase
,
(
ARM64_RUNTIME_FUNCTION
*
)
func
,
size
);
else
ret
=
find_function_info
(
pc
,
dynbase
,
func
,
size
);
if
(
ret
)
*
base
=
dynbase
;
return
ret
;
}
*
base
=
0
;
return
NULL
;
}
/**********************************************************************
* RtlAddFunctionTable (NTDLL.@)
*/
BOOLEAN
CDECL
RtlAddFunctionTable
(
RUNTIME_FUNCTION
*
table
,
DWORD
count
,
ULONG_PTR
base
)
{
ULONG_PTR
end
=
base
;
void
*
ret
;
if
(
count
)
end
+=
table
[
count
-
1
].
EndAddress
;
return
!
RtlAddGrowableFunctionTable
(
&
ret
,
table
,
count
,
0
,
base
,
end
);
}
/*******************************************************************
* RtlUnwindEx (NTDLL.@)
*/
...
...
dlls/ntdll/signal_x86_64.c
View file @
a5a25382
...
...
@@ -77,165 +77,6 @@ ALL_SYSCALLS64
#undef SYSCALL_ENTRY
/***********************************************************************
* Definitions for Win32 unwind tables
*/
union
handler_data
{
RUNTIME_FUNCTION
chain
;
ULONG
handler
;
};
struct
opcode
{
BYTE
offset
;
BYTE
code
:
4
;
BYTE
info
:
4
;
};
struct
UNWIND_INFO
{
BYTE
version
:
3
;
BYTE
flags
:
5
;
BYTE
prolog
;
BYTE
count
;
BYTE
frame_reg
:
4
;
BYTE
frame_offset
:
4
;
struct
opcode
opcodes
[
1
];
/* info->count entries */
/* followed by handler_data */
};
#define UWOP_PUSH_NONVOL 0
#define UWOP_ALLOC_LARGE 1
#define UWOP_ALLOC_SMALL 2
#define UWOP_SET_FPREG 3
#define UWOP_SAVE_NONVOL 4
#define UWOP_SAVE_NONVOL_FAR 5
#define UWOP_EPILOG 6
#define UWOP_SAVE_XMM128 8
#define UWOP_SAVE_XMM128_FAR 9
#define UWOP_PUSH_MACHFRAME 10
static
void
dump_unwind_info
(
ULONG64
base
,
RUNTIME_FUNCTION
*
function
)
{
static
const
char
*
const
reg_names
[
16
]
=
{
"rax"
,
"rcx"
,
"rdx"
,
"rbx"
,
"rsp"
,
"rbp"
,
"rsi"
,
"rdi"
,
"r8"
,
"r9"
,
"r10"
,
"r11"
,
"r12"
,
"r13"
,
"r14"
,
"r15"
};
union
handler_data
*
handler_data
;
struct
UNWIND_INFO
*
info
;
unsigned
int
i
,
count
;
TRACE
(
"**** func %lx-%lx
\n
"
,
function
->
BeginAddress
,
function
->
EndAddress
);
for
(;;)
{
if
(
function
->
UnwindData
&
1
)
{
RUNTIME_FUNCTION
*
next
=
(
RUNTIME_FUNCTION
*
)((
char
*
)
base
+
(
function
->
UnwindData
&
~
1
));
TRACE
(
"unwind info for function %p-%p chained to function %p-%p
\n
"
,
(
char
*
)
base
+
function
->
BeginAddress
,
(
char
*
)
base
+
function
->
EndAddress
,
(
char
*
)
base
+
next
->
BeginAddress
,
(
char
*
)
base
+
next
->
EndAddress
);
function
=
next
;
continue
;
}
info
=
(
struct
UNWIND_INFO
*
)((
char
*
)
base
+
function
->
UnwindData
);
TRACE
(
"unwind info at %p flags %x prolog 0x%x bytes function %p-%p
\n
"
,
info
,
info
->
flags
,
info
->
prolog
,
(
char
*
)
base
+
function
->
BeginAddress
,
(
char
*
)
base
+
function
->
EndAddress
);
if
(
info
->
frame_reg
)
TRACE
(
" frame register %s offset 0x%x(%%rsp)
\n
"
,
reg_names
[
info
->
frame_reg
],
info
->
frame_offset
*
16
);
for
(
i
=
0
;
i
<
info
->
count
;
i
++
)
{
TRACE
(
" 0x%x: "
,
info
->
opcodes
[
i
].
offset
);
switch
(
info
->
opcodes
[
i
].
code
)
{
case
UWOP_PUSH_NONVOL
:
TRACE
(
"pushq %%%s
\n
"
,
reg_names
[
info
->
opcodes
[
i
].
info
]
);
break
;
case
UWOP_ALLOC_LARGE
:
if
(
info
->
opcodes
[
i
].
info
)
{
count
=
*
(
DWORD
*
)
&
info
->
opcodes
[
i
+
1
];
i
+=
2
;
}
else
{
count
=
*
(
USHORT
*
)
&
info
->
opcodes
[
i
+
1
]
*
8
;
i
++
;
}
TRACE
(
"subq $0x%x,%%rsp
\n
"
,
count
);
break
;
case
UWOP_ALLOC_SMALL
:
count
=
(
info
->
opcodes
[
i
].
info
+
1
)
*
8
;
TRACE
(
"subq $0x%x,%%rsp
\n
"
,
count
);
break
;
case
UWOP_SET_FPREG
:
TRACE
(
"leaq 0x%x(%%rsp),%s
\n
"
,
info
->
frame_offset
*
16
,
reg_names
[
info
->
frame_reg
]
);
break
;
case
UWOP_SAVE_NONVOL
:
count
=
*
(
USHORT
*
)
&
info
->
opcodes
[
i
+
1
]
*
8
;
TRACE
(
"movq %%%s,0x%x(%%rsp)
\n
"
,
reg_names
[
info
->
opcodes
[
i
].
info
],
count
);
i
++
;
break
;
case
UWOP_SAVE_NONVOL_FAR
:
count
=
*
(
DWORD
*
)
&
info
->
opcodes
[
i
+
1
];
TRACE
(
"movq %%%s,0x%x(%%rsp)
\n
"
,
reg_names
[
info
->
opcodes
[
i
].
info
],
count
);
i
+=
2
;
break
;
case
UWOP_SAVE_XMM128
:
count
=
*
(
USHORT
*
)
&
info
->
opcodes
[
i
+
1
]
*
16
;
TRACE
(
"movaps %%xmm%u,0x%x(%%rsp)
\n
"
,
info
->
opcodes
[
i
].
info
,
count
);
i
++
;
break
;
case
UWOP_SAVE_XMM128_FAR
:
count
=
*
(
DWORD
*
)
&
info
->
opcodes
[
i
+
1
];
TRACE
(
"movaps %%xmm%u,0x%x(%%rsp)
\n
"
,
info
->
opcodes
[
i
].
info
,
count
);
i
+=
2
;
break
;
case
UWOP_PUSH_MACHFRAME
:
TRACE
(
"PUSH_MACHFRAME %u
\n
"
,
info
->
opcodes
[
i
].
info
);
break
;
case
UWOP_EPILOG
:
if
(
info
->
version
==
2
)
{
unsigned
int
offset
;
if
(
info
->
opcodes
[
i
].
info
)
offset
=
info
->
opcodes
[
i
].
offset
;
else
offset
=
(
info
->
opcodes
[
i
+
1
].
info
<<
8
)
+
info
->
opcodes
[
i
+
1
].
offset
;
TRACE
(
"epilog %p-%p
\n
"
,
(
char
*
)
base
+
function
->
EndAddress
-
offset
,
(
char
*
)
base
+
function
->
EndAddress
-
offset
+
info
->
opcodes
[
i
].
offset
);
i
+=
1
;
break
;
}
default:
FIXME
(
"unknown code %u
\n
"
,
info
->
opcodes
[
i
].
code
);
break
;
}
}
handler_data
=
(
union
handler_data
*
)
&
info
->
opcodes
[(
info
->
count
+
1
)
&
~
1
];
if
(
info
->
flags
&
UNW_FLAG_CHAININFO
)
{
TRACE
(
" chained to function %p-%p
\n
"
,
(
char
*
)
base
+
handler_data
->
chain
.
BeginAddress
,
(
char
*
)
base
+
handler_data
->
chain
.
EndAddress
);
function
=
&
handler_data
->
chain
;
continue
;
}
if
(
info
->
flags
&
(
UNW_FLAG_EHANDLER
|
UNW_FLAG_UHANDLER
))
TRACE
(
" handler %p data at %p
\n
"
,
(
char
*
)
base
+
handler_data
->
handler
,
&
handler_data
->
handler
+
1
);
break
;
}
}
static
void
dump_scope_table
(
ULONG64
base
,
const
SCOPE_TABLE
*
table
)
{
unsigned
int
i
;
...
...
@@ -249,28 +90,6 @@ static void dump_scope_table( ULONG64 base, const SCOPE_TABLE *table )
(
char
*
)
base
+
table
->
ScopeRecord
[
i
].
JumpTarget
);
}
static
RUNTIME_FUNCTION
*
find_function_info
(
ULONG_PTR
pc
,
ULONG_PTR
base
,
RUNTIME_FUNCTION
*
func
,
ULONG
size
)
{
int
min
=
0
;
int
max
=
size
-
1
;
while
(
min
<=
max
)
{
int
pos
=
(
min
+
max
)
/
2
;
if
(
pc
<
base
+
func
[
pos
].
BeginAddress
)
max
=
pos
-
1
;
else
if
(
pc
>=
base
+
func
[
pos
].
EndAddress
)
min
=
pos
+
1
;
else
{
func
+=
pos
;
while
(
func
->
UnwindData
&
1
)
/* follow chained entry */
func
=
(
RUNTIME_FUNCTION
*
)(
base
+
(
func
->
UnwindData
&
~
1
));
return
func
;
}
}
return
NULL
;
}
/***********************************************************************
* virtual_unwind
...
...
@@ -731,316 +550,6 @@ BOOLEAN WINAPI RtlIsEcCode( const void *ptr )
}
static
ULONG64
get_int_reg
(
CONTEXT
*
context
,
int
reg
)
{
return
*
(
&
context
->
Rax
+
reg
);
}
static
void
set_int_reg
(
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ctx_ptr
,
int
reg
,
ULONG64
*
val
)
{
*
(
&
context
->
Rax
+
reg
)
=
*
val
;
if
(
ctx_ptr
)
ctx_ptr
->
IntegerContext
[
reg
]
=
val
;
}
static
void
set_float_reg
(
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ctx_ptr
,
int
reg
,
M128A
*
val
)
{
/* Use a memcpy() to avoid issues if val is misaligned. */
memcpy
(
&
context
->
Xmm0
+
reg
,
val
,
sizeof
(
*
val
));
if
(
ctx_ptr
)
ctx_ptr
->
FloatingContext
[
reg
]
=
val
;
}
static
int
get_opcode_size
(
struct
opcode
op
)
{
switch
(
op
.
code
)
{
case
UWOP_ALLOC_LARGE
:
return
2
+
(
op
.
info
!=
0
);
case
UWOP_SAVE_NONVOL
:
case
UWOP_SAVE_XMM128
:
case
UWOP_EPILOG
:
return
2
;
case
UWOP_SAVE_NONVOL_FAR
:
case
UWOP_SAVE_XMM128_FAR
:
return
3
;
default
:
return
1
;
}
}
static
BOOL
is_inside_epilog
(
BYTE
*
pc
,
ULONG64
base
,
const
RUNTIME_FUNCTION
*
function
)
{
/* add or lea must be the first instruction, and it must have a rex.W prefix */
if
((
pc
[
0
]
&
0xf8
)
==
0x48
)
{
switch
(
pc
[
1
])
{
case
0x81
:
/* add $nnnn,%rsp */
if
(
pc
[
0
]
==
0x48
&&
pc
[
2
]
==
0xc4
)
{
pc
+=
7
;
break
;
}
return
FALSE
;
case
0x83
:
/* add $n,%rsp */
if
(
pc
[
0
]
==
0x48
&&
pc
[
2
]
==
0xc4
)
{
pc
+=
4
;
break
;
}
return
FALSE
;
case
0x8d
:
/* lea n(reg),%rsp */
if
(
pc
[
0
]
&
0x06
)
return
FALSE
;
/* rex.RX must be cleared */
if
(((
pc
[
2
]
>>
3
)
&
7
)
!=
4
)
return
FALSE
;
/* dest reg mus be %rsp */
if
((
pc
[
2
]
&
7
)
==
4
)
return
FALSE
;
/* no SIB byte allowed */
if
((
pc
[
2
]
>>
6
)
==
1
)
/* 8-bit offset */
{
pc
+=
4
;
break
;
}
if
((
pc
[
2
]
>>
6
)
==
2
)
/* 32-bit offset */
{
pc
+=
7
;
break
;
}
return
FALSE
;
}
}
/* now check for various pop instructions */
for
(;;)
{
if
((
*
pc
&
0xf0
)
==
0x40
)
pc
++
;
/* rex prefix */
switch
(
*
pc
)
{
case
0x58
:
/* pop %rax/%r8 */
case
0x59
:
/* pop %rcx/%r9 */
case
0x5a
:
/* pop %rdx/%r10 */
case
0x5b
:
/* pop %rbx/%r11 */
case
0x5c
:
/* pop %rsp/%r12 */
case
0x5d
:
/* pop %rbp/%r13 */
case
0x5e
:
/* pop %rsi/%r14 */
case
0x5f
:
/* pop %rdi/%r15 */
pc
++
;
continue
;
case
0xc2
:
/* ret $nn */
case
0xc3
:
/* ret */
return
TRUE
;
case
0xe9
:
/* jmp nnnn */
pc
+=
5
+
*
(
LONG
*
)(
pc
+
1
);
if
(
pc
-
(
BYTE
*
)
base
>=
function
->
BeginAddress
&&
pc
-
(
BYTE
*
)
base
<
function
->
EndAddress
)
continue
;
break
;
case
0xeb
:
/* jmp n */
pc
+=
2
+
(
signed
char
)
pc
[
1
];
if
(
pc
-
(
BYTE
*
)
base
>=
function
->
BeginAddress
&&
pc
-
(
BYTE
*
)
base
<
function
->
EndAddress
)
continue
;
break
;
case
0xf3
:
/* rep; ret (for amd64 prediction bug) */
return
pc
[
1
]
==
0xc3
;
}
return
FALSE
;
}
}
/* execute a function epilog, which must have been validated with is_inside_epilog() */
static
void
interpret_epilog
(
BYTE
*
pc
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ctx_ptr
)
{
for
(;;)
{
BYTE
rex
=
0
;
if
((
*
pc
&
0xf0
)
==
0x40
)
rex
=
*
pc
++
&
0x0f
;
/* rex prefix */
switch
(
*
pc
)
{
case
0x58
:
/* pop %rax/r8 */
case
0x59
:
/* pop %rcx/r9 */
case
0x5a
:
/* pop %rdx/r10 */
case
0x5b
:
/* pop %rbx/r11 */
case
0x5c
:
/* pop %rsp/r12 */
case
0x5d
:
/* pop %rbp/r13 */
case
0x5e
:
/* pop %rsi/r14 */
case
0x5f
:
/* pop %rdi/r15 */
set_int_reg
(
context
,
ctx_ptr
,
*
pc
-
0x58
+
(
rex
&
1
)
*
8
,
(
ULONG64
*
)
context
->
Rsp
);
context
->
Rsp
+=
sizeof
(
ULONG64
);
pc
++
;
continue
;
case
0x81
:
/* add $nnnn,%rsp */
context
->
Rsp
+=
*
(
LONG
*
)(
pc
+
2
);
pc
+=
2
+
sizeof
(
LONG
);
continue
;
case
0x83
:
/* add $n,%rsp */
context
->
Rsp
+=
(
signed
char
)
pc
[
2
];
pc
+=
3
;
continue
;
case
0x8d
:
if
((
pc
[
1
]
>>
6
)
==
1
)
/* lea n(reg),%rsp */
{
context
->
Rsp
=
get_int_reg
(
context
,
(
pc
[
1
]
&
7
)
+
(
rex
&
1
)
*
8
)
+
(
signed
char
)
pc
[
2
];
pc
+=
3
;
}
else
/* lea nnnn(reg),%rsp */
{
context
->
Rsp
=
get_int_reg
(
context
,
(
pc
[
1
]
&
7
)
+
(
rex
&
1
)
*
8
)
+
*
(
LONG
*
)(
pc
+
2
);
pc
+=
2
+
sizeof
(
LONG
);
}
continue
;
case
0xc2
:
/* ret $nn */
context
->
Rip
=
*
(
ULONG64
*
)
context
->
Rsp
;
context
->
Rsp
+=
sizeof
(
ULONG64
)
+
*
(
WORD
*
)(
pc
+
1
);
return
;
case
0xc3
:
/* ret */
case
0xf3
:
/* rep; ret */
context
->
Rip
=
*
(
ULONG64
*
)
context
->
Rsp
;
context
->
Rsp
+=
sizeof
(
ULONG64
);
return
;
case
0xe9
:
/* jmp nnnn */
pc
+=
5
+
*
(
LONG
*
)(
pc
+
1
);
continue
;
case
0xeb
:
/* jmp n */
pc
+=
2
+
(
signed
char
)
pc
[
1
];
continue
;
}
return
;
}
}
/**********************************************************************
* RtlVirtualUnwind (NTDLL.@)
*/
PVOID
WINAPI
RtlVirtualUnwind
(
ULONG
type
,
ULONG64
base
,
ULONG64
pc
,
RUNTIME_FUNCTION
*
function
,
CONTEXT
*
context
,
PVOID
*
data
,
ULONG64
*
frame_ret
,
KNONVOLATILE_CONTEXT_POINTERS
*
ctx_ptr
)
{
union
handler_data
*
handler_data
;
ULONG64
frame
,
off
;
struct
UNWIND_INFO
*
info
;
unsigned
int
i
,
prolog_offset
;
BOOL
mach_frame
=
FALSE
;
TRACE
(
"type %lx rip %I64x rsp %I64x
\n
"
,
type
,
pc
,
context
->
Rsp
);
if
(
TRACE_ON
(
seh
))
dump_unwind_info
(
base
,
function
);
frame
=
*
frame_ret
=
context
->
Rsp
;
for
(;;)
{
info
=
(
struct
UNWIND_INFO
*
)((
char
*
)
base
+
function
->
UnwindData
);
handler_data
=
(
union
handler_data
*
)
&
info
->
opcodes
[(
info
->
count
+
1
)
&
~
1
];
if
(
info
->
version
!=
1
&&
info
->
version
!=
2
)
{
FIXME
(
"unknown unwind info version %u at %p
\n
"
,
info
->
version
,
info
);
return
NULL
;
}
if
(
info
->
frame_reg
)
frame
=
get_int_reg
(
context
,
info
->
frame_reg
)
-
info
->
frame_offset
*
16
;
/* check if in prolog */
if
(
pc
>=
base
+
function
->
BeginAddress
&&
pc
<
base
+
function
->
BeginAddress
+
info
->
prolog
)
{
TRACE
(
"inside prolog.
\n
"
);
prolog_offset
=
pc
-
base
-
function
->
BeginAddress
;
}
else
{
prolog_offset
=
~
0
;
/* Since Win10 1809 epilogue does not have a special treatment in case of zero opcode count. */
if
(
info
->
count
&&
is_inside_epilog
(
(
BYTE
*
)
pc
,
base
,
function
))
{
TRACE
(
"inside epilog.
\n
"
);
interpret_epilog
(
(
BYTE
*
)
pc
,
context
,
ctx_ptr
);
*
frame_ret
=
frame
;
return
NULL
;
}
}
for
(
i
=
0
;
i
<
info
->
count
;
i
+=
get_opcode_size
(
info
->
opcodes
[
i
]))
{
if
(
prolog_offset
<
info
->
opcodes
[
i
].
offset
)
continue
;
/* skip it */
switch
(
info
->
opcodes
[
i
].
code
)
{
case
UWOP_PUSH_NONVOL
:
/* pushq %reg */
set_int_reg
(
context
,
ctx_ptr
,
info
->
opcodes
[
i
].
info
,
(
ULONG64
*
)
context
->
Rsp
);
context
->
Rsp
+=
sizeof
(
ULONG64
);
break
;
case
UWOP_ALLOC_LARGE
:
/* subq $nn,%rsp */
if
(
info
->
opcodes
[
i
].
info
)
context
->
Rsp
+=
*
(
DWORD
*
)
&
info
->
opcodes
[
i
+
1
];
else
context
->
Rsp
+=
*
(
USHORT
*
)
&
info
->
opcodes
[
i
+
1
]
*
8
;
break
;
case
UWOP_ALLOC_SMALL
:
/* subq $n,%rsp */
context
->
Rsp
+=
(
info
->
opcodes
[
i
].
info
+
1
)
*
8
;
break
;
case
UWOP_SET_FPREG
:
/* leaq nn(%rsp),%framereg */
context
->
Rsp
=
*
frame_ret
=
frame
;
break
;
case
UWOP_SAVE_NONVOL
:
/* movq %reg,n(%rsp) */
off
=
frame
+
*
(
USHORT
*
)
&
info
->
opcodes
[
i
+
1
]
*
8
;
set_int_reg
(
context
,
ctx_ptr
,
info
->
opcodes
[
i
].
info
,
(
ULONG64
*
)
off
);
break
;
case
UWOP_SAVE_NONVOL_FAR
:
/* movq %reg,nn(%rsp) */
off
=
frame
+
*
(
DWORD
*
)
&
info
->
opcodes
[
i
+
1
];
set_int_reg
(
context
,
ctx_ptr
,
info
->
opcodes
[
i
].
info
,
(
ULONG64
*
)
off
);
break
;
case
UWOP_SAVE_XMM128
:
/* movaps %xmmreg,n(%rsp) */
off
=
frame
+
*
(
USHORT
*
)
&
info
->
opcodes
[
i
+
1
]
*
16
;
set_float_reg
(
context
,
ctx_ptr
,
info
->
opcodes
[
i
].
info
,
(
M128A
*
)
off
);
break
;
case
UWOP_SAVE_XMM128_FAR
:
/* movaps %xmmreg,nn(%rsp) */
off
=
frame
+
*
(
DWORD
*
)
&
info
->
opcodes
[
i
+
1
];
set_float_reg
(
context
,
ctx_ptr
,
info
->
opcodes
[
i
].
info
,
(
M128A
*
)
off
);
break
;
case
UWOP_PUSH_MACHFRAME
:
if
(
info
->
flags
&
UNW_FLAG_CHAININFO
)
{
FIXME
(
"PUSH_MACHFRAME with chained unwind info.
\n
"
);
break
;
}
if
(
i
+
get_opcode_size
(
info
->
opcodes
[
i
])
<
info
->
count
)
{
FIXME
(
"PUSH_MACHFRAME is not the last opcode.
\n
"
);
break
;
}
if
(
info
->
opcodes
[
i
].
info
)
context
->
Rsp
+=
0x8
;
context
->
Rip
=
*
(
ULONG64
*
)
context
->
Rsp
;
context
->
Rsp
=
*
(
ULONG64
*
)(
context
->
Rsp
+
24
);
mach_frame
=
TRUE
;
break
;
case
UWOP_EPILOG
:
if
(
info
->
version
==
2
)
break
;
/* nothing to do */
default
:
FIXME
(
"unknown code %u
\n
"
,
info
->
opcodes
[
i
].
code
);
break
;
}
}
if
(
!
(
info
->
flags
&
UNW_FLAG_CHAININFO
))
break
;
function
=
&
handler_data
->
chain
;
/* restart with the chained info */
}
if
(
!
mach_frame
)
{
/* now pop return address */
context
->
Rip
=
*
(
ULONG64
*
)
context
->
Rsp
;
context
->
Rsp
+=
sizeof
(
ULONG64
);
}
if
(
!
(
info
->
flags
&
type
))
return
NULL
;
/* no matching handler */
if
(
prolog_offset
!=
~
0
)
return
NULL
;
/* inside prolog */
*
data
=
&
handler_data
->
handler
+
1
;
return
(
char
*
)
base
+
handler_data
->
handler
;
}
/**********************************************************************
* RtlLookupFunctionTable (NTDLL.@)
*/
...
...
@@ -1054,45 +563,6 @@ PRUNTIME_FUNCTION WINAPI RtlLookupFunctionTable( ULONG_PTR pc, ULONG_PTR *base,
}
/**********************************************************************
* RtlLookupFunctionEntry (NTDLL.@)
*/
PRUNTIME_FUNCTION
WINAPI
RtlLookupFunctionEntry
(
ULONG_PTR
pc
,
ULONG_PTR
*
base
,
UNWIND_HISTORY_TABLE
*
table
)
{
RUNTIME_FUNCTION
*
func
;
ULONG_PTR
dynbase
;
ULONG
size
;
if
((
func
=
RtlLookupFunctionTable
(
pc
,
base
,
&
size
)))
return
find_function_info
(
pc
,
*
base
,
func
,
size
/
sizeof
(
*
func
));
if
((
func
=
lookup_dynamic_function_table
(
pc
,
&
dynbase
,
&
size
)))
{
RUNTIME_FUNCTION
*
ret
=
find_function_info
(
pc
,
dynbase
,
func
,
size
);
if
(
ret
)
*
base
=
dynbase
;
return
ret
;
}
*
base
=
0
;
return
NULL
;
}
/**********************************************************************
* RtlAddFunctionTable (NTDLL.@)
*/
BOOLEAN
CDECL
RtlAddFunctionTable
(
RUNTIME_FUNCTION
*
table
,
DWORD
count
,
ULONG_PTR
base
)
{
ULONG_PTR
end
=
base
;
void
*
ret
;
if
(
count
)
end
+=
table
[
count
-
1
].
EndAddress
;
return
!
RtlAddGrowableFunctionTable
(
&
ret
,
table
,
count
,
0
,
base
,
end
);
}
struct
unwind_exception_frame
{
EXCEPTION_REGISTRATION_RECORD
frame
;
...
...
dlls/ntdll/unwind.c
0 → 100644
View file @
a5a25382
/*
* Exception unwinding
*
* Copyright 1999, 2005, 2019, 2024 Alexandre Julliard
* Copyright 2021 Martin Storsjö
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __i386__
#include <stdlib.h>
#include <stdarg.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winternl.h"
#include "wine/exception.h"
#include "ntdll_misc.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL
(
unwind
);
/***********************************************************************
* ARM64 support
*/
#if defined(__aarch64__) || defined(__arm64ec__)
struct
unwind_info_ext
{
WORD
epilog
;
BYTE
codes
;
BYTE
reserved
;
};
struct
unwind_info_epilog
{
DWORD
offset
:
18
;
DWORD
res
:
4
;
DWORD
index
:
10
;
};
static
const
BYTE
unwind_code_len
[
256
]
=
{
/* 00 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 20 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 40 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 60 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 80 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* a0 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* c0 */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* e0 */
4
,
1
,
2
,
1
,
1
,
1
,
1
,
2
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
};
static
unsigned
int
get_sequence_len
(
BYTE
*
ptr
,
BYTE
*
end
)
{
unsigned
int
ret
=
0
;
while
(
ptr
<
end
)
{
if
(
*
ptr
==
0xe4
||
*
ptr
==
0xe5
)
break
;
ptr
+=
unwind_code_len
[
*
ptr
];
ret
++
;
}
return
ret
;
}
static
void
restore_regs
(
int
reg
,
int
count
,
int
pos
,
ARM64_NT_CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS_ARM64
*
ptrs
)
{
int
i
,
offset
=
max
(
0
,
pos
);
for
(
i
=
0
;
i
<
count
;
i
++
)
{
if
(
ptrs
&&
reg
+
i
>=
19
)
(
&
ptrs
->
X19
)[
reg
+
i
-
19
]
=
(
DWORD64
*
)
context
->
Sp
+
i
+
offset
;
context
->
X
[
reg
+
i
]
=
((
DWORD64
*
)
context
->
Sp
)[
i
+
offset
];
}
if
(
pos
<
0
)
context
->
Sp
+=
-
8
*
pos
;
}
static
void
restore_fpregs
(
int
reg
,
int
count
,
int
pos
,
ARM64_NT_CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS_ARM64
*
ptrs
)
{
int
i
,
offset
=
max
(
0
,
pos
);
for
(
i
=
0
;
i
<
count
;
i
++
)
{
if
(
ptrs
&&
reg
+
i
>=
8
)
(
&
ptrs
->
D8
)[
reg
+
i
-
8
]
=
(
DWORD64
*
)
context
->
Sp
+
i
+
offset
;
context
->
V
[
reg
+
i
].
D
[
0
]
=
((
double
*
)
context
->
Sp
)[
i
+
offset
];
}
if
(
pos
<
0
)
context
->
Sp
+=
-
8
*
pos
;
}
static
void
do_pac_auth
(
ARM64_NT_CONTEXT
*
context
)
{
register
DWORD64
x17
__asm__
(
"x17"
)
=
context
->
Lr
;
register
DWORD64
x16
__asm__
(
"x16"
)
=
context
->
Sp
;
/* This is the autib1716 instruction. The hint instruction is used here
* as gcc does not assemble autib1716 for pre armv8.3a targets. For
* pre-armv8.3a targets, this is just treated as a hint instruction, which
* is ignored. */
__asm__
(
"hint 0xe"
:
"+r"
(
x17
)
:
"r"
(
x16
)
);
context
->
Lr
=
x17
;
}
static
void
process_unwind_codes
(
BYTE
*
ptr
,
BYTE
*
end
,
ARM64_NT_CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS_ARM64
*
ptrs
,
int
skip
)
{
unsigned
int
val
,
len
,
save_next
=
2
;
/* skip codes */
while
(
ptr
<
end
&&
skip
)
{
if
(
*
ptr
==
0xe4
||
*
ptr
==
0xe5
)
break
;
ptr
+=
unwind_code_len
[
*
ptr
];
skip
--
;
}
while
(
ptr
<
end
)
{
if
((
len
=
unwind_code_len
[
*
ptr
])
>
1
)
{
if
(
ptr
+
len
>
end
)
break
;
val
=
ptr
[
0
]
*
0x100
+
ptr
[
1
];
}
else
val
=
*
ptr
;
if
(
*
ptr
<
0x20
)
/* alloc_s */
context
->
Sp
+=
16
*
(
val
&
0x1f
);
else
if
(
*
ptr
<
0x40
)
/* save_r19r20_x */
restore_regs
(
19
,
save_next
,
-
(
val
&
0x1f
),
context
,
ptrs
);
else
if
(
*
ptr
<
0x80
)
/* save_fplr */
restore_regs
(
29
,
2
,
val
&
0x3f
,
context
,
ptrs
);
else
if
(
*
ptr
<
0xc0
)
/* save_fplr_x */
restore_regs
(
29
,
2
,
-
(
val
&
0x3f
)
-
1
,
context
,
ptrs
);
else
if
(
*
ptr
<
0xc8
)
/* alloc_m */
context
->
Sp
+=
16
*
(
val
&
0x7ff
);
else
if
(
*
ptr
<
0xcc
)
/* save_regp */
restore_regs
(
19
+
((
val
>>
6
)
&
0xf
),
save_next
,
val
&
0x3f
,
context
,
ptrs
);
else
if
(
*
ptr
<
0xd0
)
/* save_regp_x */
restore_regs
(
19
+
((
val
>>
6
)
&
0xf
),
save_next
,
-
(
val
&
0x3f
)
-
1
,
context
,
ptrs
);
else
if
(
*
ptr
<
0xd4
)
/* save_reg */
restore_regs
(
19
+
((
val
>>
6
)
&
0xf
),
1
,
val
&
0x3f
,
context
,
ptrs
);
else
if
(
*
ptr
<
0xd6
)
/* save_reg_x */
restore_regs
(
19
+
((
val
>>
5
)
&
0xf
),
1
,
-
(
val
&
0x1f
)
-
1
,
context
,
ptrs
);
else
if
(
*
ptr
<
0xd8
)
/* save_lrpair */
{
restore_regs
(
19
+
2
*
((
val
>>
6
)
&
0x7
),
1
,
val
&
0x3f
,
context
,
ptrs
);
restore_regs
(
30
,
1
,
(
val
&
0x3f
)
+
1
,
context
,
ptrs
);
}
else
if
(
*
ptr
<
0xda
)
/* save_fregp */
restore_fpregs
(
8
+
((
val
>>
6
)
&
0x7
),
save_next
,
val
&
0x3f
,
context
,
ptrs
);
else
if
(
*
ptr
<
0xdc
)
/* save_fregp_x */
restore_fpregs
(
8
+
((
val
>>
6
)
&
0x7
),
save_next
,
-
(
val
&
0x3f
)
-
1
,
context
,
ptrs
);
else
if
(
*
ptr
<
0xde
)
/* save_freg */
restore_fpregs
(
8
+
((
val
>>
6
)
&
0x7
),
1
,
val
&
0x3f
,
context
,
ptrs
);
else
if
(
*
ptr
==
0xde
)
/* save_freg_x */
restore_fpregs
(
8
+
((
val
>>
5
)
&
0x7
),
1
,
-
(
val
&
0x3f
)
-
1
,
context
,
ptrs
);
else
if
(
*
ptr
==
0xe0
)
/* alloc_l */
context
->
Sp
+=
16
*
((
ptr
[
1
]
<<
16
)
+
(
ptr
[
2
]
<<
8
)
+
ptr
[
3
]);
else
if
(
*
ptr
==
0xe1
)
/* set_fp */
context
->
Sp
=
context
->
Fp
;
else
if
(
*
ptr
==
0xe2
)
/* add_fp */
context
->
Sp
=
context
->
Fp
-
8
*
(
val
&
0xff
);
else
if
(
*
ptr
==
0xe3
)
/* nop */
/* nop */
;
else
if
(
*
ptr
==
0xe4
)
/* end */
break
;
else
if
(
*
ptr
==
0xe5
)
/* end_c */
break
;
else
if
(
*
ptr
==
0xe6
)
/* save_next */
{
save_next
+=
2
;
ptr
+=
len
;
continue
;
}
else
if
(
*
ptr
==
0xe9
)
/* MSFT_OP_MACHINE_FRAME */
{
context
->
Pc
=
((
DWORD64
*
)
context
->
Sp
)[
1
];
context
->
Sp
=
((
DWORD64
*
)
context
->
Sp
)[
0
];
context
->
ContextFlags
&=
~
CONTEXT_UNWOUND_TO_CALL
;
}
else
if
(
*
ptr
==
0xea
)
/* MSFT_OP_CONTEXT */
{
memcpy
(
context
,
(
DWORD64
*
)
context
->
Sp
,
sizeof
(
CONTEXT
)
);
}
else
if
(
*
ptr
==
0xfc
)
/* pac_sign_lr */
{
do_pac_auth
(
context
);
}
else
{
WARN
(
"unsupported code %02x
\n
"
,
*
ptr
);
return
;
}
save_next
=
2
;
ptr
+=
len
;
}
}
static
void
*
unwind_packed_data
(
ULONG_PTR
base
,
ULONG_PTR
pc
,
ARM64_RUNTIME_FUNCTION
*
func
,
ARM64_NT_CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS_ARM64
*
ptrs
)
{
int
i
;
unsigned
int
len
,
offset
,
skip
=
0
;
unsigned
int
int_size
=
func
->
RegI
*
8
,
fp_size
=
func
->
RegF
*
8
,
regsave
,
local_size
;
unsigned
int
int_regs
,
fp_regs
,
saved_regs
,
local_size_regs
;
TRACE
(
"function %I64x-%I64x: len=%#x flag=%x regF=%u regI=%u H=%u CR=%u frame=%x
\n
"
,
base
+
func
->
BeginAddress
,
base
+
func
->
BeginAddress
+
func
->
FunctionLength
*
4
,
func
->
FunctionLength
,
func
->
Flag
,
func
->
RegF
,
func
->
RegI
,
func
->
H
,
func
->
CR
,
func
->
FrameSize
);
if
(
func
->
CR
==
1
)
int_size
+=
8
;
if
(
func
->
RegF
)
fp_size
+=
8
;
regsave
=
((
int_size
+
fp_size
+
8
*
8
*
func
->
H
)
+
0xf
)
&
~
0xf
;
local_size
=
func
->
FrameSize
*
16
-
regsave
;
int_regs
=
int_size
/
8
;
fp_regs
=
fp_size
/
8
;
saved_regs
=
regsave
/
8
;
local_size_regs
=
local_size
/
8
;
/* check for prolog/epilog */
if
(
func
->
Flag
==
1
)
{
offset
=
((
pc
-
base
)
-
func
->
BeginAddress
)
/
4
;
if
(
offset
<
17
||
offset
>=
func
->
FunctionLength
-
15
)
{
len
=
(
int_size
+
8
)
/
16
+
(
fp_size
+
8
)
/
16
;
switch
(
func
->
CR
)
{
case
2
:
len
++
;
/* pacibsp */
/* fall through */
case
3
:
len
++
;
/* mov x29,sp */
len
++
;
/* stp x29,lr,[sp,0] */
if
(
local_size
<=
512
)
break
;
/* fall through */
case
0
:
case
1
:
if
(
local_size
)
len
++
;
/* sub sp,sp,#local_size */
if
(
local_size
>
4088
)
len
++
;
/* sub sp,sp,#4088 */
break
;
}
len
+=
4
*
func
->
H
;
if
(
offset
<
len
)
/* prolog */
{
skip
=
len
-
offset
;
}
else
if
(
offset
>=
func
->
FunctionLength
-
(
len
+
1
))
/* epilog */
{
skip
=
offset
-
(
func
->
FunctionLength
-
(
len
+
1
));
}
}
}
if
(
!
skip
)
{
if
(
func
->
CR
==
3
||
func
->
CR
==
2
)
{
DWORD64
*
fp
=
(
DWORD64
*
)
context
->
Fp
;
/* X[29] */
context
->
Sp
=
context
->
Fp
;
context
->
X
[
29
]
=
fp
[
0
];
context
->
X
[
30
]
=
fp
[
1
];
}
context
->
Sp
+=
local_size
;
if
(
fp_size
)
restore_fpregs
(
8
,
fp_regs
,
int_regs
,
context
,
ptrs
);
if
(
func
->
CR
==
1
)
restore_regs
(
30
,
1
,
int_regs
-
1
,
context
,
ptrs
);
restore_regs
(
19
,
func
->
RegI
,
-
saved_regs
,
context
,
ptrs
);
}
else
{
unsigned
int
pos
=
0
;
switch
(
func
->
CR
)
{
case
3
:
case
2
:
/* mov x29,sp */
if
(
pos
++
>=
skip
)
context
->
Sp
=
context
->
Fp
;
if
(
local_size
<=
512
)
{
/* stp x29,lr,[sp,-#local_size]! */
if
(
pos
++
>=
skip
)
restore_regs
(
29
,
2
,
-
local_size_regs
,
context
,
ptrs
);
break
;
}
/* stp x29,lr,[sp,0] */
if
(
pos
++
>=
skip
)
restore_regs
(
29
,
2
,
0
,
context
,
ptrs
);
/* fall through */
case
0
:
case
1
:
if
(
!
local_size
)
break
;
/* sub sp,sp,#local_size */
if
(
pos
++
>=
skip
)
context
->
Sp
+=
(
local_size
-
1
)
%
4088
+
1
;
if
(
local_size
>
4088
&&
pos
++
>=
skip
)
context
->
Sp
+=
4088
;
break
;
}
if
(
func
->
H
)
pos
+=
4
;
if
(
fp_size
)
{
if
(
func
->
RegF
%
2
==
0
&&
pos
++
>=
skip
)
/* str d%u,[sp,#fp_size] */
restore_fpregs
(
8
+
func
->
RegF
,
1
,
int_regs
+
fp_regs
-
1
,
context
,
ptrs
);
for
(
i
=
(
func
->
RegF
+
1
)
/
2
-
1
;
i
>=
0
;
i
--
)
{
if
(
pos
++
<
skip
)
continue
;
if
(
!
i
&&
!
int_size
)
/* stp d8,d9,[sp,-#regsave]! */
restore_fpregs
(
8
,
2
,
-
saved_regs
,
context
,
ptrs
);
else
/* stp dn,dn+1,[sp,#offset] */
restore_fpregs
(
8
+
2
*
i
,
2
,
int_regs
+
2
*
i
,
context
,
ptrs
);
}
}
if
(
func
->
RegI
%
2
)
{
if
(
pos
++
>=
skip
)
{
/* stp xn,lr,[sp,#offset] */
if
(
func
->
CR
==
1
)
restore_regs
(
30
,
1
,
int_regs
-
1
,
context
,
ptrs
);
/* str xn,[sp,#offset] */
restore_regs
(
18
+
func
->
RegI
,
1
,
(
func
->
RegI
>
1
)
?
func
->
RegI
-
1
:
-
saved_regs
,
context
,
ptrs
);
}
}
else
if
(
func
->
CR
==
1
)
{
/* str lr,[sp,#offset] */
if
(
pos
++
>=
skip
)
restore_regs
(
30
,
1
,
func
->
RegI
?
int_regs
-
1
:
-
saved_regs
,
context
,
ptrs
);
}
for
(
i
=
func
->
RegI
/
2
-
1
;
i
>=
0
;
i
--
)
{
if
(
pos
++
<
skip
)
continue
;
if
(
i
)
/* stp xn,xn+1,[sp,#offset] */
restore_regs
(
19
+
2
*
i
,
2
,
2
*
i
,
context
,
ptrs
);
else
/* stp x19,x20,[sp,-#regsave]! */
restore_regs
(
19
,
2
,
-
saved_regs
,
context
,
ptrs
);
}
}
if
(
func
->
CR
==
2
)
do_pac_auth
(
context
);
return
NULL
;
}
static
void
*
unwind_full_data
(
ULONG_PTR
base
,
ULONG_PTR
pc
,
ARM64_RUNTIME_FUNCTION
*
func
,
ARM64_NT_CONTEXT
*
context
,
PVOID
*
handler_data
,
KNONVOLATILE_CONTEXT_POINTERS_ARM64
*
ptrs
)
{
IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA
*
info
;
struct
unwind_info_epilog
*
info_epilog
;
unsigned
int
i
,
codes
,
epilogs
,
len
,
offset
;
void
*
data
;
BYTE
*
end
;
info
=
(
IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA
*
)((
char
*
)
base
+
func
->
UnwindData
);
data
=
info
+
1
;
epilogs
=
info
->
EpilogCount
;
codes
=
info
->
CodeWords
;
if
(
!
codes
&&
!
epilogs
)
{
struct
unwind_info_ext
*
infoex
=
data
;
codes
=
infoex
->
codes
;
epilogs
=
infoex
->
epilog
;
data
=
infoex
+
1
;
}
info_epilog
=
data
;
if
(
!
info
->
EpilogInHeader
)
data
=
info_epilog
+
epilogs
;
offset
=
((
pc
-
base
)
-
func
->
BeginAddress
)
/
4
;
end
=
(
BYTE
*
)
data
+
codes
*
4
;
TRACE
(
"function %I64x-%I64x: len=%#x ver=%u X=%u E=%u epilogs=%u codes=%u
\n
"
,
base
+
func
->
BeginAddress
,
base
+
func
->
BeginAddress
+
info
->
FunctionLength
*
4
,
info
->
FunctionLength
,
info
->
Version
,
info
->
ExceptionDataPresent
,
info
->
EpilogInHeader
,
epilogs
,
codes
*
4
);
/* check for prolog */
if
(
offset
<
codes
*
4
)
{
len
=
get_sequence_len
(
data
,
end
);
if
(
offset
<
len
)
{
process_unwind_codes
(
data
,
end
,
context
,
ptrs
,
len
-
offset
);
return
NULL
;
}
}
/* check for epilog */
if
(
!
info
->
EpilogInHeader
)
{
for
(
i
=
0
;
i
<
epilogs
;
i
++
)
{
if
(
offset
<
info_epilog
[
i
].
offset
)
break
;
if
(
offset
-
info_epilog
[
i
].
offset
<
codes
*
4
-
info_epilog
[
i
].
index
)
{
BYTE
*
ptr
=
(
BYTE
*
)
data
+
info_epilog
[
i
].
index
;
len
=
get_sequence_len
(
ptr
,
end
);
if
(
offset
<=
info_epilog
[
i
].
offset
+
len
)
{
process_unwind_codes
(
ptr
,
end
,
context
,
ptrs
,
offset
-
info_epilog
[
i
].
offset
);
return
NULL
;
}
}
}
}
else
if
(
info
->
FunctionLength
-
offset
<=
codes
*
4
-
epilogs
)
{
BYTE
*
ptr
=
(
BYTE
*
)
data
+
epilogs
;
len
=
get_sequence_len
(
ptr
,
end
)
+
1
;
if
(
offset
>=
info
->
FunctionLength
-
len
)
{
process_unwind_codes
(
ptr
,
end
,
context
,
ptrs
,
offset
-
(
info
->
FunctionLength
-
len
)
);
return
NULL
;
}
}
process_unwind_codes
(
data
,
end
,
context
,
ptrs
,
0
);
/* get handler since we are inside the main code */
if
(
info
->
ExceptionDataPresent
)
{
DWORD
*
handler_rva
=
(
DWORD
*
)
data
+
codes
;
*
handler_data
=
handler_rva
+
1
;
return
(
char
*
)
base
+
*
handler_rva
;
}
return
NULL
;
}
static
ARM64_RUNTIME_FUNCTION
*
find_function_info_arm64
(
ULONG_PTR
pc
,
ULONG_PTR
base
,
ARM64_RUNTIME_FUNCTION
*
func
,
ULONG
count
)
{
int
min
=
0
;
int
max
=
count
-
1
;
while
(
min
<=
max
)
{
int
pos
=
(
min
+
max
)
/
2
;
ULONG_PTR
start
=
base
+
func
[
pos
].
BeginAddress
;
if
(
pc
>=
start
)
{
ULONG
len
=
func
[
pos
].
Flag
?
func
[
pos
].
FunctionLength
:
((
IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA
*
)(
base
+
func
[
pos
].
UnwindData
))
->
FunctionLength
;
if
(
pc
<
start
+
4
*
len
)
return
func
+
pos
;
min
=
pos
+
1
;
}
else
max
=
pos
-
1
;
}
return
NULL
;
}
#ifdef __arm64ec__
#define RtlVirtualUnwind RtlVirtualUnwind_arm64
#define RtlLookupFunctionEntry RtlLookupFunctionEntry_arm64
#endif
/**********************************************************************
* RtlVirtualUnwind (NTDLL.@)
*/
PVOID
WINAPI
RtlVirtualUnwind
(
ULONG
type
,
ULONG_PTR
base
,
ULONG_PTR
pc
,
ARM64_RUNTIME_FUNCTION
*
func
,
ARM64_NT_CONTEXT
*
context
,
PVOID
*
handler_data
,
ULONG_PTR
*
frame_ret
,
KNONVOLATILE_CONTEXT_POINTERS_ARM64
*
ctx_ptr
)
{
void
*
handler
;
TRACE
(
"type %lx pc %I64x sp %I64x func %I64x
\n
"
,
type
,
pc
,
context
->
Sp
,
base
+
func
->
BeginAddress
);
*
handler_data
=
NULL
;
context
->
Pc
=
0
;
if
(
func
->
Flag
)
handler
=
unwind_packed_data
(
base
,
pc
,
func
,
context
,
ctx_ptr
);
else
handler
=
unwind_full_data
(
base
,
pc
,
func
,
context
,
handler_data
,
ctx_ptr
);
TRACE
(
"ret: pc=%I64x lr=%I64x sp=%I64x handler=%p
\n
"
,
context
->
Pc
,
context
->
Lr
,
context
->
Sp
,
handler
);
if
(
!
context
->
Pc
)
{
context
->
Pc
=
context
->
Lr
;
context
->
ContextFlags
|=
CONTEXT_UNWOUND_TO_CALL
;
}
*
frame_ret
=
context
->
Sp
;
return
handler
;
}
/**********************************************************************
* RtlLookupFunctionEntry (NTDLL.@)
*/
PARM64_RUNTIME_FUNCTION
WINAPI
RtlLookupFunctionEntry
(
ULONG_PTR
pc
,
ULONG_PTR
*
base
,
UNWIND_HISTORY_TABLE
*
table
)
{
ARM64_RUNTIME_FUNCTION
*
func
;
ULONG_PTR
dynbase
;
ULONG
size
;
if
((
func
=
(
ARM64_RUNTIME_FUNCTION
*
)
RtlLookupFunctionTable
(
pc
,
base
,
&
size
)))
return
find_function_info_arm64
(
pc
,
*
base
,
func
,
size
/
sizeof
(
*
func
));
if
((
func
=
(
ARM64_RUNTIME_FUNCTION
*
)
lookup_dynamic_function_table
(
pc
,
&
dynbase
,
&
size
)))
{
ARM64_RUNTIME_FUNCTION
*
ret
=
find_function_info_arm64
(
pc
,
dynbase
,
func
,
size
);
if
(
ret
)
*
base
=
dynbase
;
return
ret
;
}
*
base
=
0
;
return
NULL
;
}
/**********************************************************************
* RtlAddFunctionTable (NTDLL.@)
*/
#ifndef __arm64ec__
BOOLEAN
CDECL
RtlAddFunctionTable
(
RUNTIME_FUNCTION
*
table
,
DWORD
count
,
ULONG_PTR
base
)
{
ULONG_PTR
end
=
base
;
void
*
ret
;
if
(
count
)
{
RUNTIME_FUNCTION
*
func
=
table
+
count
-
1
;
ULONG
len
=
func
->
Flag
?
func
->
FunctionLength
:
((
IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA
*
)(
base
+
func
->
UnwindData
))
->
FunctionLength
;
end
+=
func
->
BeginAddress
+
4
*
len
;
}
return
!
RtlAddGrowableFunctionTable
(
&
ret
,
table
,
count
,
0
,
base
,
end
);
}
#else
#undef RtlVirtualUnwind
#undef RtlLookupFunctionEntry
#endif
#endif
/* __aarch64__ */
/***********************************************************************
* ARM support
*/
#ifdef __arm__
struct
unwind_info
{
DWORD
function_length
:
18
;
DWORD
version
:
2
;
DWORD
x
:
1
;
DWORD
e
:
1
;
DWORD
f
:
1
;
DWORD
epilog
:
5
;
DWORD
codes
:
4
;
};
struct
unwind_info_ext
{
WORD
epilog
;
BYTE
codes
;
BYTE
reserved
;
};
struct
unwind_info_epilog
{
DWORD
offset
:
18
;
DWORD
res
:
2
;
DWORD
cond
:
4
;
DWORD
index
:
8
;
};
static
const
BYTE
unwind_code_len
[
256
]
=
{
/* 00 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 20 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 40 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 60 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 80 */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* a0 */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* c0 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* e0 */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
3
,
4
,
3
,
4
,
1
,
1
,
1
,
1
,
1
};
static
const
BYTE
unwind_instr_len
[
256
]
=
{
/* 00 */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* 20 */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* 40 */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* 60 */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* 80 */
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
/* a0 */
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
/* c0 */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
/* e0 */
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
2
,
2
,
0
,
4
,
0
,
0
,
0
,
0
,
0
,
4
,
4
,
2
,
2
,
4
,
4
,
2
,
4
,
2
,
4
,
0
};
static
unsigned
int
get_sequence_len
(
BYTE
*
ptr
,
BYTE
*
end
,
int
include_end
)
{
unsigned
int
ret
=
0
;
while
(
ptr
<
end
)
{
if
(
*
ptr
>=
0xfd
)
{
if
(
*
ptr
<=
0xfe
&&
include_end
)
ret
+=
unwind_instr_len
[
*
ptr
];
break
;
}
ret
+=
unwind_instr_len
[
*
ptr
];
ptr
+=
unwind_code_len
[
*
ptr
];
}
return
ret
;
}
static
void
pop_regs_mask
(
int
mask
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
int
i
;
for
(
i
=
0
;
i
<=
12
;
i
++
)
{
if
(
!
(
mask
&
(
1
<<
i
)))
continue
;
if
(
ptrs
&&
i
>=
4
&&
i
<=
11
)
(
&
ptrs
->
R4
)[
i
-
4
]
=
(
DWORD
*
)
context
->
Sp
;
if
(
i
>=
4
)
(
&
context
->
R0
)[
i
]
=
*
(
DWORD
*
)
context
->
Sp
;
context
->
Sp
+=
4
;
}
}
static
void
pop_regs_range
(
int
last
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
int
i
;
for
(
i
=
4
;
i
<=
last
;
i
++
)
{
if
(
ptrs
)
(
&
ptrs
->
R4
)[
i
-
4
]
=
(
DWORD
*
)
context
->
Sp
;
(
&
context
->
R0
)[
i
]
=
*
(
DWORD
*
)
context
->
Sp
;
context
->
Sp
+=
4
;
}
}
static
void
pop_lr
(
int
increment
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
if
(
ptrs
)
ptrs
->
Lr
=
(
DWORD
*
)
context
->
Sp
;
context
->
Lr
=
*
(
DWORD
*
)
context
->
Sp
;
context
->
Sp
+=
increment
;
}
static
void
pop_fpregs_range
(
int
first
,
int
last
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
int
i
;
for
(
i
=
first
;
i
<=
last
;
i
++
)
{
if
(
ptrs
&&
i
>=
8
&&
i
<=
15
)
(
&
ptrs
->
D8
)[
i
-
8
]
=
(
ULONGLONG
*
)
context
->
Sp
;
context
->
D
[
i
]
=
*
(
ULONGLONG
*
)
context
->
Sp
;
context
->
Sp
+=
8
;
}
}
static
void
ms_opcode
(
BYTE
opcode
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
switch
(
opcode
)
{
case
1
:
/* MSFT_OP_MACHINE_FRAME */
context
->
Pc
=
((
DWORD
*
)
context
->
Sp
)[
1
];
context
->
Sp
=
((
DWORD
*
)
context
->
Sp
)[
0
];
context
->
ContextFlags
&=
~
CONTEXT_UNWOUND_TO_CALL
;
break
;
case
2
:
/* MSFT_OP_CONTEXT */
{
int
i
;
CONTEXT
*
src
=
(
CONTEXT
*
)
context
->
Sp
;
*
context
=
*
src
;
if
(
!
ptrs
)
break
;
for
(
i
=
0
;
i
<
8
;
i
++
)
(
&
ptrs
->
R4
)[
i
]
=
&
src
->
R4
+
i
;
ptrs
->
Lr
=
&
src
->
Lr
;
for
(
i
=
0
;
i
<
8
;
i
++
)
(
&
ptrs
->
D8
)[
i
]
=
&
src
->
D
[
i
+
8
];
break
;
}
default:
WARN
(
"unsupported code %02x
\n
"
,
opcode
);
break
;
}
}
static
void
process_unwind_codes
(
BYTE
*
ptr
,
BYTE
*
end
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
,
int
skip
)
{
unsigned
int
val
,
len
;
unsigned
int
i
;
/* skip codes */
while
(
ptr
<
end
&&
skip
)
{
if
(
*
ptr
>=
0xfd
)
break
;
skip
-=
unwind_instr_len
[
*
ptr
];
ptr
+=
unwind_code_len
[
*
ptr
];
}
while
(
ptr
<
end
)
{
len
=
unwind_code_len
[
*
ptr
];
if
(
ptr
+
len
>
end
)
break
;
val
=
0
;
for
(
i
=
0
;
i
<
len
;
i
++
)
val
=
(
val
<<
8
)
|
ptr
[
i
];
if
(
*
ptr
<=
0x7f
)
/* add sp, sp, #x */
context
->
Sp
+=
4
*
(
val
&
0x7f
);
else
if
(
*
ptr
<=
0xbf
)
/* pop {r0-r12,lr} */
{
pop_regs_mask
(
val
&
0x1fff
,
context
,
ptrs
);
if
(
val
&
0x2000
)
pop_lr
(
4
,
context
,
ptrs
);
}
else
if
(
*
ptr
<=
0xcf
)
/* mov sp, rX */
context
->
Sp
=
(
&
context
->
R0
)[
val
&
0x0f
];
else
if
(
*
ptr
<=
0xd7
)
/* pop {r4-rX,lr} */
{
pop_regs_range
(
(
val
&
0x03
)
+
4
,
context
,
ptrs
);
if
(
val
&
0x04
)
pop_lr
(
4
,
context
,
ptrs
);
}
else
if
(
*
ptr
<=
0xdf
)
/* pop {r4-rX,lr} */
{
pop_regs_range
(
(
val
&
0x03
)
+
8
,
context
,
ptrs
);
if
(
val
&
0x04
)
pop_lr
(
4
,
context
,
ptrs
);
}
else
if
(
*
ptr
<=
0xe7
)
/* vpop {d8-dX} */
pop_fpregs_range
(
8
,
(
val
&
0x07
)
+
8
,
context
,
ptrs
);
else
if
(
*
ptr
<=
0xeb
)
/* add sp, sp, #x */
context
->
Sp
+=
4
*
(
val
&
0x3ff
);
else
if
(
*
ptr
<=
0xed
)
/* pop {r0-r12,lr} */
{
pop_regs_mask
(
val
&
0xff
,
context
,
ptrs
);
if
(
val
&
0x100
)
pop_lr
(
4
,
context
,
ptrs
);
}
else
if
(
*
ptr
<=
0xee
)
/* Microsoft-specific 0x00-0x0f, Available 0x10-0xff */
ms_opcode
(
val
&
0xff
,
context
,
ptrs
);
else
if
(
*
ptr
<=
0xef
&&
((
val
&
0xff
)
<=
0x0f
))
/* ldr lr, [sp], #x */
pop_lr
(
4
*
(
val
&
0x0f
),
context
,
ptrs
);
else
if
(
*
ptr
<=
0xf4
)
/* Available */
WARN
(
"unsupported code %02x
\n
"
,
*
ptr
);
else
if
(
*
ptr
<=
0xf5
)
/* vpop {dS-dE} */
pop_fpregs_range
(
(
val
&
0xf0
)
>>
4
,
(
val
&
0x0f
),
context
,
ptrs
);
else
if
(
*
ptr
<=
0xf6
)
/* vpop {dS-dE} */
pop_fpregs_range
(
((
val
&
0xf0
)
>>
4
)
+
16
,
(
val
&
0x0f
)
+
16
,
context
,
ptrs
);
else
if
(
*
ptr
==
0xf7
||
*
ptr
==
0xf9
)
/* add sp, sp, #x */
context
->
Sp
+=
4
*
(
val
&
0xffff
);
else
if
(
*
ptr
==
0xf8
||
*
ptr
==
0xfa
)
/* add sp, sp, #x */
context
->
Sp
+=
4
*
(
val
&
0xffffff
);
else
if
(
*
ptr
<=
0xfc
)
/* nop */
/* nop */
;
else
/* end */
break
;
ptr
+=
len
;
}
}
static
void
*
unwind_packed_data
(
ULONG_PTR
base
,
ULONG_PTR
pc
,
RUNTIME_FUNCTION
*
func
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
int
i
,
pos
=
0
;
int
pf
=
0
,
ef
=
0
,
fpoffset
=
0
,
stack
=
func
->
StackAdjust
;
int
prologue_regmask
=
0
;
int
epilogue_regmask
=
0
;
unsigned
int
offset
,
len
;
BYTE
prologue
[
10
],
*
prologue_end
,
epilogue
[
20
],
*
epilogue_end
;
TRACE
(
"function %lx-%lx: len=%#x flag=%x ret=%u H=%u reg=%u R=%u L=%u C=%u stackadjust=%x
\n
"
,
base
+
func
->
BeginAddress
,
base
+
func
->
BeginAddress
+
func
->
FunctionLength
*
2
,
func
->
FunctionLength
,
func
->
Flag
,
func
->
Ret
,
func
->
H
,
func
->
Reg
,
func
->
R
,
func
->
L
,
func
->
C
,
func
->
StackAdjust
);
offset
=
(
pc
-
base
)
-
func
->
BeginAddress
;
if
(
func
->
StackAdjust
>=
0x03f4
)
{
pf
=
func
->
StackAdjust
&
0x04
;
ef
=
func
->
StackAdjust
&
0x08
;
stack
=
(
func
->
StackAdjust
&
3
)
+
1
;
}
if
(
!
func
->
R
||
pf
)
{
int
first
=
4
,
last
=
func
->
Reg
+
4
;
if
(
pf
)
{
first
=
(
~
func
->
StackAdjust
)
&
3
;
if
(
func
->
R
)
last
=
3
;
}
for
(
i
=
first
;
i
<=
last
;
i
++
)
prologue_regmask
|=
1
<<
i
;
fpoffset
=
last
+
1
-
first
;
}
if
(
!
func
->
R
||
ef
)
{
int
first
=
4
,
last
=
func
->
Reg
+
4
;
if
(
ef
)
{
first
=
(
~
func
->
StackAdjust
)
&
3
;
if
(
func
->
R
)
last
=
3
;
}
for
(
i
=
first
;
i
<=
last
;
i
++
)
epilogue_regmask
|=
1
<<
i
;
}
if
(
func
->
C
)
{
prologue_regmask
|=
1
<<
11
;
epilogue_regmask
|=
1
<<
11
;
}
if
(
func
->
L
)
{
prologue_regmask
|=
1
<<
14
;
/* lr */
if
(
func
->
Ret
!=
0
)
epilogue_regmask
|=
1
<<
14
;
/* lr */
else
if
(
!
func
->
H
)
epilogue_regmask
|=
1
<<
15
;
/* pc */
}
/* Synthesize prologue opcodes */
if
(
stack
&&
!
pf
)
{
if
(
stack
<=
0x7f
)
{
prologue
[
pos
++
]
=
stack
;
/* sub sp, sp, #x */
}
else
{
prologue
[
pos
++
]
=
0xe8
|
(
stack
>>
8
);
/* sub.w sp, sp, #x */
prologue
[
pos
++
]
=
stack
&
0xff
;
}
}
if
(
func
->
R
&&
func
->
Reg
!=
7
)
prologue
[
pos
++
]
=
0xe0
|
func
->
Reg
;
/* vpush {d8-dX} */
if
(
func
->
C
&&
fpoffset
==
0
)
prologue
[
pos
++
]
=
0xfb
;
/* mov r11, sp - handled as nop16 */
else
if
(
func
->
C
)
prologue
[
pos
++
]
=
0xfc
;
/* add r11, sp, #x - handled as nop32 */
if
(
prologue_regmask
&
0xf00
)
/* r8-r11 set */
{
int
bitmask
=
prologue_regmask
&
0x1fff
;
if
(
prologue_regmask
&
(
1
<<
14
))
/* lr */
bitmask
|=
0x2000
;
prologue
[
pos
++
]
=
0x80
|
(
bitmask
>>
8
);
/* push.w {r0-r12,lr} */
prologue
[
pos
++
]
=
bitmask
&
0xff
;
}
else
if
(
prologue_regmask
)
/* r0-r7, lr set */
{
int
bitmask
=
prologue_regmask
&
0xff
;
if
(
prologue_regmask
&
(
1
<<
14
))
/* lr */
bitmask
|=
0x100
;
prologue
[
pos
++
]
=
0xec
|
(
bitmask
>>
8
);
/* push {r0-r7,lr} */
prologue
[
pos
++
]
=
bitmask
&
0xff
;
}
if
(
func
->
H
)
prologue
[
pos
++
]
=
0x04
;
/* push {r0-r3} - handled as sub sp, sp, #16 */
prologue
[
pos
++
]
=
0xff
;
/* end */
prologue_end
=
&
prologue
[
pos
];
/* Synthesize epilogue opcodes */
pos
=
0
;
if
(
stack
&&
!
ef
)
{
if
(
stack
<=
0x7f
)
{
epilogue
[
pos
++
]
=
stack
;
/* sub sp, sp, #x */
}
else
{
epilogue
[
pos
++
]
=
0xe8
|
(
stack
>>
8
);
/* sub.w sp, sp, #x */
epilogue
[
pos
++
]
=
stack
&
0xff
;
}
}
if
(
func
->
R
&&
func
->
Reg
!=
7
)
epilogue
[
pos
++
]
=
0xe0
|
func
->
Reg
;
/* vpush {d8-dX} */
if
(
epilogue_regmask
&
0x7f00
)
/* r8-r11, lr set */
{
int
bitmask
=
epilogue_regmask
&
0x1fff
;
if
(
epilogue_regmask
&
(
3
<<
14
))
/* lr or pc */
bitmask
|=
0x2000
;
epilogue
[
pos
++
]
=
0x80
|
(
bitmask
>>
8
);
/* push.w {r0-r12,lr} */
epilogue
[
pos
++
]
=
bitmask
&
0xff
;
}
else
if
(
epilogue_regmask
)
/* r0-r7, pc set */
{
int
bitmask
=
epilogue_regmask
&
0xff
;
if
(
epilogue_regmask
&
(
1
<<
15
))
/* pc */
bitmask
|=
0x100
;
/* lr */
epilogue
[
pos
++
]
=
0xec
|
(
bitmask
>>
8
);
/* push {r0-r7,lr} */
epilogue
[
pos
++
]
=
bitmask
&
0xff
;
}
if
(
func
->
H
&&
!
(
func
->
L
&&
func
->
Ret
==
0
))
epilogue
[
pos
++
]
=
0x04
;
/* add sp, sp, #16 */
else
if
(
func
->
H
&&
(
func
->
L
&&
func
->
Ret
==
0
))
{
epilogue
[
pos
++
]
=
0xef
;
/* ldr lr, [sp], #20 */
epilogue
[
pos
++
]
=
5
;
}
if
(
func
->
Ret
==
1
)
epilogue
[
pos
++
]
=
0xfd
;
/* bx lr */
else
if
(
func
->
Ret
==
2
)
epilogue
[
pos
++
]
=
0xfe
;
/* b address */
else
epilogue
[
pos
++
]
=
0xff
;
/* end */
epilogue_end
=
&
epilogue
[
pos
];
if
(
func
->
Flag
==
1
&&
offset
<
4
*
(
prologue_end
-
prologue
))
{
/* Check prologue */
len
=
get_sequence_len
(
prologue
,
prologue_end
,
0
);
if
(
offset
<
len
)
{
process_unwind_codes
(
prologue
,
prologue_end
,
context
,
ptrs
,
len
-
offset
);
return
NULL
;
}
}
if
(
func
->
Ret
!=
3
&&
2
*
func
->
FunctionLength
-
offset
<=
4
*
(
epilogue_end
-
epilogue
))
{
/* Check epilogue */
len
=
get_sequence_len
(
epilogue
,
epilogue_end
,
1
);
if
(
offset
>=
2
*
func
->
FunctionLength
-
len
)
{
process_unwind_codes
(
epilogue
,
epilogue_end
,
context
,
ptrs
,
offset
-
(
2
*
func
->
FunctionLength
-
len
)
);
return
NULL
;
}
}
/* Execute full prologue */
process_unwind_codes
(
prologue
,
prologue_end
,
context
,
ptrs
,
0
);
return
NULL
;
}
static
void
*
unwind_full_data
(
ULONG_PTR
base
,
ULONG_PTR
pc
,
RUNTIME_FUNCTION
*
func
,
CONTEXT
*
context
,
PVOID
*
handler_data
,
KNONVOLATILE_CONTEXT_POINTERS
*
ptrs
)
{
struct
unwind_info
*
info
;
struct
unwind_info_epilog
*
info_epilog
;
unsigned
int
i
,
codes
,
epilogs
,
len
,
offset
;
void
*
data
;
BYTE
*
end
;
info
=
(
struct
unwind_info
*
)((
char
*
)
base
+
func
->
UnwindData
);
data
=
info
+
1
;
epilogs
=
info
->
epilog
;
codes
=
info
->
codes
;
if
(
!
codes
&&
!
epilogs
)
{
struct
unwind_info_ext
*
infoex
=
data
;
codes
=
infoex
->
codes
;
epilogs
=
infoex
->
epilog
;
data
=
infoex
+
1
;
}
info_epilog
=
data
;
if
(
!
info
->
e
)
data
=
info_epilog
+
epilogs
;
offset
=
(
pc
-
base
)
-
func
->
BeginAddress
;
end
=
(
BYTE
*
)
data
+
codes
*
4
;
TRACE
(
"function %lx-%lx: len=%#x ver=%u X=%u E=%u F=%u epilogs=%u codes=%u
\n
"
,
base
+
func
->
BeginAddress
,
base
+
func
->
BeginAddress
+
info
->
function_length
*
2
,
info
->
function_length
,
info
->
version
,
info
->
x
,
info
->
e
,
info
->
f
,
epilogs
,
codes
*
4
);
/* check for prolog */
if
(
offset
<
codes
*
4
*
4
&&
!
info
->
f
)
{
len
=
get_sequence_len
(
data
,
end
,
0
);
if
(
offset
<
len
)
{
process_unwind_codes
(
data
,
end
,
context
,
ptrs
,
len
-
offset
);
return
NULL
;
}
}
/* check for epilog */
if
(
!
info
->
e
)
{
for
(
i
=
0
;
i
<
epilogs
;
i
++
)
{
/* TODO: Currently not checking epilogue conditions. */
if
(
offset
<
2
*
info_epilog
[
i
].
offset
)
break
;
if
(
offset
-
2
*
info_epilog
[
i
].
offset
<
(
codes
*
4
-
info_epilog
[
i
].
index
)
*
4
)
{
BYTE
*
ptr
=
(
BYTE
*
)
data
+
info_epilog
[
i
].
index
;
len
=
get_sequence_len
(
ptr
,
end
,
1
);
if
(
offset
<=
2
*
info_epilog
[
i
].
offset
+
len
)
{
process_unwind_codes
(
ptr
,
end
,
context
,
ptrs
,
offset
-
2
*
info_epilog
[
i
].
offset
);
return
NULL
;
}
}
}
}
else
if
(
2
*
info
->
function_length
-
offset
<=
(
codes
*
4
-
epilogs
)
*
4
)
{
BYTE
*
ptr
=
(
BYTE
*
)
data
+
epilogs
;
len
=
get_sequence_len
(
ptr
,
end
,
1
);
if
(
offset
>=
2
*
info
->
function_length
-
len
)
{
process_unwind_codes
(
ptr
,
end
,
context
,
ptrs
,
offset
-
(
2
*
info
->
function_length
-
len
)
);
return
NULL
;
}
}
process_unwind_codes
(
data
,
end
,
context
,
ptrs
,
0
);
/* get handler since we are inside the main code */
if
(
info
->
x
)
{
DWORD
*
handler_rva
=
(
DWORD
*
)
data
+
codes
;
*
handler_data
=
handler_rva
+
1
;
return
(
char
*
)
base
+
*
handler_rva
;
}
return
NULL
;
}
static
RUNTIME_FUNCTION
*
find_function_info
(
ULONG_PTR
pc
,
ULONG_PTR
base
,
RUNTIME_FUNCTION
*
func
,
ULONG
count
)
{
int
min
=
0
;
int
max
=
count
-
1
;
while
(
min
<=
max
)
{
int
pos
=
(
min
+
max
)
/
2
;
ULONG_PTR
start
=
base
+
(
func
[
pos
].
BeginAddress
&
~
1
);
if
(
pc
>=
start
)
{
struct
unwind_info
*
info
=
(
struct
unwind_info
*
)(
base
+
func
[
pos
].
UnwindData
);
if
(
pc
<
start
+
2
*
(
func
[
pos
].
Flag
?
func
[
pos
].
FunctionLength
:
info
->
function_length
))
return
func
+
pos
;
min
=
pos
+
1
;
}
else
max
=
pos
-
1
;
}
return
NULL
;
}
/***********************************************************************
* RtlVirtualUnwind (NTDLL.@)
*/
PVOID
WINAPI
RtlVirtualUnwind
(
ULONG
type
,
ULONG_PTR
base
,
ULONG_PTR
pc
,
RUNTIME_FUNCTION
*
func
,
CONTEXT
*
context
,
PVOID
*
handler_data
,
ULONG_PTR
*
frame_ret
,
KNONVOLATILE_CONTEXT_POINTERS
*
ctx_ptr
)
{
void
*
handler
;
TRACE
(
"type %lx pc %Ix sp %lx func %lx
\n
"
,
type
,
pc
,
context
->
Sp
,
base
+
func
->
BeginAddress
);
*
handler_data
=
NULL
;
context
->
Pc
=
0
;
if
(
func
->
Flag
)
handler
=
unwind_packed_data
(
base
,
pc
,
func
,
context
,
ctx_ptr
);
else
handler
=
unwind_full_data
(
base
,
pc
,
func
,
context
,
handler_data
,
ctx_ptr
);
TRACE
(
"ret: pc=%lx lr=%lx sp=%lx handler=%p
\n
"
,
context
->
Pc
,
context
->
Lr
,
context
->
Sp
,
handler
);
if
(
!
context
->
Pc
)
{
context
->
Pc
=
context
->
Lr
;
context
->
ContextFlags
|=
CONTEXT_UNWOUND_TO_CALL
;
}
*
frame_ret
=
context
->
Sp
;
return
handler
;
}
/**********************************************************************
* RtlLookupFunctionEntry (NTDLL.@)
*/
PRUNTIME_FUNCTION
WINAPI
RtlLookupFunctionEntry
(
ULONG_PTR
pc
,
ULONG_PTR
*
base
,
UNWIND_HISTORY_TABLE
*
table
)
{
RUNTIME_FUNCTION
*
func
;
ULONG_PTR
dynbase
;
ULONG
size
;
if
((
func
=
RtlLookupFunctionTable
(
pc
,
base
,
&
size
)))
return
find_function_info
(
pc
,
*
base
,
func
,
size
/
sizeof
(
*
func
));
if
((
func
=
lookup_dynamic_function_table
(
pc
,
&
dynbase
,
&
size
)))
{
RUNTIME_FUNCTION
*
ret
=
find_function_info
(
pc
,
dynbase
,
func
,
size
);
if
(
ret
)
*
base
=
dynbase
;
return
ret
;
}
*
base
=
0
;
return
NULL
;
}
/**********************************************************************
* RtlAddFunctionTable (NTDLL.@)
*/
BOOLEAN
CDECL
RtlAddFunctionTable
(
RUNTIME_FUNCTION
*
table
,
DWORD
count
,
ULONG_PTR
base
)
{
ULONG_PTR
end
=
base
;
void
*
ret
;
if
(
count
)
{
RUNTIME_FUNCTION
*
func
=
table
+
count
-
1
;
struct
unwind_info
*
info
=
(
struct
unwind_info
*
)(
base
+
func
->
UnwindData
);
end
+=
func
->
BeginAddress
+
2
*
(
func
->
Flag
?
func
->
FunctionLength
:
info
->
function_length
);
}
return
!
RtlAddGrowableFunctionTable
(
&
ret
,
table
,
count
,
0
,
base
,
end
);
}
#endif
/* __arm__ */
/***********************************************************************
* x86-64 support
*/
#ifdef __x86_64__
union
handler_data
{
RUNTIME_FUNCTION
chain
;
ULONG
handler
;
};
struct
opcode
{
BYTE
offset
;
BYTE
code
:
4
;
BYTE
info
:
4
;
};
struct
UNWIND_INFO
{
BYTE
version
:
3
;
BYTE
flags
:
5
;
BYTE
prolog
;
BYTE
count
;
BYTE
frame_reg
:
4
;
BYTE
frame_offset
:
4
;
struct
opcode
opcodes
[
1
];
/* info->count entries */
/* followed by handler_data */
};
#define UWOP_PUSH_NONVOL 0
#define UWOP_ALLOC_LARGE 1
#define UWOP_ALLOC_SMALL 2
#define UWOP_SET_FPREG 3
#define UWOP_SAVE_NONVOL 4
#define UWOP_SAVE_NONVOL_FAR 5
#define UWOP_EPILOG 6
#define UWOP_SAVE_XMM128 8
#define UWOP_SAVE_XMM128_FAR 9
#define UWOP_PUSH_MACHFRAME 10
static
void
dump_unwind_info
(
ULONG64
base
,
RUNTIME_FUNCTION
*
function
)
{
static
const
char
*
const
reg_names
[
16
]
=
{
"rax"
,
"rcx"
,
"rdx"
,
"rbx"
,
"rsp"
,
"rbp"
,
"rsi"
,
"rdi"
,
"r8"
,
"r9"
,
"r10"
,
"r11"
,
"r12"
,
"r13"
,
"r14"
,
"r15"
};
union
handler_data
*
handler_data
;
struct
UNWIND_INFO
*
info
;
unsigned
int
i
,
count
;
TRACE
(
"**** func %lx-%lx
\n
"
,
function
->
BeginAddress
,
function
->
EndAddress
);
for
(;;)
{
if
(
function
->
UnwindData
&
1
)
{
RUNTIME_FUNCTION
*
next
=
(
RUNTIME_FUNCTION
*
)((
char
*
)
base
+
(
function
->
UnwindData
&
~
1
));
TRACE
(
"unwind info for function %p-%p chained to function %p-%p
\n
"
,
(
char
*
)
base
+
function
->
BeginAddress
,
(
char
*
)
base
+
function
->
EndAddress
,
(
char
*
)
base
+
next
->
BeginAddress
,
(
char
*
)
base
+
next
->
EndAddress
);
function
=
next
;
continue
;
}
info
=
(
struct
UNWIND_INFO
*
)((
char
*
)
base
+
function
->
UnwindData
);
TRACE
(
"unwind info at %p flags %x prolog 0x%x bytes function %p-%p
\n
"
,
info
,
info
->
flags
,
info
->
prolog
,
(
char
*
)
base
+
function
->
BeginAddress
,
(
char
*
)
base
+
function
->
EndAddress
);
if
(
info
->
frame_reg
)
TRACE
(
" frame register %s offset 0x%x(%%rsp)
\n
"
,
reg_names
[
info
->
frame_reg
],
info
->
frame_offset
*
16
);
for
(
i
=
0
;
i
<
info
->
count
;
i
++
)
{
TRACE
(
" 0x%x: "
,
info
->
opcodes
[
i
].
offset
);
switch
(
info
->
opcodes
[
i
].
code
)
{
case
UWOP_PUSH_NONVOL
:
TRACE
(
"pushq %%%s
\n
"
,
reg_names
[
info
->
opcodes
[
i
].
info
]
);
break
;
case
UWOP_ALLOC_LARGE
:
if
(
info
->
opcodes
[
i
].
info
)
{
count
=
*
(
DWORD
*
)
&
info
->
opcodes
[
i
+
1
];
i
+=
2
;
}
else
{
count
=
*
(
USHORT
*
)
&
info
->
opcodes
[
i
+
1
]
*
8
;
i
++
;
}
TRACE
(
"subq $0x%x,%%rsp
\n
"
,
count
);
break
;
case
UWOP_ALLOC_SMALL
:
count
=
(
info
->
opcodes
[
i
].
info
+
1
)
*
8
;
TRACE
(
"subq $0x%x,%%rsp
\n
"
,
count
);
break
;
case
UWOP_SET_FPREG
:
TRACE
(
"leaq 0x%x(%%rsp),%s
\n
"
,
info
->
frame_offset
*
16
,
reg_names
[
info
->
frame_reg
]
);
break
;
case
UWOP_SAVE_NONVOL
:
count
=
*
(
USHORT
*
)
&
info
->
opcodes
[
i
+
1
]
*
8
;
TRACE
(
"movq %%%s,0x%x(%%rsp)
\n
"
,
reg_names
[
info
->
opcodes
[
i
].
info
],
count
);
i
++
;
break
;
case
UWOP_SAVE_NONVOL_FAR
:
count
=
*
(
DWORD
*
)
&
info
->
opcodes
[
i
+
1
];
TRACE
(
"movq %%%s,0x%x(%%rsp)
\n
"
,
reg_names
[
info
->
opcodes
[
i
].
info
],
count
);
i
+=
2
;
break
;
case
UWOP_SAVE_XMM128
:
count
=
*
(
USHORT
*
)
&
info
->
opcodes
[
i
+
1
]
*
16
;
TRACE
(
"movaps %%xmm%u,0x%x(%%rsp)
\n
"
,
info
->
opcodes
[
i
].
info
,
count
);
i
++
;
break
;
case
UWOP_SAVE_XMM128_FAR
:
count
=
*
(
DWORD
*
)
&
info
->
opcodes
[
i
+
1
];
TRACE
(
"movaps %%xmm%u,0x%x(%%rsp)
\n
"
,
info
->
opcodes
[
i
].
info
,
count
);
i
+=
2
;
break
;
case
UWOP_PUSH_MACHFRAME
:
TRACE
(
"PUSH_MACHFRAME %u
\n
"
,
info
->
opcodes
[
i
].
info
);
break
;
case
UWOP_EPILOG
:
if
(
info
->
version
==
2
)
{
unsigned
int
offset
;
if
(
info
->
opcodes
[
i
].
info
)
offset
=
info
->
opcodes
[
i
].
offset
;
else
offset
=
(
info
->
opcodes
[
i
+
1
].
info
<<
8
)
+
info
->
opcodes
[
i
+
1
].
offset
;
TRACE
(
"epilog %p-%p
\n
"
,
(
char
*
)
base
+
function
->
EndAddress
-
offset
,
(
char
*
)
base
+
function
->
EndAddress
-
offset
+
info
->
opcodes
[
i
].
offset
);
i
+=
1
;
break
;
}
default:
FIXME
(
"unknown code %u
\n
"
,
info
->
opcodes
[
i
].
code
);
break
;
}
}
handler_data
=
(
union
handler_data
*
)
&
info
->
opcodes
[(
info
->
count
+
1
)
&
~
1
];
if
(
info
->
flags
&
UNW_FLAG_CHAININFO
)
{
TRACE
(
" chained to function %p-%p
\n
"
,
(
char
*
)
base
+
handler_data
->
chain
.
BeginAddress
,
(
char
*
)
base
+
handler_data
->
chain
.
EndAddress
);
function
=
&
handler_data
->
chain
;
continue
;
}
if
(
info
->
flags
&
(
UNW_FLAG_EHANDLER
|
UNW_FLAG_UHANDLER
))
TRACE
(
" handler %p data at %p
\n
"
,
(
char
*
)
base
+
handler_data
->
handler
,
&
handler_data
->
handler
+
1
);
break
;
}
}
static
ULONG64
get_int_reg
(
CONTEXT
*
context
,
int
reg
)
{
return
*
(
&
context
->
Rax
+
reg
);
}
static
void
set_int_reg
(
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ctx_ptr
,
int
reg
,
ULONG64
*
val
)
{
*
(
&
context
->
Rax
+
reg
)
=
*
val
;
if
(
ctx_ptr
)
ctx_ptr
->
IntegerContext
[
reg
]
=
val
;
}
static
void
set_float_reg
(
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ctx_ptr
,
int
reg
,
M128A
*
val
)
{
/* Use a memcpy() to avoid issues if val is misaligned. */
memcpy
(
&
context
->
Xmm0
+
reg
,
val
,
sizeof
(
*
val
));
if
(
ctx_ptr
)
ctx_ptr
->
FloatingContext
[
reg
]
=
val
;
}
static
int
get_opcode_size
(
struct
opcode
op
)
{
switch
(
op
.
code
)
{
case
UWOP_ALLOC_LARGE
:
return
2
+
(
op
.
info
!=
0
);
case
UWOP_SAVE_NONVOL
:
case
UWOP_SAVE_XMM128
:
case
UWOP_EPILOG
:
return
2
;
case
UWOP_SAVE_NONVOL_FAR
:
case
UWOP_SAVE_XMM128_FAR
:
return
3
;
default:
return
1
;
}
}
static
BOOL
is_inside_epilog
(
BYTE
*
pc
,
ULONG64
base
,
const
RUNTIME_FUNCTION
*
function
)
{
/* add or lea must be the first instruction, and it must have a rex.W prefix */
if
((
pc
[
0
]
&
0xf8
)
==
0x48
)
{
switch
(
pc
[
1
])
{
case
0x81
:
/* add $nnnn,%rsp */
if
(
pc
[
0
]
==
0x48
&&
pc
[
2
]
==
0xc4
)
{
pc
+=
7
;
break
;
}
return
FALSE
;
case
0x83
:
/* add $n,%rsp */
if
(
pc
[
0
]
==
0x48
&&
pc
[
2
]
==
0xc4
)
{
pc
+=
4
;
break
;
}
return
FALSE
;
case
0x8d
:
/* lea n(reg),%rsp */
if
(
pc
[
0
]
&
0x06
)
return
FALSE
;
/* rex.RX must be cleared */
if
(((
pc
[
2
]
>>
3
)
&
7
)
!=
4
)
return
FALSE
;
/* dest reg mus be %rsp */
if
((
pc
[
2
]
&
7
)
==
4
)
return
FALSE
;
/* no SIB byte allowed */
if
((
pc
[
2
]
>>
6
)
==
1
)
/* 8-bit offset */
{
pc
+=
4
;
break
;
}
if
((
pc
[
2
]
>>
6
)
==
2
)
/* 32-bit offset */
{
pc
+=
7
;
break
;
}
return
FALSE
;
}
}
/* now check for various pop instructions */
for
(;;)
{
if
((
*
pc
&
0xf0
)
==
0x40
)
pc
++
;
/* rex prefix */
switch
(
*
pc
)
{
case
0x58
:
/* pop %rax/%r8 */
case
0x59
:
/* pop %rcx/%r9 */
case
0x5a
:
/* pop %rdx/%r10 */
case
0x5b
:
/* pop %rbx/%r11 */
case
0x5c
:
/* pop %rsp/%r12 */
case
0x5d
:
/* pop %rbp/%r13 */
case
0x5e
:
/* pop %rsi/%r14 */
case
0x5f
:
/* pop %rdi/%r15 */
pc
++
;
continue
;
case
0xc2
:
/* ret $nn */
case
0xc3
:
/* ret */
return
TRUE
;
case
0xe9
:
/* jmp nnnn */
pc
+=
5
+
*
(
LONG
*
)(
pc
+
1
);
if
(
pc
-
(
BYTE
*
)
base
>=
function
->
BeginAddress
&&
pc
-
(
BYTE
*
)
base
<
function
->
EndAddress
)
continue
;
break
;
case
0xeb
:
/* jmp n */
pc
+=
2
+
(
signed
char
)
pc
[
1
];
if
(
pc
-
(
BYTE
*
)
base
>=
function
->
BeginAddress
&&
pc
-
(
BYTE
*
)
base
<
function
->
EndAddress
)
continue
;
break
;
case
0xf3
:
/* rep; ret (for amd64 prediction bug) */
return
pc
[
1
]
==
0xc3
;
}
return
FALSE
;
}
}
/* execute a function epilog, which must have been validated with is_inside_epilog() */
static
void
interpret_epilog
(
BYTE
*
pc
,
CONTEXT
*
context
,
KNONVOLATILE_CONTEXT_POINTERS
*
ctx_ptr
)
{
for
(;;)
{
BYTE
rex
=
0
;
if
((
*
pc
&
0xf0
)
==
0x40
)
rex
=
*
pc
++
&
0x0f
;
/* rex prefix */
switch
(
*
pc
)
{
case
0x58
:
/* pop %rax/r8 */
case
0x59
:
/* pop %rcx/r9 */
case
0x5a
:
/* pop %rdx/r10 */
case
0x5b
:
/* pop %rbx/r11 */
case
0x5c
:
/* pop %rsp/r12 */
case
0x5d
:
/* pop %rbp/r13 */
case
0x5e
:
/* pop %rsi/r14 */
case
0x5f
:
/* pop %rdi/r15 */
set_int_reg
(
context
,
ctx_ptr
,
*
pc
-
0x58
+
(
rex
&
1
)
*
8
,
(
ULONG64
*
)
context
->
Rsp
);
context
->
Rsp
+=
sizeof
(
ULONG64
);
pc
++
;
continue
;
case
0x81
:
/* add $nnnn,%rsp */
context
->
Rsp
+=
*
(
LONG
*
)(
pc
+
2
);
pc
+=
2
+
sizeof
(
LONG
);
continue
;
case
0x83
:
/* add $n,%rsp */
context
->
Rsp
+=
(
signed
char
)
pc
[
2
];
pc
+=
3
;
continue
;
case
0x8d
:
if
((
pc
[
1
]
>>
6
)
==
1
)
/* lea n(reg),%rsp */
{
context
->
Rsp
=
get_int_reg
(
context
,
(
pc
[
1
]
&
7
)
+
(
rex
&
1
)
*
8
)
+
(
signed
char
)
pc
[
2
];
pc
+=
3
;
}
else
/* lea nnnn(reg),%rsp */
{
context
->
Rsp
=
get_int_reg
(
context
,
(
pc
[
1
]
&
7
)
+
(
rex
&
1
)
*
8
)
+
*
(
LONG
*
)(
pc
+
2
);
pc
+=
2
+
sizeof
(
LONG
);
}
continue
;
case
0xc2
:
/* ret $nn */
context
->
Rip
=
*
(
ULONG64
*
)
context
->
Rsp
;
context
->
Rsp
+=
sizeof
(
ULONG64
)
+
*
(
WORD
*
)(
pc
+
1
);
return
;
case
0xc3
:
/* ret */
case
0xf3
:
/* rep; ret */
context
->
Rip
=
*
(
ULONG64
*
)
context
->
Rsp
;
context
->
Rsp
+=
sizeof
(
ULONG64
);
return
;
case
0xe9
:
/* jmp nnnn */
pc
+=
5
+
*
(
LONG
*
)(
pc
+
1
);
continue
;
case
0xeb
:
/* jmp n */
pc
+=
2
+
(
signed
char
)
pc
[
1
];
continue
;
}
return
;
}
}
static
RUNTIME_FUNCTION
*
find_function_info
(
ULONG_PTR
pc
,
ULONG_PTR
base
,
RUNTIME_FUNCTION
*
func
,
ULONG
count
)
{
int
min
=
0
;
int
max
=
count
-
1
;
while
(
min
<=
max
)
{
int
pos
=
(
min
+
max
)
/
2
;
if
(
pc
<
base
+
func
[
pos
].
BeginAddress
)
max
=
pos
-
1
;
else
if
(
pc
>=
base
+
func
[
pos
].
EndAddress
)
min
=
pos
+
1
;
else
{
func
+=
pos
;
while
(
func
->
UnwindData
&
1
)
/* follow chained entry */
func
=
(
RUNTIME_FUNCTION
*
)(
base
+
(
func
->
UnwindData
&
~
1
));
return
func
;
}
}
return
NULL
;
}
/**********************************************************************
* RtlVirtualUnwind (NTDLL.@)
*/
PVOID
WINAPI
RtlVirtualUnwind
(
ULONG
type
,
ULONG64
base
,
ULONG64
pc
,
RUNTIME_FUNCTION
*
function
,
CONTEXT
*
context
,
PVOID
*
data
,
ULONG64
*
frame_ret
,
KNONVOLATILE_CONTEXT_POINTERS
*
ctx_ptr
)
{
union
handler_data
*
handler_data
;
ULONG64
frame
,
off
;
struct
UNWIND_INFO
*
info
;
unsigned
int
i
,
prolog_offset
;
BOOL
mach_frame
=
FALSE
;
TRACE
(
"type %lx rip %I64x rsp %I64x
\n
"
,
type
,
pc
,
context
->
Rsp
);
if
(
TRACE_ON
(
unwind
))
dump_unwind_info
(
base
,
function
);
frame
=
*
frame_ret
=
context
->
Rsp
;
for
(;;)
{
info
=
(
struct
UNWIND_INFO
*
)((
char
*
)
base
+
function
->
UnwindData
);
handler_data
=
(
union
handler_data
*
)
&
info
->
opcodes
[(
info
->
count
+
1
)
&
~
1
];
if
(
info
->
version
!=
1
&&
info
->
version
!=
2
)
{
FIXME
(
"unknown unwind info version %u at %p
\n
"
,
info
->
version
,
info
);
return
NULL
;
}
if
(
info
->
frame_reg
)
frame
=
get_int_reg
(
context
,
info
->
frame_reg
)
-
info
->
frame_offset
*
16
;
/* check if in prolog */
if
(
pc
>=
base
+
function
->
BeginAddress
&&
pc
<
base
+
function
->
BeginAddress
+
info
->
prolog
)
{
TRACE
(
"inside prolog.
\n
"
);
prolog_offset
=
pc
-
base
-
function
->
BeginAddress
;
}
else
{
prolog_offset
=
~
0
;
/* Since Win10 1809 epilogue does not have a special treatment in case of zero opcode count. */
if
(
info
->
count
&&
is_inside_epilog
(
(
BYTE
*
)
pc
,
base
,
function
))
{
TRACE
(
"inside epilog.
\n
"
);
interpret_epilog
(
(
BYTE
*
)
pc
,
context
,
ctx_ptr
);
*
frame_ret
=
frame
;
return
NULL
;
}
}
for
(
i
=
0
;
i
<
info
->
count
;
i
+=
get_opcode_size
(
info
->
opcodes
[
i
]))
{
if
(
prolog_offset
<
info
->
opcodes
[
i
].
offset
)
continue
;
/* skip it */
switch
(
info
->
opcodes
[
i
].
code
)
{
case
UWOP_PUSH_NONVOL
:
/* pushq %reg */
set_int_reg
(
context
,
ctx_ptr
,
info
->
opcodes
[
i
].
info
,
(
ULONG64
*
)
context
->
Rsp
);
context
->
Rsp
+=
sizeof
(
ULONG64
);
break
;
case
UWOP_ALLOC_LARGE
:
/* subq $nn,%rsp */
if
(
info
->
opcodes
[
i
].
info
)
context
->
Rsp
+=
*
(
DWORD
*
)
&
info
->
opcodes
[
i
+
1
];
else
context
->
Rsp
+=
*
(
USHORT
*
)
&
info
->
opcodes
[
i
+
1
]
*
8
;
break
;
case
UWOP_ALLOC_SMALL
:
/* subq $n,%rsp */
context
->
Rsp
+=
(
info
->
opcodes
[
i
].
info
+
1
)
*
8
;
break
;
case
UWOP_SET_FPREG
:
/* leaq nn(%rsp),%framereg */
context
->
Rsp
=
*
frame_ret
=
frame
;
break
;
case
UWOP_SAVE_NONVOL
:
/* movq %reg,n(%rsp) */
off
=
frame
+
*
(
USHORT
*
)
&
info
->
opcodes
[
i
+
1
]
*
8
;
set_int_reg
(
context
,
ctx_ptr
,
info
->
opcodes
[
i
].
info
,
(
ULONG64
*
)
off
);
break
;
case
UWOP_SAVE_NONVOL_FAR
:
/* movq %reg,nn(%rsp) */
off
=
frame
+
*
(
DWORD
*
)
&
info
->
opcodes
[
i
+
1
];
set_int_reg
(
context
,
ctx_ptr
,
info
->
opcodes
[
i
].
info
,
(
ULONG64
*
)
off
);
break
;
case
UWOP_SAVE_XMM128
:
/* movaps %xmmreg,n(%rsp) */
off
=
frame
+
*
(
USHORT
*
)
&
info
->
opcodes
[
i
+
1
]
*
16
;
set_float_reg
(
context
,
ctx_ptr
,
info
->
opcodes
[
i
].
info
,
(
M128A
*
)
off
);
break
;
case
UWOP_SAVE_XMM128_FAR
:
/* movaps %xmmreg,nn(%rsp) */
off
=
frame
+
*
(
DWORD
*
)
&
info
->
opcodes
[
i
+
1
];
set_float_reg
(
context
,
ctx_ptr
,
info
->
opcodes
[
i
].
info
,
(
M128A
*
)
off
);
break
;
case
UWOP_PUSH_MACHFRAME
:
if
(
info
->
flags
&
UNW_FLAG_CHAININFO
)
{
FIXME
(
"PUSH_MACHFRAME with chained unwind info.
\n
"
);
break
;
}
if
(
i
+
get_opcode_size
(
info
->
opcodes
[
i
])
<
info
->
count
)
{
FIXME
(
"PUSH_MACHFRAME is not the last opcode.
\n
"
);
break
;
}
if
(
info
->
opcodes
[
i
].
info
)
context
->
Rsp
+=
0x8
;
context
->
Rip
=
*
(
ULONG64
*
)
context
->
Rsp
;
context
->
Rsp
=
*
(
ULONG64
*
)(
context
->
Rsp
+
24
);
mach_frame
=
TRUE
;
break
;
case
UWOP_EPILOG
:
if
(
info
->
version
==
2
)
break
;
/* nothing to do */
default:
FIXME
(
"unknown code %u
\n
"
,
info
->
opcodes
[
i
].
code
);
break
;
}
}
if
(
!
(
info
->
flags
&
UNW_FLAG_CHAININFO
))
break
;
function
=
&
handler_data
->
chain
;
/* restart with the chained info */
}
if
(
!
mach_frame
)
{
/* now pop return address */
context
->
Rip
=
*
(
ULONG64
*
)
context
->
Rsp
;
context
->
Rsp
+=
sizeof
(
ULONG64
);
}
if
(
!
(
info
->
flags
&
type
))
return
NULL
;
/* no matching handler */
if
(
prolog_offset
!=
~
0
)
return
NULL
;
/* inside prolog */
*
data
=
&
handler_data
->
handler
+
1
;
return
(
char
*
)
base
+
handler_data
->
handler
;
}
/**********************************************************************
* RtlLookupFunctionEntry (NTDLL.@)
*/
PRUNTIME_FUNCTION
WINAPI
RtlLookupFunctionEntry
(
ULONG_PTR
pc
,
ULONG_PTR
*
base
,
UNWIND_HISTORY_TABLE
*
table
)
{
RUNTIME_FUNCTION
*
func
;
ULONG_PTR
dynbase
;
ULONG
size
;
#ifdef __arm64ec__
if
(
RtlIsEcCode
(
(
void
*
)
pc
))
return
(
RUNTIME_FUNCTION
*
)
RtlLookupFunctionEntry_arm64
(
pc
,
base
,
table
);
#endif
if
((
func
=
RtlLookupFunctionTable
(
pc
,
base
,
&
size
)))
return
find_function_info
(
pc
,
*
base
,
func
,
size
/
sizeof
(
*
func
));
if
((
func
=
lookup_dynamic_function_table
(
pc
,
&
dynbase
,
&
size
)))
{
RUNTIME_FUNCTION
*
ret
=
find_function_info
(
pc
,
dynbase
,
func
,
size
);
if
(
ret
)
*
base
=
dynbase
;
return
ret
;
}
*
base
=
0
;
return
NULL
;
}
/**********************************************************************
* RtlAddFunctionTable (NTDLL.@)
*/
BOOLEAN
CDECL
RtlAddFunctionTable
(
RUNTIME_FUNCTION
*
table
,
DWORD
count
,
ULONG_PTR
base
)
{
ULONG_PTR
end
=
base
;
void
*
ret
;
if
(
count
)
end
+=
table
[
count
-
1
].
EndAddress
;
return
!
RtlAddGrowableFunctionTable
(
&
ret
,
table
,
count
,
0
,
base
,
end
);
}
#endif
/* __x86_64__ */
#endif
/* !__i386__ */
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