1 // Copyright (c) 2019, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "pe_util.h"
31
32 #include <windows.h>
33 #include <winnt.h>
34 #include <atlbase.h>
35 #include <ImageHlp.h>
36
37 #include <functional>
38
39 #include "common/windows/string_utils-inl.h"
40 #include "common/windows/guid_string.h"
41
42 namespace {
43
44 /*
45 * Not defined in WinNT.h for some reason. Definitions taken from:
46 * http://uninformed.org/index.cgi?v=4&a=1&p=13
47 *
48 */
49 typedef unsigned char UBYTE;
50
51 #if !defined(_WIN64)
52 #define UNW_FLAG_EHANDLER 0x01
53 #define UNW_FLAG_UHANDLER 0x02
54 #define UNW_FLAG_CHAININFO 0x04
55 #endif
56
57 union UnwindCode {
58 struct {
59 UBYTE offset_in_prolog;
60 UBYTE unwind_operation_code : 4;
61 UBYTE operation_info : 4;
62 };
63 USHORT frame_offset;
64 };
65
66 enum UnwindOperationCodes {
67 UWOP_PUSH_NONVOL = 0, /* info == register number */
68 UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */
69 UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */
70 UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
71 UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */
72 UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
73 // XXX: these are missing from MSDN!
74 // See: http://www.osronline.com/ddkx/kmarch/64bitamd_4rs7.htm
75 UWOP_SAVE_XMM,
76 UWOP_SAVE_XMM_FAR,
77 UWOP_SAVE_XMM128, /* info == XMM reg number, offset in next slot */
78 UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
79 UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */
80 };
81
82 // See: http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
83 // Note: some fields removed as we don't use them.
84 struct UnwindInfo {
85 UBYTE version : 3;
86 UBYTE flags : 5;
87 UBYTE size_of_prolog;
88 UBYTE count_of_codes;
89 UBYTE frame_register : 4;
90 UBYTE frame_offset : 4;
91 UnwindCode unwind_code[1];
92 };
93
94 struct CV_INFO_PDB70 {
95 ULONG cv_signature;
96 GUID signature;
97 ULONG age;
98 CHAR pdb_filename[ANYSIZE_ARRAY];
99 };
100
101 #define CV_SIGNATURE_RSDS 'SDSR'
102
103 // A helper class to scope a PLOADED_IMAGE.
104 class AutoImage {
105 public:
AutoImage(PLOADED_IMAGE img)106 explicit AutoImage(PLOADED_IMAGE img) : img_(img) {}
~AutoImage()107 ~AutoImage() {
108 if (img_)
109 ImageUnload(img_);
110 }
111
operator PLOADED_IMAGE()112 operator PLOADED_IMAGE() { return img_; }
operator ->()113 PLOADED_IMAGE operator->() { return img_; }
114
115 private:
116 PLOADED_IMAGE img_;
117 };
118 } // namespace
119
120 namespace google_breakpad {
121
122 using std::unique_ptr;
123 using google_breakpad::GUIDString;
124
ReadModuleInfo(const wstring & pe_file,PDBModuleInfo * info)125 bool ReadModuleInfo(const wstring & pe_file, PDBModuleInfo * info) {
126 // Convert wchar to native charset because ImageLoad only takes
127 // a PSTR as input.
128 string img_file;
129 if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {
130 fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",
131 pe_file.c_str());
132 return false;
133 }
134
135 AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));
136 if (!img) {
137 fprintf(stderr, "Failed to load %s\n", img_file.c_str());
138 return false;
139 }
140
141 info->cpu = FileHeaderMachineToCpuString(
142 img->FileHeader->FileHeader.Machine);
143
144 PIMAGE_OPTIONAL_HEADER64 optional_header =
145 &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader;
146
147 // Search debug directories for a guid signature & age
148 DWORD debug_rva = optional_header->
149 DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
150 DWORD debug_size = optional_header->
151 DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
152 PIMAGE_DEBUG_DIRECTORY debug_directories =
153 static_cast<PIMAGE_DEBUG_DIRECTORY>(
154 ImageRvaToVa(img->FileHeader,
155 img->MappedAddress,
156 debug_rva,
157 &img->LastRvaSection));
158
159 for (DWORD i = 0; i < debug_size / sizeof(*debug_directories); i++) {
160 if (debug_directories[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW ||
161 debug_directories[i].SizeOfData < sizeof(CV_INFO_PDB70)) {
162 continue;
163 }
164
165 struct CV_INFO_PDB70* cv_info = static_cast<CV_INFO_PDB70*>(ImageRvaToVa(
166 img->FileHeader,
167 img->MappedAddress,
168 debug_directories[i].AddressOfRawData,
169 &img->LastRvaSection));
170 if (cv_info->cv_signature != CV_SIGNATURE_RSDS) {
171 continue;
172 }
173
174 info->debug_identifier = GenerateDebugIdentifier(cv_info->age,
175 cv_info->signature);
176
177 // This code assumes that the pdb_filename is stored as ASCII without
178 // multibyte characters, but it's not clear if that's true.
179 size_t debug_file_length = strnlen_s(cv_info->pdb_filename, MAX_PATH);
180 if (debug_file_length < 0 || debug_file_length >= MAX_PATH) {
181 fprintf(stderr, "PE debug directory is corrupt.\n");
182 return false;
183 }
184 std::string debug_file(cv_info->pdb_filename, debug_file_length);
185 if (!WindowsStringUtils::safe_mbstowcs(debug_file, &info->debug_file)) {
186 fprintf(stderr, "PDB filename '%s' contains unrecognized characters.\n",
187 debug_file.c_str());
188 return false;
189 }
190 info->debug_file = WindowsStringUtils::GetBaseName(info->debug_file);
191
192 return true;
193 }
194
195 fprintf(stderr, "Image is missing debug information.\n");
196 return false;
197 }
198
ReadPEInfo(const wstring & pe_file,PEModuleInfo * info)199 bool ReadPEInfo(const wstring & pe_file, PEModuleInfo * info) {
200 // Convert wchar to native charset because ImageLoad only takes
201 // a PSTR as input.
202 string img_file;
203 if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {
204 fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",
205 pe_file.c_str());
206 return false;
207 }
208
209 AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));
210 if (!img) {
211 fprintf(stderr, "Failed to open PE file: %S\n", pe_file.c_str());
212 return false;
213 }
214
215 info->code_file = WindowsStringUtils::GetBaseName(pe_file);
216
217 // The date and time that the file was created by the linker.
218 DWORD TimeDateStamp = img->FileHeader->FileHeader.TimeDateStamp;
219 // The size of the file in bytes, including all headers.
220 DWORD SizeOfImage = 0;
221 PIMAGE_OPTIONAL_HEADER64 opt =
222 &((PIMAGE_NT_HEADERS64)img->FileHeader)->OptionalHeader;
223 if (opt->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
224 // 64-bit PE file.
225 SizeOfImage = opt->SizeOfImage;
226 }
227 else {
228 // 32-bit PE file.
229 SizeOfImage = img->FileHeader->OptionalHeader.SizeOfImage;
230 }
231 wchar_t code_identifier[32];
232 swprintf(code_identifier,
233 sizeof(code_identifier) / sizeof(code_identifier[0]),
234 L"%08X%X", TimeDateStamp, SizeOfImage);
235 info->code_identifier = code_identifier;
236
237 return true;
238 }
239
PrintPEFrameData(const wstring & pe_file,FILE * out_file)240 bool PrintPEFrameData(const wstring & pe_file, FILE * out_file)
241 {
242 // Convert wchar to native charset because ImageLoad only takes
243 // a PSTR as input.
244 string img_file;
245 if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {
246 fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",
247 pe_file.c_str());
248 return false;
249 }
250
251 AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));
252 if (!img) {
253 fprintf(stderr, "Failed to load %s\n", img_file.c_str());
254 return false;
255 }
256 PIMAGE_OPTIONAL_HEADER64 optional_header =
257 &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader;
258 if (optional_header->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
259 fprintf(stderr, "Not a PE32+ image\n");
260 return false;
261 }
262
263 // Read Exception Directory
264 DWORD exception_rva = optional_header->
265 DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
266 DWORD exception_size = optional_header->
267 DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
268 PIMAGE_RUNTIME_FUNCTION_ENTRY funcs =
269 static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
270 ImageRvaToVa(img->FileHeader,
271 img->MappedAddress,
272 exception_rva,
273 &img->LastRvaSection));
274 for (DWORD i = 0; i < exception_size / sizeof(*funcs); i++) {
275 DWORD unwind_rva = funcs[i].UnwindInfoAddress;
276 // handle chaining
277 while (unwind_rva & 0x1) {
278 unwind_rva ^= 0x1;
279 PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func =
280 static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
281 ImageRvaToVa(img->FileHeader,
282 img->MappedAddress,
283 unwind_rva,
284 &img->LastRvaSection));
285 unwind_rva = chained_func->UnwindInfoAddress;
286 }
287
288 UnwindInfo *unwind_info = static_cast<UnwindInfo *>(
289 ImageRvaToVa(img->FileHeader,
290 img->MappedAddress,
291 unwind_rva,
292 &img->LastRvaSection));
293
294 DWORD stack_size = 8; // minimal stack size is 8 for RIP
295 DWORD rip_offset = 8;
296 do {
297 for (UBYTE c = 0; c < unwind_info->count_of_codes; c++) {
298 UnwindCode *unwind_code = &unwind_info->unwind_code[c];
299 switch (unwind_code->unwind_operation_code) {
300 case UWOP_PUSH_NONVOL: {
301 stack_size += 8;
302 break;
303 }
304 case UWOP_ALLOC_LARGE: {
305 if (unwind_code->operation_info == 0) {
306 c++;
307 if (c < unwind_info->count_of_codes)
308 stack_size += (unwind_code + 1)->frame_offset * 8;
309 }
310 else {
311 c += 2;
312 if (c < unwind_info->count_of_codes)
313 stack_size += (unwind_code + 1)->frame_offset |
314 ((unwind_code + 2)->frame_offset << 16);
315 }
316 break;
317 }
318 case UWOP_ALLOC_SMALL: {
319 stack_size += unwind_code->operation_info * 8 + 8;
320 break;
321 }
322 case UWOP_SET_FPREG:
323 case UWOP_SAVE_XMM:
324 case UWOP_SAVE_XMM_FAR:
325 break;
326 case UWOP_SAVE_NONVOL:
327 case UWOP_SAVE_XMM128: {
328 c++; // skip slot with offset
329 break;
330 }
331 case UWOP_SAVE_NONVOL_FAR:
332 case UWOP_SAVE_XMM128_FAR: {
333 c += 2; // skip 2 slots with offset
334 break;
335 }
336 case UWOP_PUSH_MACHFRAME: {
337 if (unwind_code->operation_info) {
338 stack_size += 88;
339 }
340 else {
341 stack_size += 80;
342 }
343 rip_offset += 80;
344 break;
345 }
346 }
347 }
348 if (unwind_info->flags & UNW_FLAG_CHAININFO) {
349 PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func =
350 reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
351 (unwind_info->unwind_code +
352 ((unwind_info->count_of_codes + 1) & ~1)));
353
354 unwind_info = static_cast<UnwindInfo *>(
355 ImageRvaToVa(img->FileHeader,
356 img->MappedAddress,
357 chained_func->UnwindInfoAddress,
358 &img->LastRvaSection));
359 }
360 else {
361 unwind_info = NULL;
362 }
363 } while (unwind_info);
364 fprintf(out_file, "STACK CFI INIT %lx %lx .cfa: $rsp .ra: .cfa %lu - ^\n",
365 funcs[i].BeginAddress,
366 funcs[i].EndAddress - funcs[i].BeginAddress, rip_offset);
367 fprintf(out_file, "STACK CFI %lx .cfa: $rsp %lu +\n",
368 funcs[i].BeginAddress, stack_size);
369 }
370
371 return true;
372 }
373
GenerateDebugIdentifier(DWORD age,GUID signature)374 wstring GenerateDebugIdentifier(DWORD age, GUID signature)
375 {
376 // Use the same format that the MS symbol server uses in filesystem
377 // hierarchies.
378 wchar_t age_string[9];
379 swprintf(age_string, sizeof(age_string) / sizeof(age_string[0]),
380 L"%x", age);
381
382 // remove when VC++7.1 is no longer supported
383 age_string[sizeof(age_string) / sizeof(age_string[0]) - 1] = L'\0';
384
385 wstring debug_identifier = GUIDString::GUIDToSymbolServerWString(&signature);
386 debug_identifier.append(age_string);
387
388 return debug_identifier;
389 }
390
GenerateDebugIdentifier(DWORD age,DWORD signature)391 wstring GenerateDebugIdentifier(DWORD age, DWORD signature)
392 {
393 // Use the same format that the MS symbol server uses in filesystem
394 // hierarchies.
395 wchar_t identifier_string[17];
396 swprintf(identifier_string,
397 sizeof(identifier_string) / sizeof(identifier_string[0]),
398 L"%08X%x", signature, age);
399
400 // remove when VC++7.1 is no longer supported
401 identifier_string[sizeof(identifier_string) /
402 sizeof(identifier_string[0]) - 1] = L'\0';
403
404 return wstring(identifier_string);
405 }
406
407 } // namespace google_breakpad
408