1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | /*++ |
6 | |
7 | Module Name: |
8 | |
9 | remote-unwind.cpp |
10 | |
11 | Abstract: |
12 | |
13 | Implementation of out of context unwind using libunwind8 |
14 | remote unwind API. |
15 | |
16 | This file contains code based on libunwind8 |
17 | |
18 | Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P. |
19 | Contributed by David Mosberger-Tang <davidm@hpl.hp.com> |
20 | |
21 | Permission is hereby granted, free of charge, to any person obtaining |
22 | a copy of this software and associated documentation files (the |
23 | "Software"), to deal in the Software without restriction, including |
24 | without limitation the rights to use, copy, modify, merge, publish, |
25 | distribute, sublicense, and/or sell copies of the Software, and to |
26 | permit persons to whom the Software is furnished to do so, subject to |
27 | the following conditions: |
28 | |
29 | The above copyright notice and this permission notice shall be |
30 | included in all copies or substantial portions of the Software. |
31 | |
32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
33 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
34 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
35 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
36 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
37 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
38 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
39 | |
40 | --*/ |
41 | |
42 | #include "config.h" |
43 | #include "pal/palinternal.h" |
44 | #include "pal/dbgmsg.h" |
45 | #include "pal/critsect.h" |
46 | #include "pal/debug.h" |
47 | #include "pal_endian.h" |
48 | #include "pal.h" |
49 | #include <dlfcn.h> |
50 | |
51 | // Sub-headers included from the libunwind.h contain an empty struct |
52 | // and clang issues a warning. Until the libunwind is fixed, disable |
53 | // the warning. |
54 | #pragma clang diagnostic push |
55 | #pragma clang diagnostic ignored "-Wextern-c-compat" |
56 | #include <libunwind.h> |
57 | #pragma clang diagnostic pop |
58 | |
59 | SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); |
60 | |
61 | // Only used on the AMD64 build |
62 | #if defined(_AMD64_) && defined(HAVE_UNW_GET_ACCESSORS) |
63 | |
64 | #include <elf.h> |
65 | #include <link.h> |
66 | |
67 | #ifndef ElfW |
68 | #define ElfW(foo) Elf_ ## foo |
69 | #endif |
70 | #define Ehdr ElfW(Ehdr) |
71 | #define Phdr ElfW(Phdr) |
72 | #define Shdr ElfW(Shdr) |
73 | #define Nhdr ElfW(Nhdr) |
74 | #define Dyn ElfW(Dyn) |
75 | |
76 | extern void UnwindContextToWinContext(unw_cursor_t *cursor, CONTEXT *winContext); |
77 | extern void GetContextPointers(unw_cursor_t *cursor, unw_context_t *unwContext, KNONVOLATILE_CONTEXT_POINTERS *contextPointers); |
78 | |
79 | typedef struct _libunwindInfo |
80 | { |
81 | SIZE_T BaseAddress; |
82 | CONTEXT *Context; |
83 | UnwindReadMemoryCallback ReadMemory; |
84 | } libunwindInfo; |
85 | |
86 | #define DW_EH_VERSION 1 |
87 | |
88 | // DWARF Pointer-Encoding (PEs). |
89 | // |
90 | // Pointer-Encodings were invented for the GCC exception-handling |
91 | // support for C++, but they represent a rather generic way of |
92 | // describing the format in which an address/pointer is stored. |
93 | // The Pointer-Encoding format is partially documented in Linux Base |
94 | // Spec v1.3 (http://www.linuxbase.org/spec/). |
95 | |
96 | #define DW_EH_PE_FORMAT_MASK 0x0f // format of the encoded value |
97 | #define DW_EH_PE_APPL_MASK 0x70 // how the value is to be applied |
98 | #define DW_EH_PE_indirect 0x80 // Flag bit. If set, the resulting pointer is the |
99 | // address of the word that contains the final address |
100 | // Pointer-encoding formats |
101 | #define DW_EH_PE_omit 0xff |
102 | #define DW_EH_PE_ptr 0x00 // pointer-sized unsigned value |
103 | #define DW_EH_PE_uleb128 0x01 // unsigned LE base-128 value |
104 | #define DW_EH_PE_udata2 0x02 // unsigned 16-bit value |
105 | #define DW_EH_PE_udata4 0x03 // unsigned 32-bit value |
106 | #define DW_EH_PE_udata8 0x04 // unsigned 64-bit value |
107 | #define DW_EH_PE_sleb128 0x09 // signed LE base-128 value |
108 | #define DW_EH_PE_sdata2 0x0a // signed 16-bit value |
109 | #define DW_EH_PE_sdata4 0x0b // signed 32-bit value |
110 | #define DW_EH_PE_sdata8 0x0c // signed 64-bit value |
111 | |
112 | // Pointer-encoding application |
113 | #define DW_EH_PE_absptr 0x00 // absolute value |
114 | #define DW_EH_PE_pcrel 0x10 // rel. to addr. of encoded value |
115 | #define DW_EH_PE_textrel 0x20 // text-relative (GCC-specific???) |
116 | #define DW_EH_PE_datarel 0x30 // data-relative |
117 | |
118 | // The following are not documented by LSB v1.3, yet they are used by |
119 | // GCC, presumably they aren't documented by LSB since they aren't |
120 | // used on Linux |
121 | #define DW_EH_PE_funcrel 0x40 // start-of-procedure-relative |
122 | #define DW_EH_PE_aligned 0x50 // aligned pointer |
123 | |
124 | #define DWARF_CIE_VERSION 3 // GCC emits version 1??? |
125 | |
126 | // DWARF frame header |
127 | typedef struct _eh_frame_hdr |
128 | { |
129 | unsigned char version; |
130 | unsigned char eh_frame_ptr_enc; |
131 | unsigned char fde_count_enc; |
132 | unsigned char table_enc; |
133 | // The rest of the header is variable-length and consists of the |
134 | // following members: |
135 | // |
136 | // encoded_t eh_frame_ptr; |
137 | // encoded_t fde_count; |
138 | // struct |
139 | // { |
140 | // encoded_t start_ip; // first address covered by this FDE |
141 | // encoded_t fde_offset; // offset of the FDE |
142 | // } binary_search_table[fde_count]; |
143 | } eh_frame_hdr; |
144 | |
145 | // "DW_EH_PE_datarel|DW_EH_PE_sdata4" encoded fde table entry |
146 | typedef struct _table_entry |
147 | { |
148 | int32_t start_ip; |
149 | int32_t fde_offset; |
150 | } table_entry; |
151 | |
152 | // DWARF unwind info |
153 | typedef struct dwarf_cie_info |
154 | { |
155 | unw_word_t cie_instr_start; // start addr. of CIE "initial_instructions" |
156 | unw_word_t cie_instr_end; // end addr. of CIE "initial_instructions" |
157 | unw_word_t fde_instr_start; // start addr. of FDE "instructions" |
158 | unw_word_t fde_instr_end; // end addr. of FDE "instructions" |
159 | unw_word_t code_align; // code-alignment factor |
160 | unw_word_t data_align; // data-alignment factor |
161 | unw_word_t ret_addr_column; // column of return-address register |
162 | unw_word_t handler; // address of personality-routine |
163 | uint16_t abi; |
164 | uint16_t tag; |
165 | uint8_t fde_encoding; |
166 | uint8_t lsda_encoding; |
167 | unsigned int sized_augmentation : 1; |
168 | unsigned int have_abi_marker : 1; |
169 | unsigned int signal_frame : 1; |
170 | } dwarf_cie_info_t; |
171 | |
172 | static bool |
173 | ReadValue8(const libunwindInfo* info, unw_word_t* addr, uint8_t* valp) |
174 | { |
175 | uint8_t value; |
176 | if (!info->ReadMemory((PVOID)*addr, &value, sizeof(value))) { |
177 | return false; |
178 | } |
179 | *addr += sizeof(value); |
180 | *valp = value; |
181 | return true; |
182 | } |
183 | |
184 | static bool |
185 | ReadValue16(const libunwindInfo* info, unw_word_t* addr, uint16_t* valp) |
186 | { |
187 | uint16_t value; |
188 | if (!info->ReadMemory((PVOID)*addr, &value, sizeof(value))) { |
189 | return false; |
190 | } |
191 | *addr += sizeof(value); |
192 | *valp = VAL16(value); |
193 | return true; |
194 | } |
195 | |
196 | static bool |
197 | ReadValue32(const libunwindInfo* info, unw_word_t* addr, uint32_t* valp) |
198 | { |
199 | uint32_t value; |
200 | if (!info->ReadMemory((PVOID)*addr, &value, sizeof(value))) { |
201 | return false; |
202 | } |
203 | *addr += sizeof(value); |
204 | *valp = VAL32(value); |
205 | return true; |
206 | } |
207 | |
208 | static bool |
209 | ReadValue64(const libunwindInfo* info, unw_word_t* addr, uint64_t* valp) |
210 | { |
211 | uint64_t value; |
212 | if (!info->ReadMemory((PVOID)*addr, &value, sizeof(value))) { |
213 | return false; |
214 | } |
215 | *addr += sizeof(value); |
216 | *valp = VAL64(value); |
217 | return true; |
218 | } |
219 | |
220 | static bool |
221 | ReadPointer(const libunwindInfo* info, unw_word_t* addr, unw_word_t* valp) |
222 | { |
223 | #ifdef BIT64 |
224 | uint64_t val64; |
225 | if (ReadValue64(info, addr, &val64)) { |
226 | *valp = val64; |
227 | return true; |
228 | } |
229 | #else |
230 | uint32_t val32; |
231 | if (ReadValue32(info, addr, &val32)) { |
232 | *valp = val32; |
233 | return true; |
234 | } |
235 | #endif |
236 | return false; |
237 | } |
238 | |
239 | // Read a unsigned "little-endian base 128" value. See Chapter 7.6 of DWARF spec v3. |
240 | static bool |
241 | ReadULEB128(const libunwindInfo* info, unw_word_t* addr, unw_word_t* valp) |
242 | { |
243 | unw_word_t value = 0; |
244 | unsigned char byte; |
245 | int shift = 0; |
246 | |
247 | do |
248 | { |
249 | if (!ReadValue8(info, addr, &byte)) { |
250 | return false; |
251 | } |
252 | value |= ((unw_word_t)byte & 0x7f) << shift; |
253 | shift += 7; |
254 | } while (byte & 0x80); |
255 | |
256 | *valp = value; |
257 | return true; |
258 | } |
259 | |
260 | // Read a signed "little-endian base 128" value. See Chapter 7.6 of DWARF spec v3. |
261 | static bool |
262 | ReadSLEB128(const libunwindInfo* info, unw_word_t* addr, unw_word_t* valp) |
263 | { |
264 | unw_word_t value = 0; |
265 | unsigned char byte; |
266 | int shift = 0; |
267 | |
268 | do |
269 | { |
270 | if (!ReadValue8(info, addr, &byte)) { |
271 | return false; |
272 | } |
273 | value |= ((unw_word_t)byte & 0x7f) << shift; |
274 | shift += 7; |
275 | } while (byte & 0x80); |
276 | |
277 | if ((shift < (8 * sizeof(unw_word_t))) && ((byte & 0x40) != 0)) { |
278 | value |= ((unw_word_t)-1) << shift; |
279 | } |
280 | |
281 | *valp = value; |
282 | return true; |
283 | } |
284 | |
285 | static bool |
286 | ReadEncodedPointer(const libunwindInfo* info, unw_word_t* addr, unsigned char encoding, unw_word_t funcRel, unw_word_t* valp) |
287 | { |
288 | unw_word_t initialAddr = *addr; |
289 | uint16_t value16; |
290 | uint32_t value32; |
291 | uint64_t value64; |
292 | unw_word_t value; |
293 | |
294 | if (encoding == DW_EH_PE_omit) |
295 | { |
296 | *valp = 0; |
297 | return true; |
298 | } |
299 | else if (encoding == DW_EH_PE_aligned) |
300 | { |
301 | int size = sizeof(unw_word_t); |
302 | *addr = (initialAddr + size - 1) & -size; |
303 | return ReadPointer(info, addr, valp); |
304 | } |
305 | |
306 | switch (encoding & DW_EH_PE_FORMAT_MASK) |
307 | { |
308 | case DW_EH_PE_ptr: |
309 | if (!ReadPointer(info, addr, &value)) { |
310 | return false; |
311 | } |
312 | break; |
313 | |
314 | case DW_EH_PE_uleb128: |
315 | if (!ReadULEB128(info, addr, &value)) { |
316 | return false; |
317 | } |
318 | break; |
319 | |
320 | case DW_EH_PE_sleb128: |
321 | if (!ReadSLEB128(info, addr, &value)) { |
322 | return false; |
323 | } |
324 | break; |
325 | |
326 | case DW_EH_PE_udata2: |
327 | if (!ReadValue16(info, addr, &value16)) { |
328 | return false; |
329 | } |
330 | value = value16; |
331 | break; |
332 | |
333 | case DW_EH_PE_udata4: |
334 | if (!ReadValue32(info, addr, &value32)) { |
335 | return false; |
336 | } |
337 | value = value32; |
338 | break; |
339 | |
340 | case DW_EH_PE_udata8: |
341 | if (!ReadValue64(info, addr, &value64)) { |
342 | return false; |
343 | } |
344 | value = value64; |
345 | break; |
346 | |
347 | case DW_EH_PE_sdata2: |
348 | if (!ReadValue16(info, addr, &value16)) { |
349 | return false; |
350 | } |
351 | value = (int16_t)value16; |
352 | break; |
353 | |
354 | case DW_EH_PE_sdata4: |
355 | if (!ReadValue32(info, addr, &value32)) { |
356 | return false; |
357 | } |
358 | value = (int32_t)value32; |
359 | break; |
360 | |
361 | case DW_EH_PE_sdata8: |
362 | if (!ReadValue64(info, addr, &value64)) { |
363 | return false; |
364 | } |
365 | value = (int64_t)value64; |
366 | break; |
367 | |
368 | default: |
369 | ASSERT("ReadEncodedPointer: invalid encoding format %x\n" , encoding); |
370 | return false; |
371 | } |
372 | |
373 | // 0 is a special value and always absolute |
374 | if (value == 0) { |
375 | *valp = 0; |
376 | return true; |
377 | } |
378 | |
379 | switch (encoding & DW_EH_PE_APPL_MASK) |
380 | { |
381 | case DW_EH_PE_absptr: |
382 | break; |
383 | |
384 | case DW_EH_PE_pcrel: |
385 | value += initialAddr; |
386 | break; |
387 | |
388 | case DW_EH_PE_funcrel: |
389 | _ASSERTE(funcRel != UINTPTR_MAX); |
390 | value += funcRel; |
391 | break; |
392 | |
393 | case DW_EH_PE_textrel: |
394 | case DW_EH_PE_datarel: |
395 | default: |
396 | ASSERT("ReadEncodedPointer: invalid application type %x\n" , encoding); |
397 | return false; |
398 | } |
399 | |
400 | if (encoding & DW_EH_PE_indirect) |
401 | { |
402 | unw_word_t indirect_addr = value; |
403 | if (!ReadPointer(info, &indirect_addr, &value)) { |
404 | return false; |
405 | } |
406 | } |
407 | |
408 | *valp = value; |
409 | return true; |
410 | } |
411 | |
412 | static bool |
413 | LookupTableEntry(const libunwindInfo* info, int32_t ip, unw_word_t tableAddr, size_t tableCount, table_entry* entry, bool* found) |
414 | { |
415 | size_t low, high, mid; |
416 | unw_word_t addr; |
417 | int32_t start_ip; |
418 | |
419 | *found = false; |
420 | |
421 | // do a binary search on table |
422 | for (low = 0, high = tableCount; low < high;) |
423 | { |
424 | mid = (low + high) / 2; |
425 | addr = tableAddr + (mid * sizeof(table_entry)); |
426 | |
427 | if (!ReadValue32(info, &addr, (uint32_t*)&start_ip)) { |
428 | return false; |
429 | } |
430 | if (ip < start_ip) { |
431 | high = mid; |
432 | } |
433 | else { |
434 | low = mid + 1; |
435 | } |
436 | } |
437 | |
438 | if (high > 0) { |
439 | addr = tableAddr + ((high - 1) * sizeof(table_entry)); |
440 | // Assumes that the table_entry is two 32 bit values |
441 | _ASSERTE(sizeof(*entry) == sizeof(uint64_t)); |
442 | if (!ReadValue64(info, &addr, (uint64_t*)entry)) { |
443 | return false; |
444 | } |
445 | *found = true; |
446 | } |
447 | |
448 | return true; |
449 | } |
450 | |
451 | static bool |
452 | ParseCie(const libunwindInfo* info, unw_word_t addr, dwarf_cie_info_t* dci) |
453 | { |
454 | uint8_t ch, version, fdeEncoding, handlerEncoding; |
455 | unw_word_t cieLength, cieEndAddr; |
456 | uint32_t value32; |
457 | uint64_t value64; |
458 | |
459 | memset(dci, 0, sizeof (*dci)); |
460 | |
461 | // Pick appropriate default for FDE-encoding. DWARF spec says |
462 | // start-IP (initial_location) and the code-size (address_range) are |
463 | // "address-unit sized constants". The `R' augmentation can be used |
464 | // to override this, but by default, we pick an address-sized unit |
465 | // for fde_encoding. |
466 | #if BIT64 |
467 | fdeEncoding = DW_EH_PE_udata8; |
468 | #else |
469 | fdeEncoding = DW_EH_PE_udata4; |
470 | #endif |
471 | |
472 | dci->lsda_encoding = DW_EH_PE_omit; |
473 | dci->handler = 0; |
474 | |
475 | if (!ReadValue32(info, &addr, &value32)) { |
476 | return false; |
477 | } |
478 | |
479 | if (value32 != 0xffffffff) |
480 | { |
481 | // The CIE is in the 32-bit DWARF format |
482 | uint32_t cieId; |
483 | |
484 | // DWARF says CIE id should be 0xffffffff, but in .eh_frame, it's 0 |
485 | const uint32_t expectedId = 0; |
486 | |
487 | cieLength = value32; |
488 | cieEndAddr = addr + cieLength; |
489 | |
490 | if (!ReadValue32(info, &addr, &cieId)) { |
491 | return false; |
492 | } |
493 | if (cieId != expectedId) { |
494 | ASSERT("ParseCie: unexpected cie id %x\n" , cieId); |
495 | return false; |
496 | } |
497 | } |
498 | else |
499 | { |
500 | // The CIE is in the 64-bit DWARF format |
501 | uint64_t cieId; |
502 | |
503 | // DWARF says CIE id should be 0xffffffffffffffff, but in .eh_frame, it's 0 |
504 | const uint64_t expectedId = 0; |
505 | |
506 | if (!ReadValue64(info, &addr, &value64)) { |
507 | return false; |
508 | } |
509 | cieLength = value64; |
510 | cieEndAddr = addr + cieLength; |
511 | |
512 | if (!ReadValue64(info, &addr, &cieId)) { |
513 | return false; |
514 | } |
515 | if (cieId != expectedId) { |
516 | ASSERT("ParseCie: unexpected cie id %lx\n" , cieId); |
517 | return false; |
518 | } |
519 | } |
520 | dci->cie_instr_end = cieEndAddr; |
521 | |
522 | if (!ReadValue8(info, &addr, &version)) { |
523 | return false; |
524 | } |
525 | if (version != 1 && version != DWARF_CIE_VERSION) { |
526 | ASSERT("ParseCie: invalid cie version %x\n" , version); |
527 | return false; |
528 | } |
529 | |
530 | // Read the augmentation string |
531 | uint8_t augmentationString[8]; |
532 | memset(augmentationString, 0, sizeof(augmentationString)); |
533 | |
534 | for (int i = 0; i < sizeof(augmentationString); i++) |
535 | { |
536 | if (!ReadValue8(info, &addr, &ch)) { |
537 | return false; |
538 | } |
539 | if (ch == 0) { |
540 | break; |
541 | } |
542 | augmentationString[i] = ch; |
543 | } |
544 | |
545 | // Read the code and data alignment |
546 | if (!ReadULEB128(info, &addr, &dci->code_align)) { |
547 | return false; |
548 | } |
549 | if (!ReadSLEB128(info, &addr, &dci->data_align)) { |
550 | return false; |
551 | } |
552 | |
553 | // Read the return-address column either as a u8 or as a uleb128 |
554 | if (version == 1) |
555 | { |
556 | if (!ReadValue8(info, &addr, &ch)) { |
557 | return false; |
558 | } |
559 | dci->ret_addr_column = ch; |
560 | } |
561 | else |
562 | { |
563 | if (!ReadULEB128(info, &addr, &dci->ret_addr_column)) { |
564 | return false; |
565 | } |
566 | } |
567 | |
568 | // Parse the augmentation string |
569 | for (int i = 0; i < sizeof(augmentationString); i++) |
570 | { |
571 | bool done = false; |
572 | unw_word_t augmentationSize; |
573 | |
574 | switch (augmentationString[i]) |
575 | { |
576 | case '\0': |
577 | done = true; |
578 | break; |
579 | |
580 | case 'z': |
581 | dci->sized_augmentation = 1; |
582 | if (!ReadULEB128(info, &addr, &augmentationSize)) { |
583 | return false; |
584 | } |
585 | break; |
586 | |
587 | case 'L': |
588 | // read the LSDA pointer-encoding format |
589 | if (!ReadValue8(info, &addr, &ch)) { |
590 | return false; |
591 | } |
592 | dci->lsda_encoding = ch; |
593 | break; |
594 | |
595 | case 'R': |
596 | // read the FDE pointer-encoding format |
597 | if (!ReadValue8(info, &addr, &fdeEncoding)) { |
598 | return false; |
599 | } |
600 | break; |
601 | |
602 | case 'P': |
603 | // read the personality-routine pointer-encoding format |
604 | if (!ReadValue8(info, &addr, &handlerEncoding)) { |
605 | return false; |
606 | } |
607 | if (!ReadEncodedPointer(info, &addr, handlerEncoding, UINTPTR_MAX, &dci->handler)) { |
608 | return false; |
609 | } |
610 | break; |
611 | |
612 | case 'S': |
613 | // This is a signal frame |
614 | dci->signal_frame = 1; |
615 | |
616 | // Temporarily set it to one so dwarf_parse_fde() knows that |
617 | // it should fetch the actual ABI/TAG pair from the FDE. |
618 | dci->have_abi_marker = 1; |
619 | break; |
620 | |
621 | default: |
622 | if (dci->sized_augmentation) { |
623 | // If we have the size of the augmentation body, we can skip |
624 | // over the parts that we don't understand, so we're OK |
625 | done = true; |
626 | break; |
627 | } |
628 | ASSERT("ParseCie: unexpected argumentation string '%s'\n" , augmentationString[i]); |
629 | return false; |
630 | } |
631 | |
632 | if (done) { |
633 | break; |
634 | } |
635 | } |
636 | dci->fde_encoding = fdeEncoding; |
637 | dci->cie_instr_start = addr; |
638 | return true; |
639 | } |
640 | |
641 | static bool |
642 | (const libunwindInfo* info, unw_word_t* addrp, unw_proc_info_t *pip, int need_unwind_info) |
643 | { |
644 | unw_word_t addr = *addrp, fdeEndAddr, cieOffsetAddr, cieAddr; |
645 | uint32_t value32; |
646 | uint64_t value64; |
647 | |
648 | if (!ReadValue32(info, &addr, &value32)) { |
649 | return false; |
650 | } |
651 | if (value32 != 0xffffffff) |
652 | { |
653 | int32_t cieOffset = 0; |
654 | |
655 | // In some configurations, an FDE with a 0 length indicates the end of the FDE-table |
656 | if (value32 == 0) { |
657 | return false; |
658 | } |
659 | // the FDE is in the 32-bit DWARF format */ |
660 | *addrp = fdeEndAddr = addr + value32; |
661 | cieOffsetAddr = addr; |
662 | |
663 | if (!ReadValue32(info, &addr, (uint32_t*)&cieOffset)) { |
664 | return false; |
665 | } |
666 | // Ignore CIEs (happens during linear search) |
667 | if (cieOffset == 0) { |
668 | return true; |
669 | } |
670 | // DWARF says that the CIE_pointer in the FDE is a .debug_frame-relative offset, |
671 | // but the GCC-generated .eh_frame sections instead store a "pcrelative" offset, |
672 | // which is just as fine as it's self-contained |
673 | cieAddr = cieOffsetAddr - cieOffset; |
674 | } |
675 | else |
676 | { |
677 | int64_t cieOffset = 0; |
678 | |
679 | // the FDE is in the 64-bit DWARF format */ |
680 | if (!ReadValue64(info, &addr, (uint64_t*)&value64)) { |
681 | return false; |
682 | } |
683 | *addrp = fdeEndAddr = addr + value64; |
684 | cieOffsetAddr = addr; |
685 | |
686 | if (!ReadValue64(info, &addr, (uint64_t*)&cieOffset)) { |
687 | return false; |
688 | } |
689 | // Ignore CIEs (happens during linear search) |
690 | if (cieOffset == 0) { |
691 | return true; |
692 | } |
693 | // DWARF says that the CIE_pointer in the FDE is a .debug_frame-relative offset, |
694 | // but the GCC-generated .eh_frame sections instead store a "pcrelative" offset, |
695 | // which is just as fine as it's self-contained |
696 | cieAddr = (unw_word_t)((uint64_t)cieOffsetAddr - cieOffset); |
697 | } |
698 | |
699 | dwarf_cie_info_t dci; |
700 | if (!ParseCie(info, cieAddr, &dci)) { |
701 | return false; |
702 | } |
703 | |
704 | unw_word_t ipStart, ipRange; |
705 | if (!ReadEncodedPointer(info, &addr, dci.fde_encoding, UINTPTR_MAX, &ipStart)) { |
706 | return false; |
707 | } |
708 | |
709 | // IP-range has same encoding as FDE pointers, except that it's always an absolute value |
710 | uint8_t ipRangeEncoding = dci.fde_encoding & DW_EH_PE_FORMAT_MASK; |
711 | if (!ReadEncodedPointer(info, &addr, ipRangeEncoding, UINTPTR_MAX, &ipRange)) { |
712 | return false; |
713 | } |
714 | pip->start_ip = ipStart; |
715 | pip->end_ip = ipStart + ipRange; |
716 | pip->handler = dci.handler; |
717 | |
718 | unw_word_t augmentationSize, augmentationEndAddr; |
719 | if (dci.sized_augmentation) { |
720 | if (!ReadULEB128(info, &addr, &augmentationSize)) { |
721 | return false; |
722 | } |
723 | augmentationEndAddr = addr + augmentationSize; |
724 | } |
725 | |
726 | // Read language specific data area address |
727 | if (!ReadEncodedPointer(info, &addr, dci.lsda_encoding, pip->start_ip, &pip->lsda)) { |
728 | return false; |
729 | } |
730 | |
731 | // Now fill out the proc info if requested |
732 | if (need_unwind_info) |
733 | { |
734 | if (dci.have_abi_marker) |
735 | { |
736 | if (!ReadValue16(info, &addr, &dci.abi)) { |
737 | return false; |
738 | } |
739 | if (!ReadValue16(info, &addr, &dci.tag)) { |
740 | return false; |
741 | } |
742 | } |
743 | if (dci.sized_augmentation) { |
744 | dci.fde_instr_start = augmentationEndAddr; |
745 | } |
746 | else { |
747 | dci.fde_instr_start = addr; |
748 | } |
749 | dci.fde_instr_end = fdeEndAddr; |
750 | |
751 | pip->format = UNW_INFO_FORMAT_TABLE; |
752 | pip->unwind_info_size = sizeof(dci); |
753 | pip->unwind_info = malloc(sizeof(dci)); |
754 | if (pip->unwind_info == nullptr) { |
755 | return -UNW_ENOMEM; |
756 | } |
757 | memcpy(pip->unwind_info, &dci, sizeof(dci)); |
758 | } |
759 | |
760 | return true; |
761 | } |
762 | |
763 | |
764 | static int |
765 | get_dyn_info_list_addr(unw_addr_space_t as, unw_word_t *dilap, void *arg) |
766 | { |
767 | return -UNW_ENOINFO; |
768 | } |
769 | |
770 | static int |
771 | access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, int write, void *arg) |
772 | { |
773 | if (write) |
774 | { |
775 | ASSERT("Memory write must never be called by libunwind during stackwalk\n" ); |
776 | return -UNW_EINVAL; |
777 | } |
778 | const auto *info = (libunwindInfo*)arg; |
779 | |
780 | if (info->ReadMemory((PVOID)addr, valp, sizeof(*valp))) |
781 | { |
782 | return UNW_ESUCCESS; |
783 | } |
784 | else |
785 | { |
786 | return -UNW_EUNSPEC; |
787 | } |
788 | } |
789 | |
790 | static int |
791 | access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, int write, void *arg) |
792 | { |
793 | if (write) |
794 | { |
795 | ASSERT("Register write must never be called by libunwind during stackwalk\n" ); |
796 | return -UNW_EREADONLYREG; |
797 | } |
798 | |
799 | const auto *info = (libunwindInfo*)arg; |
800 | CONTEXT *winContext = info->Context; |
801 | |
802 | switch (regnum) |
803 | { |
804 | #if defined(_AMD64_) |
805 | case UNW_REG_IP: *valp = (unw_word_t)winContext->Rip; break; |
806 | case UNW_REG_SP: *valp = (unw_word_t)winContext->Rsp; break; |
807 | case UNW_X86_64_RBP: *valp = (unw_word_t)winContext->Rbp; break; |
808 | case UNW_X86_64_RBX: *valp = (unw_word_t)winContext->Rbx; break; |
809 | case UNW_X86_64_R12: *valp = (unw_word_t)winContext->R12; break; |
810 | case UNW_X86_64_R13: *valp = (unw_word_t)winContext->R13; break; |
811 | case UNW_X86_64_R14: *valp = (unw_word_t)winContext->R14; break; |
812 | case UNW_X86_64_R15: *valp = (unw_word_t)winContext->R15; break; |
813 | #elif defined(_ARM_) |
814 | case UNW_ARM_R13: *valp = (unw_word_t)winContext->Sp; break; |
815 | case UNW_ARM_R14: *valp = (unw_word_t)winContext->Lr; break; |
816 | case UNW_ARM_R15: *valp = (unw_word_t)winContext->Pc; break; |
817 | case UNW_ARM_R4: *valp = (unw_word_t)winContext->R4; break; |
818 | case UNW_ARM_R5: *valp = (unw_word_t)winContext->R5; break; |
819 | case UNW_ARM_R6: *valp = (unw_word_t)winContext->R6; break; |
820 | case UNW_ARM_R7: *valp = (unw_word_t)winContext->R7; break; |
821 | case UNW_ARM_R8: *valp = (unw_word_t)winContext->R8; break; |
822 | case UNW_ARM_R9: *valp = (unw_word_t)winContext->R9; break; |
823 | case UNW_ARM_R10: *valp = (unw_word_t)winContext->R10; break; |
824 | case UNW_ARM_R11: *valp = (unw_word_t)winContext->R11; break; |
825 | #elif defined(_ARM64_) |
826 | case UNW_REG_IP: *valp = (unw_word_t)winContext->Pc; break; |
827 | case UNW_REG_SP: *valp = (unw_word_t)winContext->Sp; break; |
828 | case UNW_AARCH64_X29: *valp = (unw_word_t)winContext->Fp; break; |
829 | case UNW_AARCH64_X30: *valp = (unw_word_t)winContext->Lr; break; |
830 | case UNW_AARCH64_X19: *valp = (unw_word_t)winContext->X19; break; |
831 | case UNW_AARCH64_X20: *valp = (unw_word_t)winContext->X20; break; |
832 | case UNW_AARCH64_X21: *valp = (unw_word_t)winContext->X21; break; |
833 | case UNW_AARCH64_X22: *valp = (unw_word_t)winContext->X22; break; |
834 | case UNW_AARCH64_X23: *valp = (unw_word_t)winContext->X23; break; |
835 | case UNW_AARCH64_X24: *valp = (unw_word_t)winContext->X24; break; |
836 | case UNW_AARCH64_X25: *valp = (unw_word_t)winContext->X25; break; |
837 | case UNW_AARCH64_X26: *valp = (unw_word_t)winContext->X26; break; |
838 | case UNW_AARCH64_X27: *valp = (unw_word_t)winContext->X27; break; |
839 | case UNW_AARCH64_X28: *valp = (unw_word_t)winContext->X28; break; |
840 | #else |
841 | #error unsupported architecture |
842 | #endif |
843 | default: |
844 | ASSERT("Attempt to read an unknown register\n" ); |
845 | return -UNW_EBADREG; |
846 | } |
847 | return UNW_ESUCCESS; |
848 | } |
849 | |
850 | static int |
851 | access_fpreg(unw_addr_space_t as, unw_regnum_t regnum, unw_fpreg_t *fpvalp, int write, void *arg) |
852 | { |
853 | ASSERT("Not supposed to be ever called\n" ); |
854 | return -UNW_EINVAL; |
855 | } |
856 | |
857 | static int |
858 | resume(unw_addr_space_t as, unw_cursor_t *cp, void *arg) |
859 | { |
860 | ASSERT("Not supposed to be ever called\n" ); |
861 | return -UNW_EINVAL; |
862 | } |
863 | |
864 | static int |
865 | get_proc_name(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len, unw_word_t *offp, void *arg) |
866 | { |
867 | ASSERT("Not supposed to be ever called\n" ); |
868 | return -UNW_EINVAL; |
869 | } |
870 | |
871 | static int |
872 | find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pip, int need_unwind_info, void *arg) |
873 | { |
874 | const auto *info = (libunwindInfo*)arg; |
875 | memset(pip, 0, sizeof(*pip)); |
876 | |
877 | Ehdr ehdr; |
878 | if (!info->ReadMemory((void*)info->BaseAddress, &ehdr, sizeof(ehdr))) { |
879 | ERROR("ELF: reading ehdr %p\n" , info->BaseAddress); |
880 | return -UNW_EINVAL; |
881 | } |
882 | Phdr* phdrAddr = reinterpret_cast<Phdr*>(info->BaseAddress + ehdr.e_phoff); |
883 | int phnum = ehdr.e_phnum; |
884 | TRACE("ELF: base %p ip %p e_type %d e_phnum %d e_phoff %p\n" , info->BaseAddress, ip, ehdr.e_type, ehdr.e_phnum, ehdr.e_phoff); |
885 | |
886 | // The eh_frame header |
887 | Phdr ehPhdr; |
888 | memset(&ehPhdr, 0, sizeof(ehPhdr)); |
889 | |
890 | // Search for the module's dynamic header and unwind frames |
891 | Dyn* dynamicAddr = nullptr; |
892 | |
893 | for (int i = 0; i < phnum; i++, phdrAddr++) |
894 | { |
895 | Phdr ph; |
896 | if (!info->ReadMemory(phdrAddr, &ph, sizeof(ph))) { |
897 | ERROR("ELF: reading phdrAddr %p\n" , phdrAddr); |
898 | return -UNW_EINVAL; |
899 | } |
900 | TRACE("ELF: phdr %p type %d (%x) vaddr %p memsz %016llx paddr %p filesz %016llx offset %p align %016llx\n" , |
901 | phdrAddr, ph.p_type, ph.p_type, ph.p_vaddr, ph.p_memsz, ph.p_paddr, ph.p_filesz, ph.p_offset, ph.p_align); |
902 | |
903 | switch (ph.p_type) |
904 | { |
905 | case PT_DYNAMIC: |
906 | if (ehdr.e_type == ET_EXEC) { |
907 | dynamicAddr = reinterpret_cast<Dyn*>(ph.p_vaddr); |
908 | } |
909 | if (ehdr.e_type == ET_DYN) { |
910 | dynamicAddr = reinterpret_cast<Dyn*>(ph.p_vaddr + info->BaseAddress); |
911 | } |
912 | break; |
913 | |
914 | case PT_GNU_EH_FRAME: |
915 | ehPhdr = ph; |
916 | break; |
917 | } |
918 | } |
919 | |
920 | if (dynamicAddr != nullptr) |
921 | { |
922 | for (;;) |
923 | { |
924 | Dyn dyn; |
925 | if (!info->ReadMemory(dynamicAddr, &dyn, sizeof(dyn))) { |
926 | ERROR("ELF: reading dynamicAddr %p\n" , dynamicAddr); |
927 | return -UNW_EINVAL; |
928 | } |
929 | if (dyn.d_tag == DT_PLTGOT) { |
930 | TRACE("ELF: dyn %p tag %d (%x) d_ptr %p\n" , dynamicAddr, dyn.d_tag, dyn.d_tag, dyn.d_un.d_ptr); |
931 | pip->gp = dyn.d_un.d_ptr; |
932 | break; |
933 | } |
934 | else if (dyn.d_tag == DT_NULL) { |
935 | break; |
936 | } |
937 | dynamicAddr++; |
938 | } |
939 | } |
940 | unw_word_t ehFrameHdrAddr = ehPhdr.p_offset + info->BaseAddress; |
941 | eh_frame_hdr ehFrameHdr; |
942 | |
943 | if (!info->ReadMemory((PVOID)ehFrameHdrAddr, &ehFrameHdr, sizeof(eh_frame_hdr))) { |
944 | ERROR("ELF: reading ehFrameHdrAddr %p\n" , ehFrameHdrAddr); |
945 | return -UNW_EINVAL; |
946 | } |
947 | TRACE("ehFrameHdrAddr %p version %d eh_frame_ptr_enc %d fde_count_enc %d table_enc %d\n" , |
948 | ehFrameHdrAddr, ehFrameHdr.version, ehFrameHdr.eh_frame_ptr_enc, ehFrameHdr.fde_count_enc, ehFrameHdr.table_enc); |
949 | |
950 | if (ehFrameHdr.version != DW_EH_VERSION) { |
951 | ASSERT("ehFrameHdr version %x not supported\n" , ehFrameHdr.version); |
952 | return -UNW_EBADVERSION; |
953 | } |
954 | unw_word_t addr = ehFrameHdrAddr + sizeof(eh_frame_hdr); |
955 | unw_word_t ehFrameStart; |
956 | unw_word_t fdeCount; |
957 | |
958 | // Decode the eh_frame_hdr info |
959 | if (!ReadEncodedPointer(info, &addr, ehFrameHdr.eh_frame_ptr_enc, UINTPTR_MAX, &ehFrameStart)) { |
960 | ERROR("decoding eh_frame_ptr\n" ); |
961 | return -UNW_EINVAL; |
962 | } |
963 | if (!ReadEncodedPointer(info, &addr, ehFrameHdr.fde_count_enc, UINTPTR_MAX, &fdeCount)) { |
964 | ERROR("decoding fde_count_enc\n" ); |
965 | return -UNW_EINVAL; |
966 | } |
967 | TRACE("ehFrameStart %p fdeCount %p ip offset %08x\n" , ehFrameStart, fdeCount, (int32_t)(ip - ehFrameHdrAddr)); |
968 | |
969 | // LookupTableEntry assumes this encoding |
970 | if (ehFrameHdr.table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) { |
971 | ASSERT("Table encoding not supported %x\n" , ehFrameHdr.table_enc); |
972 | return -UNW_EINVAL; |
973 | } |
974 | // Find the fde using a binary search on the frame table |
975 | table_entry entry; |
976 | bool found; |
977 | if (!LookupTableEntry(info, ip - ehFrameHdrAddr, addr, fdeCount, &entry, &found)) { |
978 | ERROR("LookupTableEntry\n" ); |
979 | return -UNW_EINVAL; |
980 | } |
981 | unw_word_t fdeAddr = entry.fde_offset + ehFrameHdrAddr; |
982 | TRACE("start_ip %08x fde_offset %08x fdeAddr %p found %d\n" , entry.start_ip, entry.fde_offset, fdeAddr, found); |
983 | |
984 | // Unwind info not found |
985 | if (!found) { |
986 | return -UNW_ENOINFO; |
987 | } |
988 | |
989 | // Now get the unwind info |
990 | if (!ExtractProcInfoFromFde(info, &fdeAddr, pip, need_unwind_info)) { |
991 | ERROR("ExtractProcInfoFromFde\n" ); |
992 | return -UNW_EINVAL; |
993 | } |
994 | |
995 | _ASSERTE(ip >= pip->start_ip && ip <= pip->end_ip); |
996 | return UNW_ESUCCESS; |
997 | } |
998 | |
999 | static void |
1000 | put_unwind_info(unw_addr_space_t as, unw_proc_info_t *pip, void *arg) |
1001 | { |
1002 | if (pip->unwind_info != nullptr) { |
1003 | free(pip->unwind_info); |
1004 | pip->unwind_info = nullptr; |
1005 | } |
1006 | } |
1007 | |
1008 | static unw_accessors_t unwind_accessors = |
1009 | { |
1010 | .find_proc_info = find_proc_info, |
1011 | .put_unwind_info = put_unwind_info, |
1012 | .get_dyn_info_list_addr = get_dyn_info_list_addr, |
1013 | .access_mem = access_mem, |
1014 | .access_reg = access_reg, |
1015 | .access_fpreg = access_fpreg, |
1016 | .resume = resume, |
1017 | .get_proc_name = get_proc_name |
1018 | }; |
1019 | |
1020 | /*++ |
1021 | Function: |
1022 | PAL_VirtualUnwindOutOfProc |
1023 | |
1024 | Unwind the stack given the context for a "remote" target using the |
1025 | provided read memory callback. |
1026 | |
1027 | Assumes the IP is in the module of the base address provided (coreclr). |
1028 | |
1029 | Parameters: |
1030 | context - the start context in the target |
1031 | contextPointers - the context of the next frame |
1032 | baseAddress - base address of the module to find the unwind info |
1033 | readMemoryCallback - reads memory from the target |
1034 | --*/ |
1035 | BOOL |
1036 | PALAPI |
1037 | PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback) |
1038 | { |
1039 | unw_addr_space_t addrSpace = 0; |
1040 | unw_cursor_t cursor; |
1041 | libunwindInfo info; |
1042 | BOOL result = FALSE; |
1043 | int st; |
1044 | |
1045 | info.BaseAddress = baseAddress; |
1046 | info.Context = context; |
1047 | info.ReadMemory = readMemoryCallback; |
1048 | |
1049 | addrSpace = unw_create_addr_space(&unwind_accessors, 0); |
1050 | |
1051 | st = unw_init_remote(&cursor, addrSpace, &info); |
1052 | if (st < 0) |
1053 | { |
1054 | result = FALSE; |
1055 | goto exit; |
1056 | } |
1057 | |
1058 | st = unw_step(&cursor); |
1059 | if (st < 0) |
1060 | { |
1061 | result = FALSE; |
1062 | goto exit; |
1063 | } |
1064 | |
1065 | UnwindContextToWinContext(&cursor, context); |
1066 | |
1067 | if (contextPointers != NULL) |
1068 | { |
1069 | GetContextPointers(&cursor, NULL, contextPointers); |
1070 | } |
1071 | result = TRUE; |
1072 | |
1073 | exit: |
1074 | if (addrSpace != 0) |
1075 | { |
1076 | unw_destroy_addr_space(addrSpace); |
1077 | } |
1078 | return result; |
1079 | } |
1080 | |
1081 | #else |
1082 | |
1083 | BOOL |
1084 | PALAPI |
1085 | PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback) |
1086 | { |
1087 | return FALSE; |
1088 | } |
1089 | |
1090 | #endif // defined(_AMD64_) && defined(HAVE_UNW_GET_ACCESSORS) |
1091 | |