libdd-libunwind-sys 1.0.2

Rust bindings for using unwind library inside libdatadog
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
/* libunwind - a platform-independent unwind library
   Copyright (C) 2008 CodeSourcery
   Copyright (C) 2011-2013 Linaro Limited
   Copyright (C) 2012 Tommi Rantala <tt.rantala@gmail.com>
   Copyright 2022,2024 Blackberry Limited.

This file is part of libunwind.

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */

#include "dwarf_i.h"
#include "ucontext_i.h"
#include "unwind_i.h"

static const int WSIZE = sizeof (unw_word_t);

/* Recognise PLT entries such as:
  40ddf0:       b0000570        adrp    x16, 4ba000 <_GLOBAL_OFFSET_TABLE_+0x2a8>
  40ddf4:       f9433611        ldr     x17, [x16,#1640]
  40ddf8:       9119a210        add     x16, x16, #0x668
  40ddfc:       d61f0220        br      x17

  \note The current implementation only supports little endian modes.
*/
static int
_is_plt_entry (struct dwarf_cursor *c)
{
  unw_word_t w0 = 0, w1 = 0;
  unw_accessors_t *a;

  if (c->as->big_endian)
    {
      return 0;
    }

  /*
    A PLT (Procedure Linkage Table) is used by the dynamic linker to map the
    relative address of a position independent function call onto the real
    address of the function. If we attempt to unwind from any instruction
    inside the PLT, and the PLT is missing DWARF unwind information, then
    conventional unwinding will fail because although the function has been
    "called" we have not yet entered the prologue and set-up the stack frame.

    This code looks to see if the instruction is anywhere within a "recognised"
    PLT entry (note that the IP could be anywhere within the PLT, so we have to
    examine nearby instructions).
  */

  struct instruction_entry
    {
      uint32_t pattern;
      uint32_t mask;
    } instructions[4] =
    {
      // aarch64
      {0x90000010,0x9f00001f}, // adrp
      {0xf9400211,0xffc003ff}, // ldr
      {0x91000210,0xff8003ff}, // add
      {0xd61f0220,0xffffffff}, // br
    };

  a = unw_get_accessors (c->as);
  if ((*a->access_mem) (c->as, c->ip, &w0, 0, c->as_arg) < 0)
    {
      return 0;
    }

  /*
    NB: the following code is endian sensitive!

    The current implementation is for little-endian modes, big-endian modes
    will see the first instruction in the high bits of w0, and the second
    instruction in the low bits of w0. Some tweaks will be needed to read from
    the correct part of the word to support big endian modes.
  */
  if ((w0      & instructions[0].mask) == instructions[0].pattern &&
     ((w0>>32) & instructions[1].mask) == instructions[1].pattern)
  {
    if ((*a->access_mem) (c->as, c->ip+8, &w1, 0, c->as_arg) >= 0 &&
       (w1      & instructions[2].mask) == instructions[2].pattern &&
      ((w1>>32) & instructions[3].mask) == instructions[3].pattern)
      {
        return 1;
      }
    else
      {
        return 0;
      }
  }
  else if ((w0 & instructions[2].mask) == instructions[2].pattern &&
     ((w0>>32) & instructions[3].mask) == instructions[3].pattern)
  {
    w1 = w0;
    if ((*a->access_mem) (c->as, c->ip-8, &w0, 0, c->as_arg) >= 0 &&
       (w0      & instructions[0].mask) == instructions[0].pattern &&
      ((w0>>32) & instructions[1].mask) == instructions[1].pattern)
      {
        return 1;
      }
    else
      {
        return 0;
      }
  }
  else if ((w0 & instructions[1].mask) == instructions[1].pattern &&
     ((w0>>32) & instructions[2].mask) == instructions[2].pattern)
  {
    if ((*a->access_mem) (c->as, c->ip-4, &w0, 0, c->as_arg) < 0 ||
        (*a->access_mem) (c->as, c->ip+4, &w1, 0, c->as_arg) < 0)
    {
      return 0;
    }
  }
  else if ((w0 & instructions[3].mask) == instructions[3].pattern)
  {
    if ((*a->access_mem) (c->as, c->ip-12, &w0, 0, c->as_arg) < 0 ||
        (*a->access_mem) (c->as, c->ip-4, &w1, 0, c->as_arg) < 0)
    {
      return 0;
    }
  }

  if ((w0      & instructions[0].mask) == instructions[0].pattern &&
     ((w0>>32) & instructions[1].mask) == instructions[1].pattern &&
      (w1      & instructions[2].mask) == instructions[2].pattern &&
     ((w1>>32) & instructions[3].mask) == instructions[3].pattern)
    {
      return 1;
    }
  else
    {
      return 0;
    }
}

int
unw_is_plt_entry (unw_cursor_t *uc)
{
	return _is_plt_entry (&((struct cursor *)uc)->dwarf);
}

typedef enum frame_record_location
  {
    NONE,           /* frame record creation has not been detected, use LR */
    AT_SP_OFFSET,   /* frame record creation has been detected, but FP
                       update not detected */
    AT_FP,          /* frame record creation and FP update detected */
    NO_PROC_INFO,   /* no proc info available */
  } frame_record_location_t;

typedef struct frame_state
  {
    frame_record_location_t loc;
    int32_t offset;
  } frame_state_t;

/* Recognise when a frame record storing FP+LR has been created and whether FP
   has been updated to point to the frame record. For example:
  4183d4:       a9bd7bfd        stp     x29, x30, [sp,#-48]!    <= FP+LR stored
  4183d8:       d2800005        mov     x5, #0x0
  4183dc:       d2800004        mov     x4, #0x0
  4183e0:       910003fd        mov     x29, sp                 <= FP updated
  4183e4:       a90153f3        stp     x19, x20, [sp,#16]
  ...
  418444:       a94153f3        ldp     x19, x20, [sp,#16]
  418448:       f94013f5        ldr     x21, [sp,#32]
  41844c:       a8c37bfd        ldp     x29, x30, [sp],#48      <= FP+LR retrieved
  418450:       d65f03c0        ret
*/
static frame_state_t
get_frame_state (unw_cursor_t *cursor)
{
  struct cursor *c = (struct cursor *) cursor;
  unw_accessors_t *a = unw_get_accessors (c->dwarf.as);
  unw_word_t w, start_ip, ip, offp;
  frame_state_t fs;
  fs.loc = NO_PROC_INFO;
  fs.offset = 0;

  /* PLT entries do not create frame records */
  if (_is_plt_entry (&c->dwarf))
    return fs;

  /* Use get_proc_name to find start_ip of procedure */
  char name[128];
  if (((*a->get_proc_name) (c->dwarf.as, c->dwarf.ip, name, sizeof(name), &offp, c->dwarf.as_arg)) != 0)
    return fs;

  fs.loc = NONE;
  start_ip = c->dwarf.ip - offp;

  frame_state_t saved_fs;
  saved_fs.loc = NONE;
  saved_fs.offset = 0;

  /* Check for frame record instructions since the start of the procedure (start_ip).
   * access_mem reads WSIZE bytes, so two instructions are checked in each iteration
   */
  for (ip = start_ip; ip < c->dwarf.ip; ip += WSIZE)
    {
      if ((*a->access_mem) (c->dwarf.as, ip, &w, 0, c->dwarf.as_arg) < 0)
        return fs;

      if (fs.loc == NONE)
        {
          /* Check that a 64-bit store pair (STP) instruction storing FP and LR
             has been executed, which would indicate that a frame record has been
             created and that the LR value is saved therein.

             From the ARM Architecture Reference Manual (ARMv8), the format of the
             64-bit STP instruction is

             | 10 | 101 | 0 | 0XX | 0 |   imm7  |  Rt2  |   Rn  |   Rt  |

             where X can be 0/1 above to indicate the instruction variant (post-
             index, pre-index or signed offset). imm7 is a 7-bit signed immediate
             offset. The following should be asserted for this check

             Rt2 == LR (x30) => 0b11110
             Rn  == SP (x31) => 0b11111
             Rt  == FP (x29) => 0b11101.

             Hence the bitmask should be constructed to assert that the instruction
             is

             | 10 | 101 | 0 | 0XX | 0 | XXXXXXX | 11110 | 11111 | 11101 |

             So using a bitmask of 0xfe407fff, the masked instruction would be
             0xa8007bfd */
          if ((((w & 0xfe407fff00000000) == 0xa8007bfd00000000) && (c->dwarf.ip > ip + 4))
           || (((w & 0x00000000fe407fff) == 0x00000000a8007bfd) && (c->dwarf.ip > ip)))
            {
              fs.loc = AT_SP_OFFSET;

              /* If the signed offset variant of the STP instruction is detected,
                 the frame record may not be currently pointed to by SP. Extract
                 the offset from the STP instruction.

                 From the ARM Architecture Reference Manual (ARMv8), the format of
                 the signed offset variant of the 64-bit STP instruction is

                 | 10 | 101 | 0 | 010 | 0 | imm7 | Rt2 | Rn | Rt |

                 Hence the bitmask should be constructed to assert that the
                 instruction is

                 | 10 | 101 | 0 | 010 | 0 | XXXXXXX | 11110 | 11111 | 11101 |

                 So using a bitmask of 0xffc07fff, the masked instruction would
                 be 0xa9007bdf. The offset value is (imm7 * 8) */
              if (((w & 0xffc07fff00000000) == 0xa9007bfd00000000) && (c->dwarf.ip > ip + 4))
                {
                  int32_t abs_offset = (w & 0x001f800000000000) >> 47;
                  fs.offset = ((w & 0x0020000000000000)? -abs_offset : abs_offset) * 8;
                }
              else if (((w & 0x00000000ffc07fff) == 0x00000000a9007bfd) && (c->dwarf.ip > ip))
                {
                  int32_t abs_offset = (w & 0x00000000001f8000) >> 15;
                  fs.offset = ((w & 0x0000000000200000)? -abs_offset : abs_offset) * 8;
                }
              else
                fs.offset = 0;

              Debug (4, "ip=0x%lx => frame record stored at SP+0x%x\n", ip, fs.offset);
            }
        }

      if (fs.loc == AT_SP_OFFSET)
        {
          /* If the STP instruction has been executed, but not the instruction
             to update FP, then the SP (with offset) should be used to find the
             frame record, otherwise the FP should be used as there are no
             guarantees that the SP is pointing to the frame record after the FP
             has been updated.

             Two methods for updating the FP have been seen in practice. The
             first is a MOV instruction. From the ARM Architecture Reference
             Manual (ARMv8), the 64-bit MOV (to/from SP) instruction to update
             FP (x29) is 0x910003fd.

             The second is an ADD instruction taking SP and FP as the source and
             destination registers, respectively. From the ARM Architecture
             Reference Manual (ARMv8), the 64-bit ADD (immediate) instruction is

             | 1 | 0 | 0 | 10001 | shift |     imm12    |   Rn  |   Rd  |

             where the values of shift and imm12 are irrelevant for this check.
             The following should be asserted

             Rn == SP (x31) => 0b11111
             Rd == FP (x29) => 0b11101.

             Hence the bitmask should be constructed to assert that the instruction
             is

             | 1 | 0 | 0 | 10001 |   XX  | XXXXXXXXXXXX | 11111 | 11101 |

             So using a bitmask of 0xff0003ff, the masked instruction would be
             0x910003fd.

             Since the MOV instruction is an alias for ADD, it is sufficient to
             only check for the latter case. */
          if ((((w & 0xff0003ff00000000) == 0x910003fd00000000) && (c->dwarf.ip > ip + 4))
           || (((w & 0x00000000ff0003ff) == 0x00000000910003fd) && (c->dwarf.ip > ip)))
            {
              fs.loc = AT_FP;
              fs.offset = 0;

              Debug (4, "ip=0x%lx => frame record stored at FP\n", ip);
            }
        }
      else if (fs.loc == AT_FP)
        {
          /* Check that a 64-bit load pair (LDP) instruction to restore FP and LR has been
             executed, which would indicate that a branch or return instruction is about to
             be executed and that FP is pointing to the caller's frame record instead. The
             LR should be examined for the return address. In this case, the RA must have
             already been authenticated.

             From the ARM Architecture Reference Manual (ARMv8), the format of the 64-bit
             LDP instruction is

             | 10 | 101 | 0 | 0XX | 1 |   imm7  |  Rt2  |   Rn  |   Rt  |

             where X can be 0/1 above to indicate the instruction variant (post-index, pre-
             index or signed offset). imm7 is a 7-bit signed immediate offset, which is
             irrelevant for this check. However, the following should be asserted

             Rt2 == LR (x30) => 0b11110
             Rn  == SP (x31) => 0b11111
             Rt  == FP (x29) => 0b11101.

             Hence the bitmask should be constructed to assert that the instruction is

             | 10 | 101 | 0 | 0XX | 1 | XXXXXXX | 11110 | 11111 | 11101 |

             So using a bitmask of 0xfe407fff, the masked instruction would be 0xa8407bfd */
          if ((((w & 0xfe407fff00000000) == 0xa8407bfd00000000) && (c->dwarf.ip > ip + 4))
           || (((w & 0x00000000fe407fff) == 0x00000000a8407bfd) && (c->dwarf.ip > ip)))
            {
              saved_fs = fs;
              fs.loc = NONE;
              fs.offset = 0;

              Debug (4, "ip=0x%lx => frame record has been loaded, use LR\n", ip);
            }
        }

      /* Check for RET instruction (0xd65f03c0). If IP is past a RET, execution
         reached this point via a branch that skipped the epilogue, so the frame
         record is still intact. Restore the state from before the epilogue. */
      if (fs.loc == NONE && saved_fs.loc != NONE)
        {
          if ((((w & 0xffffffff00000000) == 0xd65f03c000000000) && (c->dwarf.ip > ip + 4))
           || (((w & 0x00000000ffffffff) == 0x00000000d65f03c0) && (c->dwarf.ip > ip)))
            {
              fs = saved_fs;
              saved_fs.loc = NONE;
              saved_fs.offset = 0;

              Debug (4, "ip=0x%lx => past RET, restoring frame state to loc=%d\n", ip, fs.loc);
            }
        }
    }

  Debug (3, "[start_ip = 0x%lx, ip=0x%lx) => loc = %d, offset = %d\n",
            start_ip, c->dwarf.ip, fs.loc, fs.offset);

  return fs;
}

#if defined __QNX__
/*
 * QNX kernel call have neither CFI nor save frame pointer,
 * 00000000000549e8 <MsgSendsv_r>:
 *  549e8:       cb0203e2        neg     x2, x2
 *  549ec:       52800168        mov     w8, #0xb  // #11
 *  549f0:       d4000a21        svc     #0x51
 *  549f4:       d65f03c0        ret
 *  549f8:       cb0003e0        neg     x0, x0
 *  549fc:       d65f03c0        ret
 *
 * From disassemble, QNX kernel call have mov followed by svc, and may with some
 * neg instructions at beginning.
 * 1. find procedure's ip start,end start
 * 2. search mov,svc from begin, skip any neg instructions
 */
static bool is_neg_instr(uint32_t instr)
{
  /* 64bit register Xd */
  return ((instr & 0xffe003e0) == 0xcb0003e0);
}

/* QNX use w8 to pass kernel call number */
static bool is_mov_w8_instr(uint32_t instr)
{
  /* movz 32bit register Wd */
  return ((instr & 0xffe00008) == 0x52800008);
}

static bool is_svc_instr(uint32_t instr)
{
  return instr == 0xd4000a21;
}

static bool
is_qnx_kercall(struct dwarf_cursor *c)
{
  unw_word_t w0;
  unw_accessors_t *a;
  int ret;
  unw_word_t proc_start_ip;
  unw_word_t proc_end_ip;

  a = unw_get_accessors_int (c->as);
  if (c->as->big_endian || !a->get_proc_ip_range)
    {
      return false;
    }

  ret = (*a->get_proc_ip_range) (c->as, c->ip, &proc_start_ip, &proc_end_ip, c->as_arg);
  if (ret < 0)
    {
      Debug (2, "ip=0x%lx get proc ip range fail, ret = %d\n", c->ip, ret);
      return false;
    }

  unw_word_t ip = proc_start_ip;
  while ((ip < proc_end_ip) && (ip + 8 < proc_end_ip))
    {
      if ((*a->access_mem) (c->as, ip, &w0, 0, c->as_arg) < 0)
        {
          Debug (14, "access_mem ip=0x%lx fail\n", ip);
          return false;
        }

      uint32_t low32 = w0 & 0xffffffff;
      uint32_t high32 = w0 >> 32;

      if (is_mov_w8_instr(low32) && is_svc_instr(high32))
        {
          return true;
        }
      if (is_neg_instr(low32) && is_neg_instr(high32))
        {
          ip += 8;
        }
      else if (is_neg_instr(low32) && is_mov_w8_instr(high32))
        {
          ip += 4;
        }
      else
        {
          return false;
        }
    }

  return false;
}
#endif

/*
 * Save the location of VL (vector length) from the signal frame to the VG (vector
 * granule) register if it exists, otherwise do nothing. If there is an error,
 * the location is also not modified.
 */
#if defined __linux__
static int
get_sve_vl_signal_loc (struct dwarf_cursor* dwarf, unw_word_t sc_addr)
{
  uint32_t size;
  unw_addr_space_t as = dwarf->as;
  unw_accessors_t *a = unw_get_accessors_int (as);
  void *arg = dwarf->as_arg;
  unw_word_t res_addr = sc_addr + LINUX_SC_RESERVED_OFF;

  /*
   * Max possible size of reserved section is 4096. Jump by size of each record
   * until the SVE signal context section is found.
   */
  for(unw_word_t section_off = 0; section_off < 4096; section_off += size)
    {
      uint32_t magic;
      int ret;
      unw_word_t item_addr = res_addr + section_off + LINUX_SC_RESERVED_MAGIC_OFF;
      if ((ret = dwarf_readu32(as, a, &item_addr, &magic, arg)) < 0)
        return ret;
      item_addr = res_addr + section_off + LINUX_SC_RESERVED_SIZE_OFF;
      if ((ret = dwarf_readu32(as, a, &item_addr, &size, arg)) < 0)
        return ret;

      switch(magic)
        {
          case 0:
            /* End marker, size must be 0 */
            return size ? -UNW_EUNSPEC : 1;

          /* Magic number marking the SVE context section */
          case 0x53564501:
            /* Must be big enough so that the 16 bit VL value can be accessed */
            if (size < LINUX_SC_RESERVED_SVE_VL_OFF + 2)
              return -UNW_EUNSPEC;
            dwarf->loc[UNW_AARCH64_VG] = DWARF_MEM_LOC(c->dwarf, res_addr + section_off +
                                                       LINUX_SC_RESERVED_SVE_VL_OFF);
            return 1;

          default:
            /* Don't care about any of the other sections */
            break;
        }
   }
   return 1;
}
#else
static int
get_sve_vl_signal_loc (struct dwarf_cursor* dwarf, unw_word_t sc_addr)
{
   return 1;
}
#endif

static int
aarch64_handle_signal_frame (unw_cursor_t *cursor)
{
  struct cursor *c = (struct cursor *) cursor;
  int i, ret;
  unw_word_t sp_addr = c->dwarf.cfa;
  unw_word_t sc_addr;

  ret = unw_is_signal_frame (cursor);
  if (ret > 0)
    {
      c->sigcontext_format = SCF_FORMAT;
#if defined(__QNXNTO__)
      /*
       * The QNX signal trampoline receives a pointer to a _sighandler_info
       * struct in r19, which it breaks apart and passes the relevant args to
       * the signal handler in r0, r1, and r2 folowing the ARM AAPCS64
       * convention, regardless of whether SA_SIGINFO is specified in sa_flags.
       * Since r0-2 are not preserved registers, their value is
       * lost by the time the unwinder gets here, but r19 is preserved so it
       * should still be valid. If full DWARF unwinding has happened.
       */
      unw_word_t x19;
      ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X19], &x19);
      if ((ret < 0) || (x19 == 0UL))
        {
          return -UNW_EUNSPEC;
        }

      ret = dwarf_get (&c->dwarf,
                       DWARF_MEM_LOC (c->dwarf, x19 + SI_UCONTEXT_OFF),
                       &sc_addr);
      if (ret < 0)
        {
          return -UNW_EUNSPEC;
        }

      sc_addr += UC_MCONTEXT_OFF;
#else
      /*
       * For non-QNX OSes the mcontext is expected to be on the signal stack,
       */
      sc_addr = sp_addr + sizeof (siginfo_t) + UC_MCONTEXT_OFF;
#endif
    }
  else
    return -UNW_EUNSPEC;

  /* Save the SP and PC to be able to return execution at this point
     later in time (unw_resume).  */
  c->sigcontext_sp = c->dwarf.cfa;
  c->sigcontext_pc = c->dwarf.ip;

  c->sigcontext_addr = sc_addr;
  c->frame_info.frame_type = UNW_AARCH64_FRAME_SIGRETURN;
  c->frame_info.cfa_reg_offset = sc_addr - sp_addr;

  for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
    c->dwarf.loc[i] = DWARF_NULL_LOC;

  /* Update the dwarf cursor.
     Set the location of the registers to the corresponding addresses of the
     uc_mcontext / sigcontext structure contents.  */
  c->dwarf.loc[UNW_AARCH64_X0]  = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF);
  c->dwarf.loc[UNW_AARCH64_X1]  = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 1);
  c->dwarf.loc[UNW_AARCH64_X2]  = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 2);
  c->dwarf.loc[UNW_AARCH64_X3]  = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 3);
  c->dwarf.loc[UNW_AARCH64_X4]  = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 4);
  c->dwarf.loc[UNW_AARCH64_X5]  = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 5);
  c->dwarf.loc[UNW_AARCH64_X6]  = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 6);
  c->dwarf.loc[UNW_AARCH64_X7]  = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 7);
  c->dwarf.loc[UNW_AARCH64_X8]  = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 8);
  c->dwarf.loc[UNW_AARCH64_X9]  = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 9);
  c->dwarf.loc[UNW_AARCH64_X10] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 10);
  c->dwarf.loc[UNW_AARCH64_X11] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 11);
  c->dwarf.loc[UNW_AARCH64_X12] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 12);
  c->dwarf.loc[UNW_AARCH64_X13] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 13);
  c->dwarf.loc[UNW_AARCH64_X14] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 14);
  c->dwarf.loc[UNW_AARCH64_X15] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 15);
  c->dwarf.loc[UNW_AARCH64_X16] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 16);
  c->dwarf.loc[UNW_AARCH64_X17] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 17);
  c->dwarf.loc[UNW_AARCH64_X18] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 18);
  c->dwarf.loc[UNW_AARCH64_X19] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 19);
  c->dwarf.loc[UNW_AARCH64_X20] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 20);
  c->dwarf.loc[UNW_AARCH64_X21] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 21);
  c->dwarf.loc[UNW_AARCH64_X22] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 22);
  c->dwarf.loc[UNW_AARCH64_X23] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 23);
  c->dwarf.loc[UNW_AARCH64_X24] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 24);
  c->dwarf.loc[UNW_AARCH64_X25] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 25);
  c->dwarf.loc[UNW_AARCH64_X26] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 26);
  c->dwarf.loc[UNW_AARCH64_X27] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 27);
  c->dwarf.loc[UNW_AARCH64_X28] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 28);
  c->dwarf.loc[UNW_AARCH64_X29] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_X29_OFF);
  c->dwarf.loc[UNW_AARCH64_X30] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_X30_OFF);
  c->dwarf.loc[UNW_AARCH64_SP]  = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_SP_OFF);
  c->dwarf.loc[UNW_AARCH64_PC]  = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_PC_OFF);
  c->dwarf.loc[UNW_AARCH64_PSTATE]  = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_PSTATE_OFF);
  c->dwarf.loc[UNW_AARCH64_VG]  = DWARF_NULL_LOC;

  /* Set SP/CFA and PC/IP.  */
  dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_SP], &c->dwarf.cfa);
  dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_PC], &c->dwarf.ip);

  c->dwarf.pi_valid = 0;
  c->dwarf.use_prev_instr = 0;

  return get_sve_vl_signal_loc (&c->dwarf, sc_addr);
}

int
unw_step (unw_cursor_t *cursor)
{
  struct cursor *c = (struct cursor *) cursor;
  int validate = c->validate;
  unw_word_t fp = 0;
  int ret;

  Debug (1, "(cursor=%p, ip=0x%016lx, cfa=0x%016lx))\n",
         c, c->dwarf.ip, c->dwarf.cfa);

  /* Validate all addresses before dereferencing. */
  c->validate = 1;

  /* Check if this is a signal frame. */
  ret = unw_is_signal_frame (cursor);
  Debug(1, "unw_is_signal_frame()=%d\n", ret);
  if (ret > 0)
    return aarch64_handle_signal_frame (cursor);
  else if (unlikely (ret < 0))
    {
      /* IP points to non-mapped memory. */
      /* This is probably SIGBUS. */
      /* Try to load LR in IP to recover. */
      Debug(1, "Invalid address found in the call stack: 0x%lx\n", c->dwarf.ip);
      dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X30], &c->dwarf.ip);
    }

  /* Try DWARF-based unwinding... */
  c->sigcontext_format = AARCH64_SCF_NONE;
  ret = dwarf_step (&c->dwarf);
  c->dwarf_step_ret = ret;
  c->step_method = UNW_STEP_DWARF;
  Debug(1, "dwarf_step()=%d\n", ret);

  /* Restore default memory validation state */
  c->validate = validate;

  if (unlikely (ret == -UNW_ESTOPUNWIND))
    return ret;

  if (likely (ret > 0))
    {
      ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X29], &fp);
      if (ret == 0 && fp == 0)
        {
	  /* Procedure Call Standard for the ARM 64-bit Architecture (AArch64)
	   * specifies that the end of the frame record chain is indicated by
	   * the address zero in the address for the previous frame.
	   */
	  c->dwarf.ip = 0;
	  Debug (2, "NULL frame pointer X29 loc, returning 0\n");
	  return 0;
        }
    }

  if (unlikely (ret < 0))
    {
      /* DWARF failed. */

      /*
       * We could get here because of missing/bad unwind information.
       * Validate all addresses before dereferencing.
       */
      if (c->dwarf.as == unw_local_addr_space)
        {
          c->validate = 1;
        }

      if (_is_plt_entry (&c->dwarf))
        {
          Debug (2, "found plt entry\n");
          c->frame_info.frame_type = UNW_AARCH64_FRAME_STANDARD;
        }
#if defined __QNX__
      else if (is_qnx_kercall(&c->dwarf))
        {
          Debug (2, "found qnx kernel call, fallback to use link register\n");
          c->frame_info.frame_type = UNW_AARCH64_FRAME_GUESSED;
        }
#endif
      else
        {
	  /* Try use frame record. */
          c->frame_info.frame_type = UNW_AARCH64_FRAME_GUESSED;
	}

      frame_state_t fs = get_frame_state(cursor);

      /* Prefer using frame record. The LR value is stored at an offset of
         8 into the frame record.  */
      if (fs.loc != NONE && fs.loc != NO_PROC_INFO)
        {
          if (fs.loc == AT_FP)
            {
              c->step_method = UNW_STEP_FALLBACK_FP;
              /* X29 points to frame record.  */
              ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X29], &fp);
              if (unlikely (ret == 0))
                {
                  if (fp == 0)
                    {
                      /* Procedure Call Standard for the ARM 64-bit Architecture (AArch64)
                       * specifies that the end of the frame record chain is indicated by
                       * the address zero in the address for the previous frame.
                       */
                      c->dwarf.ip = 0;
                      Debug (2, "NULL frame pointer X29 loc, returning 0\n");
                      return 0;
                    }
                }

              for (int i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
                c->dwarf.loc[i] = DWARF_NULL_LOC;

              /* Frame record holds X29 and X30 values.  */
              c->dwarf.loc[UNW_AARCH64_X29] = DWARF_MEM_LOC (c->dwarf, fp);
              c->dwarf.loc[UNW_AARCH64_X30] = DWARF_MEM_LOC (c->dwarf, fp + 8);
            }
          else
            {
              c->step_method = UNW_STEP_FALLBACK_SP;
              /* Frame record stored but not pointed to by X29, use SP.  */
              unw_word_t sp;
              ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_SP], &sp);
              if (ret < 0)
                return ret;

              for (int i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
                c->dwarf.loc[i] = DWARF_NULL_LOC;

              c->frame_info.cfa_reg_offset = fs.offset;
              c->frame_info.cfa_reg_sp = 1;

              c->dwarf.loc[UNW_AARCH64_X29] = DWARF_MEM_LOC (c->dwarf, sp + fs.offset);
              c->dwarf.loc[UNW_AARCH64_X30] = DWARF_MEM_LOC (c->dwarf, sp + fs.offset + 8);
            }

          c->dwarf.loc[UNW_AARCH64_PC] = c->dwarf.loc[UNW_AARCH64_X30];

          /* Set SP/CFA and PC/IP.  */
          ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X29], &c->dwarf.cfa);
          if (ret < 0)
            return ret;
          ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_PC], &c->dwarf.ip);
          if (ret == 0)
            {
              ret = 1;
            }
          Debug (2, "fallback, CFA = 0x%016lx, IP = 0x%016lx returning %d\n",
            c->dwarf.cfa, c->dwarf.ip, ret);
          return ret;
        }

      /* No frame record, fallback to link register (X30).  */
      if (fs.loc == NO_PROC_INFO)
        {
          c->step_method = UNW_STEP_FALLBACK_LR_NO_PROC_INFO;
        }
      else
        {
          c->step_method = UNW_STEP_FALLBACK_LR;
        }
      c->loc_info = fs.loc;
      c->frame_info.cfa_reg_offset = 0;
      c->frame_info.cfa_reg_sp = 0;
      c->frame_info.fp_cfa_offset = -1;
      c->frame_info.lr_cfa_offset = -1;
      c->frame_info.sp_cfa_offset = -1;
      c->dwarf.loc[UNW_AARCH64_PC] = c->dwarf.loc[UNW_AARCH64_X30];
      c->dwarf.loc[UNW_AARCH64_X30] = DWARF_NULL_LOC;
      if (!DWARF_IS_NULL_LOC (c->dwarf.loc[UNW_AARCH64_PC]))
        {
          ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_PC], &c->dwarf.ip);
          if (ret < 0)
            {
              Debug (2, "failed to get pc from link register: %d\n", ret);
              return ret;
            }
          Debug (2, "link register (x30) = 0x%016lx\n", c->dwarf.ip);
          ret = 1;
        }
      else
        c->dwarf.ip = 0;
    }

  return (c->dwarf.ip == 0) ? 0 : 1;
}