summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorPatrick Mooney <pmooney@pfmooney.com>2020-05-21 03:58:32 +0000
committerPatrick Mooney <pmooney@pfmooney.com>2020-06-08 19:08:39 +0000
commite1fb6a07e9492184a949d5a3ba446ff53b888a2b (patch)
treeac615575ef09e24ca68f608e5263c5ba10b3fd51 /usr/src
parenteda3ef2de2d15b389090f6ef953edaea3daaace4 (diff)
downloadillumos-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.c41
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);
}