1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #if XP_WIN && HAVE_64BIT_BUILD
8 # include "Win64ModuleUnwindMetadata.h"
10 # include "MinidumpAnalyzerUtils.h"
14 # include <imagehlp.h>
19 # include "mozilla/WindowsUnwindInfo.h"
21 using namespace mozilla
;
23 namespace CrashReporter
{
25 ModuleUnwindParser::~ModuleUnwindParser() {
31 void* ModuleUnwindParser::RvaToVa(ULONG aRva
) {
32 return ImageRvaToVa(mImg
->FileHeader
, mImg
->MappedAddress
, aRva
,
33 &mImg
->LastRvaSection
);
36 ModuleUnwindParser::ModuleUnwindParser(const std::string
& aPath
)
38 // Convert wchar to native charset because ImageLoad only takes
40 std::string code_file
= UTF8ToMBCS(aPath
);
42 mImg
= ImageLoad((PSTR
)code_file
.c_str(), NULL
);
43 if (!mImg
|| !mImg
->FileHeader
) {
47 PIMAGE_OPTIONAL_HEADER64 optional_header
= &mImg
->FileHeader
->OptionalHeader
;
48 if (optional_header
->Magic
!= IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
53 optional_header
->DataDirectory
[IMAGE_DIRECTORY_ENTRY_EXCEPTION
]
56 DWORD exception_size
=
57 optional_header
->DataDirectory
[IMAGE_DIRECTORY_ENTRY_EXCEPTION
].Size
;
59 auto funcs
= (PIMAGE_RUNTIME_FUNCTION_ENTRY
)RvaToVa(exception_rva
);
64 for (DWORD i
= 0; i
< exception_size
/ sizeof(*funcs
); i
++) {
65 mUnwindMap
[funcs
[i
].BeginAddress
] = &funcs
[i
];
69 bool ModuleUnwindParser::GenerateCFIForFunction(
70 IMAGE_RUNTIME_FUNCTION_ENTRY
& aFunc
, UnwindCFI
& aRet
) {
71 DWORD unwind_rva
= aFunc
.UnwindInfoAddress
;
72 // Holds RVA to all visited IMAGE_RUNTIME_FUNCTION_ENTRY, to avoid
73 // circular references.
74 std::set
<DWORD
> visited
;
76 // Follow chained function entries
77 while (unwind_rva
& 0x1) {
80 if (visited
.end() != visited
.find(unwind_rva
)) {
83 visited
.insert(unwind_rva
);
85 auto chained_func
= (PIMAGE_RUNTIME_FUNCTION_ENTRY
)RvaToVa(unwind_rva
);
89 unwind_rva
= chained_func
->UnwindInfoAddress
;
92 visited
.insert(unwind_rva
);
94 auto unwind_info
= (UnwindInfo
*)RvaToVa(unwind_rva
);
99 DWORD stack_size
= 8; // minimal stack size is 8 for RIP
100 DWORD rip_offset
= 8;
102 for (uint8_t c
= 0; c
< unwind_info
->count_of_codes
; c
++) {
103 UnwindCode
* unwind_code
= &unwind_info
->unwind_code
[c
];
104 switch (unwind_code
->unwind_operation_code
) {
105 case UWOP_PUSH_NONVOL
: {
109 case UWOP_ALLOC_LARGE
: {
110 if (unwind_code
->operation_info
== 0) {
112 if (c
< unwind_info
->count_of_codes
) {
113 stack_size
+= (unwind_code
+ 1)->frame_offset
* 8;
117 if (c
< unwind_info
->count_of_codes
) {
118 stack_size
+= (unwind_code
+ 1)->frame_offset
|
119 ((unwind_code
+ 2)->frame_offset
<< 16);
124 case UWOP_ALLOC_SMALL
: {
125 stack_size
+= unwind_code
->operation_info
* 8 + 8;
129 // To correctly track RSP when it's been transferred to another
130 // register, we would need to emit CFI records for every unwind op.
131 // For simplicity, don't emit CFI records for this function as
132 // we know it will be incorrect after this point.
134 case UWOP_SAVE_NONVOL
:
135 case UWOP_SAVE_XMM
: // also v2 UWOP_EPILOG
136 case UWOP_SAVE_XMM128
: {
137 c
++; // skip slot with offset
140 case UWOP_SAVE_NONVOL_FAR
:
141 case UWOP_SAVE_XMM_FAR
: // also v2 UWOP_SPARE
142 case UWOP_SAVE_XMM128_FAR
: {
143 c
+= 2; // skip 2 slots with offset
146 case UWOP_PUSH_MACHFRAME
: {
147 if (unwind_code
->operation_info
) {
161 if (unwind_info
->flags
& UNW_FLAG_CHAININFO
) {
162 auto chained_func
= (PIMAGE_RUNTIME_FUNCTION_ENTRY
)((
163 unwind_info
->unwind_code
+ ((unwind_info
->count_of_codes
+ 1) & ~1)));
165 if (visited
.end() != visited
.find(chained_func
->UnwindInfoAddress
)) {
166 return false; // Circular reference
169 visited
.insert(chained_func
->UnwindInfoAddress
);
171 unwind_info
= (UnwindInfo
*)RvaToVa(chained_func
->UnwindInfoAddress
);
173 unwind_info
= nullptr;
175 } while (unwind_info
);
177 aRet
.beginAddress
= aFunc
.BeginAddress
;
178 aRet
.size
= aFunc
.EndAddress
- aFunc
.BeginAddress
;
179 aRet
.stackSize
= stack_size
;
180 aRet
.ripOffset
= rip_offset
;
184 // For unit testing we sometimes need any address that's valid in this module.
185 // Just return the first address we know of.
187 ModuleUnwindParser::GetAnyOffsetAddr() const {
188 if (mUnwindMap
.size() < 1) {
191 return mUnwindMap
.begin()->first
;
194 bool ModuleUnwindParser::GetCFI(DWORD aAddress
, UnwindCFI
& aRet
) {
195 // Figure out the begin address of the requested address.
196 auto itUW
= mUnwindMap
.lower_bound(aAddress
+ 1);
197 if (itUW
== mUnwindMap
.begin()) {
198 return false; // address before this module.
202 // Ensure that the function entry is big enough to contain this address.
203 IMAGE_RUNTIME_FUNCTION_ENTRY
& func
= *itUW
->second
;
204 if (aAddress
> func
.EndAddress
) {
208 // Do we have CFI for this function already?
209 auto itCFI
= mCFIMap
.find(aAddress
);
210 if (itCFI
!= mCFIMap
.end()) {
211 aRet
= itCFI
->second
;
216 if (!GenerateCFIForFunction(func
, aRet
)) {
220 mCFIMap
[func
.BeginAddress
] = aRet
;
224 } // namespace CrashReporter
226 #endif // XP_WIN && HAVE_64BIT_BUILD