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
7Module Name:
8
9 remote-unwind.cpp
10
11Abstract:
12
13 Implementation of out of context unwind using libunwind8
14 remote unwind API.
15
16This file contains code based on libunwind8
17
18Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P.
19 Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
20
21Permission is hereby granted, free of charge, to any person obtaining
22a copy of this software and associated documentation files (the
23"Software"), to deal in the Software without restriction, including
24without limitation the rights to use, copy, modify, merge, publish,
25distribute, sublicense, and/or sell copies of the Software, and to
26permit persons to whom the Software is furnished to do so, subject to
27the following conditions:
28
29The above copyright notice and this permission notice shall be
30included in all copies or substantial portions of the Software.
31
32THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
36LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
37OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
38WITH 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
59SET_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
76extern void UnwindContextToWinContext(unw_cursor_t *cursor, CONTEXT *winContext);
77extern void GetContextPointers(unw_cursor_t *cursor, unw_context_t *unwContext, KNONVOLATILE_CONTEXT_POINTERS *contextPointers);
78
79typedef 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
127typedef 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
146typedef struct _table_entry
147{
148 int32_t start_ip;
149 int32_t fde_offset;
150} table_entry;
151
152// DWARF unwind info
153typedef 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
172static bool
173ReadValue8(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
184static bool
185ReadValue16(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
196static bool
197ReadValue32(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
208static bool
209ReadValue64(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
220static bool
221ReadPointer(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.
240static bool
241ReadULEB128(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.
261static bool
262ReadSLEB128(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
285static bool
286ReadEncodedPointer(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
412static bool
413LookupTableEntry(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
451static bool
452ParseCie(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
641static bool
642ExtractProcInfoFromFde(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
764static int
765get_dyn_info_list_addr(unw_addr_space_t as, unw_word_t *dilap, void *arg)
766{
767 return -UNW_ENOINFO;
768}
769
770static int
771access_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
790static int
791access_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
850static int
851access_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
857static int
858resume(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
864static int
865get_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
871static int
872find_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
999static void
1000put_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
1008static 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/*++
1021Function:
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
1029Parameters:
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--*/
1035BOOL
1036PALAPI
1037PAL_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
1073exit:
1074 if (addrSpace != 0)
1075 {
1076 unw_destroy_addr_space(addrSpace);
1077 }
1078 return result;
1079}
1080
1081#else
1082
1083BOOL
1084PALAPI
1085PAL_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