diff options
author | Patrick Mooney <pmooney@pfmooney.com> | 2020-05-21 03:58:32 +0000 |
---|---|---|
committer | Patrick Mooney <pmooney@pfmooney.com> | 2020-06-08 19:08:39 +0000 |
commit | e1fb6a07e9492184a949d5a3ba446ff53b888a2b (patch) | |
tree | ac615575ef09e24ca68f608e5263c5ba10b3fd51 /usr/src | |
parent | eda3ef2de2d15b389090f6ef953edaea3daaace4 (diff) | |
download | illumos-joyent-e1fb6a07e9492184a949d5a3ba446ff53b888a2b.tar.gz |
12777 libc unwinding confused by indirect pointer encoding
Reviewed by: Robert Mustacchi <rm@fingolfin.org>
Reviewed by: Jason King <jason.king@joyent.com>
Reviewed by: Andy Fiddaman <omnios@citrus-it.co.uk>
Approved by: Dan McDonald <danmcd@joyent.com>
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/lib/libc/amd64/unwind/call_frame_inst.c | 41 |
1 files changed, 34 insertions, 7 deletions
diff --git a/usr/src/lib/libc/amd64/unwind/call_frame_inst.c b/usr/src/lib/libc/amd64/unwind/call_frame_inst.c index 82d357fb0c..3e23a5e25b 100644 --- a/usr/src/lib/libc/amd64/unwind/call_frame_inst.c +++ b/usr/src/lib/libc/amd64/unwind/call_frame_inst.c @@ -23,6 +23,7 @@ * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. + * Copyright 2020 Oxide Computer Company */ /* @@ -471,19 +472,25 @@ _Unw_get_val(void **datap, ptrdiff_t reloc, static uint64_t get_encoded_val(void **datap, ptrdiff_t reloc, int enc) { - int val = enc & 0xf; - int rel = (enc >> 4) & 0xf; + const uint8_t val = enc & 0xf; + const uint8_t rel = enc & 0x70; + const boolean_t indirect = (enc & 0x80) != 0; intptr_t loc = ((intptr_t)*datap) + reloc; uint64_t res = 0; + /* + * Calculate the offset represented by the pointer encoding. These + * DWARF extensions are defined in the Core Generic document set of the + * LSB specification. + */ switch (val) { case 0x01: res = _Unw_get_val(datap, reloc, ULEB128, 1, 1, 0); break; - case 0x2: + case 0x02: res = _Unw_get_val(datap, reloc, UNUM16, 1, 1, 0); break; - case 0x3: + case 0x03: res = _Unw_get_val(datap, reloc, UNUM32, 1, 1, 0); break; case 0x04: @@ -502,11 +509,11 @@ get_encoded_val(void **datap, ptrdiff_t reloc, int enc) res = _Unw_get_val(datap, reloc, SNUM64, 1, 1, 0); break; } - switch (rel) { - case 0: + case 0x00: break; - case 1: + case 0x10: + /* DW_EH_PE_pcrel */ if (res != 0) res += loc; break; @@ -514,6 +521,26 @@ get_encoded_val(void **datap, ptrdiff_t reloc, int enc) /* remainder not implemented */ break; } + + /* + * The high bit of the pointer encoding (DW_EH_PE_indirect = 0x80) + * indicates that a pointer-sized value should be read from the + * calculated address as the final result. + * + * Shockingly, this is not documented in any specification to date, but + * has been implemented in various unwind implementations through + * reverse-engineering of GCC. + */ + if (indirect) { + void *addr = (void *)(uintptr_t)res; + + /* + * Built only for amd64, we can count on a 64-bit pointer size + * for the indirect handling. + */ + res = _Unw_get_val(&addr, reloc, UNUM64, 1, 1, 0); + } + return (res); } |